diff options
| author | Will Crichton <wcrichto@cs.stanford.edu> | 2021-05-09 16:22:22 -0700 |
|---|---|---|
| committer | Will Crichton <wcrichto@cs.stanford.edu> | 2021-10-06 19:44:47 -0700 |
| commit | 4b3f82ad0321b8f2e2630b74bbc526ffb8fa5bda (patch) | |
| tree | 4d10d3906b55b93f95d813d1e859ef92592712a6 /src/librustdoc/html/static | |
| parent | 0eabf25b90396dead0b2a1aaa275af18a1ae6008 (diff) | |
| download | rust-4b3f82ad0321b8f2e2630b74bbc526ffb8fa5bda.tar.gz rust-4b3f82ad0321b8f2e2630b74bbc526ffb8fa5bda.zip | |
Add updated support for example-analyzer
Move rendering of examples into Finalize design Cleanup, rename found -> scraped Softer yellow Clean up dead code Document scrape_examples More simplification and documentation Remove extra css Test
Diffstat (limited to 'src/librustdoc/html/static')
| -rw-r--r-- | src/librustdoc/html/static/css/rustdoc.css | 107 | ||||
| -rw-r--r-- | src/librustdoc/html/static/js/main.js | 196 |
2 files changed, 303 insertions, 0 deletions
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 5d33681847a..ca8db4530f3 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -1970,3 +1970,110 @@ details.undocumented[open] > summary::before { margin-left: 12px; } } + +/* This part is for the new "examples" components */ + +.scraped-example:not(.expanded) .code-wrapper pre.line-numbers, .scraped-example:not(.expanded) .code-wrapper .example-wrap pre.rust { + overflow: hidden; + height: 240px; +} + +.scraped-example .code-wrapper .prev { + position: absolute; + top: 0.25em; + right: 2.25em; + z-index: 100; + cursor: pointer; +} + +.scraped-example .code-wrapper .next { + position: absolute; + top: 0.25em; + right: 1.25em; + z-index: 100; + cursor: pointer; +} + +.scraped-example .code-wrapper .expand { + position: absolute; + top: 0.25em; + right: 0.25em; + z-index: 100; + cursor: pointer; +} + +.scraped-example .code-wrapper { + position: relative; + display: flex; + flex-direction: row; + flex-wrap: wrap; + width: 100%; +} + +.scraped-example:not(.expanded) .code-wrapper:before { + content: " "; + width: 100%; + height: 20px; + position: absolute; + z-index: 100; + top: 0; + background: linear-gradient(to bottom, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0)); +} + +.scraped-example:not(.expanded) .code-wrapper:after { + content: " "; + width: 100%; + height: 20px; + position: absolute; + z-index: 100; + bottom: 0; + background: linear-gradient(to top, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0)); +} + +.scraped-example:not(.expanded) .code-wrapper { + overflow: hidden; + height: 240px; +} + +.scraped-example .code-wrapper .line-numbers { + margin: 0; + padding: 14px 0; +} + +.scraped-example .code-wrapper .line-numbers span { + padding: 0 14px; +} + +.scraped-example .code-wrapper .example-wrap { + flex: 1; + overflow-x: auto; + overflow-y: hidden; + margin-bottom: 0; +} + +.scraped-example .code-wrapper .example-wrap pre.rust { + overflow-x: inherit; + width: inherit; + overflow-y: hidden; +} + +.scraped-example .line-numbers span.highlight { + background: #f6fdb0; +} + +.scraped-example .example-wrap .rust span.highlight { + background: #f6fdb0; +} + +.more-scraped-examples { + padding-left: 10px; + border-left: 1px solid #ccc; +} + +.toggle-examples .collapse-toggle { + position: relative; +} + +.toggle-examples a { + color: #999 !important; // FIXME(wcrichto): why is important needed +} diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index e396fd9d288..5ac00ff244a 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -979,6 +979,202 @@ function hideThemeButtonState() { onHashChange(null); window.addEventListener("hashchange", onHashChange); searchState.setup(); + + /////// EXAMPLE ANALYZER + + // Merge the full set of [from, to] offsets into a minimal set of non-overlapping + // [from, to] offsets. + // NB: This is such a archetypal software engineering interview question that + // I can't believe I actually had to write it. Yes, it's O(N) in the input length -- + // but it does assume a sorted input! + function distinctRegions(locs) { + var start = -1; + var end = -1; + var output = []; + for (var i = 0; i < locs.length; i++) { + var loc = locs[i]; + if (loc[0] > end) { + if (end > 0) { + output.push([start, end]); + } + start = loc[0]; + end = loc[1]; + } else { + end = Math.max(end, loc[1]); + } + } + if (end > 0) { + output.push([start, end]); + } + return output; + } + + function convertLocsStartsToLineOffsets(code, locs) { + locs = distinctRegions(locs.slice(0).sort(function (a, b) { + return a[0] === b[0] ? a[1] - b[1] : a[0] - b[0]; + })); // sort by start; use end if start is equal. + var codeLines = code.split("\n"); + var lineIndex = 0; + var totalOffset = 0; + var output = []; + + while (locs.length > 0 && lineIndex < codeLines.length) { + var lineLength = codeLines[lineIndex].length + 1; // +1 here and later is due to omitted \n + while (locs.length > 0 && totalOffset + lineLength > locs[0][0]) { + var endIndex = lineIndex; + var charsRemaining = locs[0][1] - totalOffset; + while (endIndex < codeLines.length && charsRemaining > codeLines[endIndex].length + 1) { + charsRemaining -= codeLines[endIndex].length + 1; + endIndex += 1; + } + output.push({ + from: [lineIndex, locs[0][0] - totalOffset], + to: [endIndex, charsRemaining] + }); + locs.shift(); + } + lineIndex++; + totalOffset += lineLength; + } + return output; + } + + // inserts str into html, *but* calculates idx by eliding anything in html that's not in raw. + // ideally this would work by walking the element tree...but this is good enough for now. + function insertStrAtRawIndex(raw, html, idx, str) { + if (idx > raw.length) { + return html; + } + if (idx == raw.length) { + return html + str; + } + var rawIdx = 0; + var htmlIdx = 0; + while (rawIdx < idx && rawIdx < raw.length) { + while (raw[rawIdx] !== html[htmlIdx] && htmlIdx < html.length) { + htmlIdx++; + } + rawIdx++; + htmlIdx++; + } + return html.substring(0, htmlIdx) + str + html.substr(htmlIdx); + } + + // Scroll code block to put the given code location in the middle of the viewer + function scrollToLoc(elt, loc) { + var wrapper = elt.querySelector(".code-wrapper"); + var halfHeight = wrapper.offsetHeight / 2; + var lines = elt.querySelector('.line-numbers'); + var offsetMid = (lines.children[loc.from[0]].offsetTop + lines.children[loc.to[0]].offsetTop) / 2; + var scrollOffset = offsetMid - halfHeight; + lines.scrollTo(0, scrollOffset); + elt.querySelector(".rust").scrollTo(0, scrollOffset); + } + + function updateScrapedExample(example) { + var code = example.attributes.getNamedItem("data-code").textContent; + var codeLines = code.split("\n"); + var locs = JSON.parse(example.attributes.getNamedItem("data-locs").textContent); + locs = convertLocsStartsToLineOffsets(code, locs); + + // Add call-site highlights to code listings + var litParent = example.querySelector('.example-wrap pre.rust'); + var litHtml = litParent.innerHTML.split("\n"); + onEach(locs, function (loc) { + for (var i = loc.from[0]; i < loc.to[0] + 1; i++) { + addClass(example.querySelector('.line-numbers').children[i], "highlight"); + } + litHtml[loc.to[0]] = insertStrAtRawIndex( + codeLines[loc.to[0]], + litHtml[loc.to[0]], + loc.to[1], + "</span>"); + litHtml[loc.from[0]] = insertStrAtRawIndex( + codeLines[loc.from[0]], + litHtml[loc.from[0]], + loc.from[1], + '<span class="highlight" data-loc="' + JSON.stringify(loc).replace(/"/g, """) + '">'); + }, true); // do this backwards to avoid shifting later offsets + litParent.innerHTML = litHtml.join('\n'); + + // Toggle through list of examples in a given file + var locIndex = 0; + if (locs.length > 1) { + example.querySelector('.prev') + .addEventListener('click', function () { + locIndex = (locIndex - 1 + locs.length) % locs.length; + scrollToLoc(example, locs[locIndex]); + }); + example.querySelector('.next') + .addEventListener('click', function () { + locIndex = (locIndex + 1) % locs.length; + scrollToLoc(example, locs[locIndex]); + }); + } else { + example.querySelector('.prev').remove(); + example.querySelector('.next').remove(); + } + + // Show full code on expansion + example.querySelector('.expand').addEventListener('click', function () { + if (hasClass(example, "expanded")) { + removeClass(example, "expanded"); + scrollToLoc(example, locs[0]); + } else { + addClass(example, "expanded"); + } + }); + + // Start with the first example in view + scrollToLoc(example, locs[0]); + } + + function updateScrapedExamples() { + onEach(document.getElementsByClassName('scraped-example-list'), function (exampleSet) { + updateScrapedExample(exampleSet.querySelector(".small-section-header + .scraped-example")); + }); + + onEach(document.getElementsByClassName("more-scraped-examples"), function (more) { + var toggle = createSimpleToggle(true); + var label = "More examples"; + var wrapper = createToggle(toggle, label, 14, "toggle-examples", false); + more.parentNode.insertBefore(wrapper, more); + var examples_init = false; + + // Show additional examples on click + wrapper.onclick = function () { + if (hasClass(this, "collapsed")) { + removeClass(this, "collapsed"); + onEachLazy(this.parentNode.getElementsByClassName("hidden"), function (x) { + if (hasClass(x, "content") === false) { + removeClass(x, "hidden"); + addClass(x, "x") + } + }, true); + this.querySelector('.toggle-label').innerHTML = "Hide examples"; + this.querySelector('.inner').innerHTML = labelForToggleButton(false); + if (!examples_init) { + examples_init = true; + onEach(more.getElementsByClassName('scraped-example'), updateScrapedExample); + } + } else { + addClass(this, "collapsed"); + onEachLazy(this.parentNode.getElementsByClassName("x"), function (x) { + if (hasClass(x, "content") === false) { + addClass(x, "hidden"); + removeClass(x, "x") + } + }, true); + this.querySelector('.toggle-label').innerHTML = label; + this.querySelector('.inner').innerHTML = labelForToggleButton(true); + } + }; + }); + } + + var start = Date.now(); + updateScrapedExamples(); + console.log("updated examples took", Date.now() - start, "ms"); }()); (function () { |
