Older tags of the repo don't have the configuration.md file that the docs/index.html file uses to display configuration options. Removing them from the list since they don't apply to the use case of the documentation page.
298 lines
12 KiB
HTML
298 lines
12 KiB
HTML
<!doctype html>
|
|
<html>
|
|
<head>
|
|
<meta name="viewport" content="width=device-width">
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/3.0.1/github-markdown.css" />
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/styles/github-gist.min.css">
|
|
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
|
|
<script src="https://unpkg.com/vue-async-computed@3.8.1"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/highlight.min.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js"></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="version">version: </label>
|
|
<select name="version" id="version" v-model="version">
|
|
<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 RusfmtTagsUrl = 'https://api.github.com/repos/rust-lang/rustfmt/tags';
|
|
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,
|
|
version: versionNumber,
|
|
oldVersion: undefined,
|
|
versionOptions: ['master'],
|
|
scrolledOnce: false,
|
|
},
|
|
asyncComputed: {
|
|
async outputHtml() {
|
|
if (this.version !== this.oldVersion) {
|
|
const ConfigurationMdUrl =
|
|
`https://raw.githubusercontent.com/rust-lang/rustfmt/${this.version}/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.oldVersion = this.version;
|
|
}
|
|
|
|
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.version);
|
|
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(RusfmtTagsUrl)).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>Ecountered 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>
|