| <!doctype html> |
| <html> |
| <head> |
| <meta name="viewport" content="width=device-width"> |
| <title>Rustfmt</title> |
| <link rel="stylesheet" |
| href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/3.0.1/github-markdown.min.css" |
| integrity="sha512-7e0rszTy0dKTIgYzCeBXpmycq0EOkwAvxZ0dv/LAtQfcXWCoaRhzs+v2Mn6of3jImxPazbYxiK0kpE/7wZ/UQA==" |
| crossorigin="anonymous" |
| referrerpolicy="no-referrer" /> |
| <link rel="stylesheet" |
| href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/styles/github-gist.min.css" |
| integrity="sha512-od7JLoOTxM8w/HSKGzP9Kexc20K9p/M2zxSWsd7H1e4Ctf+8SQFtCWEZnW5u6ul5ehSECa5QmOk9ju2nQMmlVA==" |
| crossorigin="anonymous" |
| referrerpolicy="no-referrer" /> |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/marked/12.0.2/marked.min.js" |
| integrity="sha512-xeUh+KxNyTufZOje++oQHstlMQ8/rpyzPuM+gjMFYK3z5ILJGE7l2NvYL+XfliKURMpBIKKp1XoPN/qswlSMFA==" |
| crossorigin="anonymous" |
| referrerpolicy="no-referrer"></script> |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js" |
| integrity="sha512-PwQ5+jgXxxprNGc80ycHE3spgj6TuDieHe/yTkbEJ+U5aol7dTupi/4VbwHHzlQVW77Vb0GLOIsiYigHgC5vcg==" |
| crossorigin="anonymous" |
| referrerpolicy="no-referrer"></script> |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/highlight.min.js" |
| integrity="sha512-av6ZR84Ldk6j29DMhf6v0cssEhow1VROFLoKbX7wrvzZB++/nV8m0jXbYeWcHPEzSNONImx6zwBskCUT9AQidA==" |
| crossorigin="anonymous" |
| referrerpolicy="no-referrer"></script> |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js" |
| integrity="sha512-3BBFWr73Xrf8GRjO+0pl0cbVwESBvg3ovnuCXpoqOkC/mkt/hTkFtutUPrwRz8eLySYvy5v1daulkyUZYvH8jw==" |
| crossorigin="anonymous" |
| referrerpolicy="no-referrer"></script> |
| <script src="https://unpkg.com/[email protected]"></script> |
| <style> |
| @media (max-width: 767px) { |
| .markdown-body { |
| padding: 15px; |
| } |
| |
| #search { |
| max-width: 85%; |
| } |
| } |
| body { |
| overflow: scroll; |
| } |
| .markdown-body { |
| box-sizing: border-box; |
| min-width: 200px; |
| max-width: 980px; |
| margin: 0 auto; |
| padding: 45px; |
| } |
| #search { |
| border: 1px solid #d1d5da; |
| padding-left: 30px; |
| overflow: hidden; |
| } |
| .searchCondition { |
| display: flex; |
| flex-wrap: wrap; |
| } |
| .searchCondition > div { |
| margin-right: 30px; |
| } |
| .header-link { |
| position: relative; |
| } |
| .header-link:hover::before { |
| position: absolute; |
| left: -2em; |
| padding-right: 0.5em; |
| content: '\2002\00a7\2002'; |
| } |
| </style> |
| </head> |
| <body> |
| <div id="app"> |
| <article class="markdown-body"> |
| <div class="searchCondition"> |
| <div> |
| <form style="display:flex;"> |
| <label for="search" style="margin-right: 3px;" >search:</label> |
| <div style="position: relative;"> |
| <input id="search" placeholder="Search all options" v-model="searchCondition"> |
| <svg style="position: absolute; left: 8px; top: 7px;" class="octicon octicon-search subnav-search-icon" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"> |
| <path fill-rule="evenodd" d="M15.7 13.3l-3.81-3.83A5.93 5.93 0 0 0 13 6c0-3.31-2.69-6-6-6S1 2.69 1 6s2.69 6 6 6c1.3 0 2.48-.41 3.47-1.11l3.83 3.81c.19.2.45.3.7.3.25 0 .52-.09.7-.3a.996.996 0 0 0 0-1.41v.01zM7 10.7c-2.59 0-4.7-2.11-4.7-4.7 0-2.59 2.11-4.7 4.7-4.7 2.59 0 4.7 2.11 4.7 4.7 0 2.59-2.11 4.7-4.7 4.7z"></path> |
| </svg> |
| </div> |
| </form> |
| </div> |
| <div> |
| <label for="stable">stable: </label> |
| <input type="checkbox" id="stable" v-model="shouldStable"> |
| </div> |
| <div> |
| <label for="viewVersion">version: </label> |
| <select name="viewVersion" id="viewVersion" v-model="viewVersion"> |
| <option v-for="option in versionOptions" v-bind:value="option"> |
| {{ option }} |
| </option> |
| </select> |
| </div> |
| </div> |
| <div v-html="aboutHtml"></div> |
| <div v-html="configurationAboutHtml"></div> |
| <div v-html="outputHtml"></div> |
| </article> |
| </div> |
| <script> |
| const RustfmtTagsUrl = 'https://api.github.com/repos/rust-lang/rustfmt/tags'; |
| const RustfmtLatestUrl = 'https://api.github.com/repos/rust-lang/rustfmt/releases/latest'; |
| const UrlHash = window.location.hash.replace(/^#/, ''); |
| const queryParams = new URLSearchParams(window.location.search); |
| const searchParam = queryParams.get('search'); |
| const searchTerm = null !== searchParam ? searchParam : ''; |
| const versionParam = queryParams.get('version'); |
| const parseVersionParam = (version) => { |
| if (version === 'master') return 'master'; |
| if (version.startsWith('v')) return version; |
| return `v${version}`; |
| }; |
| const versionNumber = null !== versionParam ? parseVersionParam(versionParam) : 'master'; |
| new Vue({ |
| el: '#app', |
| data: { |
| aboutHtml: '', |
| configurationAboutHtml: '', |
| configurationDescriptions: [], |
| searchCondition: searchTerm, |
| shouldStable: false, |
| viewVersion: versionNumber, |
| oldViewVersion: undefined, |
| versionOptions: ['master'], |
| scrolledOnce: false, |
| }, |
| asyncComputed: { |
| async updateVersion() { |
| let latest; |
| try { |
| latest = (await axios.get(RustfmtLatestUrl)).data; |
| } catch(err) { |
| console.log(err); |
| return; |
| } |
| if (versionParam == null) { |
| this.viewVersion = latest.name; |
| } |
| }, |
| async outputHtml() { |
| if (this.viewVersion !== this.oldViewVersion) { |
| const ConfigurationMdUrl = |
| `https://raw.githubusercontent.com/rust-lang/rustfmt/${this.viewVersion}/Configurations.md`; |
| let res; |
| try { |
| res = await axios.get(ConfigurationMdUrl).catch(e => { throw e }); |
| } catch(e) { |
| this.handleReqFailure(e); |
| return; |
| } |
| const { |
| about, |
| configurationAbout, |
| configurationDescriptions |
| } = parseMarkdownAst(res.data); |
| this.aboutHtml = marked.parser(about); |
| this.configurationAboutHtml = marked.parser(configurationAbout); |
| this.configurationDescriptions = configurationDescriptions; |
| this.oldViewVersion = this.viewVersion; |
| } |
| |
| const ast = this.configurationDescriptions |
| .filter(({ head, text, stable }) => { |
| if (text.includes(this.searchCondition) === false && |
| head.includes(this.searchCondition) === false) { |
| return false; |
| } |
| return (this.shouldStable) |
| ? stable === true |
| : true; |
| }) |
| .reduce((stack, { value }) => { |
| return stack.concat(value); |
| }, []); |
| ast.links = {}; |
| |
| queryParams.set('version', this.viewVersion); |
| queryParams.set('search', this.searchCondition); |
| const curUrl = window.location.pathname + |
| '?' + queryParams.toString() + window.location.hash; |
| history.pushState(null, '', curUrl); |
| |
| const renderer = new marked.Renderer(); |
| renderer.heading = function(text, level) { |
| const id = htmlToId(text); |
| return `<h${level}> |
| <a id="${id}" href="#${id}" name="${id}" class="header-link">${text}</a> |
| </h${level}>`; |
| }; |
| |
| return marked.parser(ast, { |
| highlight(code, lang) { |
| return hljs.highlight(lang ? lang : 'rust', code).value; |
| }, |
| headerIds: true, |
| headerPrefix: '', |
| renderer, |
| }); |
| } |
| }, |
| created: async function() { |
| let tags; |
| try { |
| tags = (await axios.get(RustfmtTagsUrl)).data; |
| } catch(e) { |
| this.handleReqFailure(e); |
| return; |
| } |
| |
| const excludedTagVersions = new Set(['v0.7', 'v0.8.1']); |
| |
| const tagOptions = tags |
| .map(tag => tag.name) |
| .filter(tag => tag.startsWith('v') && !excludedTagVersions.has(tag)); |
| this.versionOptions = this.versionOptions.concat(tagOptions); |
| }, |
| updated() { |
| if (UrlHash === '') return; |
| this.$nextTick(() => { |
| const target = document.querySelector(`#${UrlHash}`); |
| if (target != null && !this.scrolledOnce) { |
| target.scrollIntoView(true); |
| this.scrolledOnce = true; |
| } |
| }); |
| }, |
| methods: { |
| handleReqFailure(e) { |
| if (e.response.status === 404) { |
| this.aboutHtml = |
| "<p>Failed to get configuration options for this version, please select the version from the dropdown above.</p>"; |
| } else if ( |
| e.response.status === 403 && |
| e.response.headers["X-RateLimit-Remaining"] === 0 |
| ) { |
| const resetDate = new Date( |
| e.response.headers['X-RateLimit-Reset'] * 1000 |
| ).toLocaleString(); |
| this.aboutHtml = |
| `<p>You have hit the GitHub API rate limit; documentation cannot be updated.` + |
| `<p>The rate limit will be reset at ${resetDate}.</p>`; |
| } else { |
| this.aboutHtml = |
| `<p>Encountered an error when fetching documentation data:</p>` + |
| `<pre><code>${e.response.data}</code></pre>` + |
| `<p>We would appreciate <a href="https://github.com/rust-lang/rustfmt/issues/new?template=bug_report.md">a bug report</a>.` + |
| `<p>Try refreshing the page.</p>`; |
| } |
| } |
| } |
| }); |
| const extractDepthOnes = (ast) => { |
| return ast.reduce((stack, next) => { |
| if (next.depth === 1) { |
| stack.push([]); |
| } |
| const lastIndex = stack.length - 1; |
| stack[lastIndex].push(next); |
| return stack; |
| }, []); |
| } |
| const extractDepthTwos = (ast) => { |
| return ast.map((elem) => { |
| return elem.reduce((stack, next) => { |
| if (next.depth === 2) { |
| stack.push([]); |
| } |
| const lastIndex = stack.length - 1; |
| stack[lastIndex].push(next); |
| return stack; |
| }, |
| [[]]); |
| }); |
| } |
| const createHeadAndValue = (ast) => { |
| return ast.map((elem) => { |
| return elem.map((val) => { |
| return { |
| head: val[0].text, |
| value: val, |
| stable: val.some((elem) => { |
| return elem.type === "list" && |
| !!elem.raw && |
| elem.raw.includes("**Stable**: Yes"); |
| }), |
| text: val.reduce((result, next) => { |
| return next.text != null |
| ? `${result} ${next.text}` |
| : result; |
| }, '') |
| } |
| }); |
| }) |
| } |
| const parseMarkdownAst = (rawMarkdown) => { |
| const ast = marked.lexer(rawMarkdown); |
| const depthOnes = extractDepthOnes(ast); |
| const depthTwos = extractDepthTwos(depthOnes); |
| const [ |
| abouts, configurations |
| ] = createHeadAndValue(depthTwos); |
| const about = abouts[0].value; |
| about.links = {}; |
| const [ |
| configurationAbout, ...configurationDescriptions |
| ] = configurations; |
| configurationAbout.value.links = {}; |
| |
| return { |
| about, |
| configurationAbout: configurationAbout.value, |
| configurationDescriptions |
| }; |
| } |
| function htmlToId(text) { |
| const tmpl = document.createElement('template'); |
| tmpl.innerHTML = text.trim(); |
| return encodeURIComponent(CSS.escape(tmpl.content.textContent)); |
| } |
| </script> |
| </body> |
| </html> |