vole
Clone
Read only, no credentials:
https://rodent.software/git/vole.git
Write over SSH with a registered key:
git clone ssh://git@0.0.0.0:2222/vole.git
A free and open-source forge that lives in your Git repo.
Highlight syntax in diff views
e7982ac5137be6e891616cdcdedc035f9019f99b · Keenan Nemetz · 12d ago · parent a76b2f38
Highlight syntax in diff views The diff parser now names each file's Prism language from the new path (or the old path for a deletion), and the shared diff template carries that language plus the side of every line. app.js queues a diff once per side, so an old-side string or comment cannot colour new-side lines, and the token palette now covers diff cells as well as blobs. The commit, compare, and change-request Files views all use the same template, so they gain highlighting together.
server/web/static/app.js +28−10
| @@ -44,11 +44,10 @@ const copyText = (value, done) => { | |||
| 44 | 44 | }); | |
| 45 | 45 | })(); | |
| 46 | 46 | ||
| 47 | // Syntax highlighting. The server sends each blob as plain lines with their | ||
| 48 | // numbers and anchors, so the page is complete and scrollable before this runs; | ||
| 49 | // the tokens come from a worker (static/prism-worker.js) so a large file never | ||
| 50 | // blocks the main thread. A blob whose language has no grammar keeps the text it | ||
| 51 | // was served with. | ||
| 47 | // Syntax highlighting. The server sends each blob and each diff side as plain | ||
| 48 | // lines, so the page is complete and scrollable before this runs; the tokens come | ||
| 49 | // from a worker (static/prism-worker.js) so a large file never blocks the main | ||
| 50 | // thread. A file whose language has no grammar keeps the text it was served with. | ||
| 52 | 51 | (() => { | |
| 53 | 52 | let worker = null; | |
| 54 | 53 | let nextId = 0; | |
| @@ -70,8 +69,15 @@ const copyText = (value, done) => { | |||
| 70 | 69 | const lines = event.data.lines; | |
| 71 | 70 | // A grammar that answered nothing, or a line count that does not match the | |
| 72 | 71 | // table, leaves the server's text alone rather than mangling it. | |
| 73 | if (!lines || lines.length !== job.cells.length) return; | ||
| 74 | for (let i = 0; i < lines.length; i++) job.cells[i].innerHTML = lines[i]; | ||
| 72 | if (lines && lines.length === job.cells.length) { | ||
| 73 | for (let i = 0; i < lines.length; i++) job.cells[i].innerHTML = lines[i]; | ||
| 74 | } | ||
| 75 | // A diff file is queued once per side, so the block is finished only when | ||
| 76 | // the last side has answered (or declined). | ||
| 77 | if (job.state) { | ||
| 78 | job.state.pending -= 1; | ||
| 79 | if (job.state.pending > 0) return; | ||
| 80 | } | ||
| 75 | 81 | job.block.dataset.highlighted = 'applied'; | |
| 76 | 82 | }); | |
| 77 | 83 | worker.addEventListener('error', () => { | |
| @@ -90,12 +96,24 @@ const copyText = (value, done) => { | |||
| 90 | 96 | const lang = block.getAttribute('data-highlight'); | |
| 91 | 97 | const cells = Array.from(block.querySelectorAll('td.code')); | |
| 92 | 98 | if (!lang || !cells.length) return; | |
| 99 | // A diff interleaves the old and new sides. Highlighting them as one stream | ||
| 100 | // lets a multiline comment or string started on one side colour lines from | ||
| 101 | // the other, so each side is its own job. A blob has no side and is one job. | ||
| 102 | const groups = new Map(); | ||
| 103 | for (const cell of cells) { | ||
| 104 | const side = cell.getAttribute('data-highlight-side') || ''; | ||
| 105 | if (!groups.has(side)) groups.set(side, []); | ||
| 106 | groups.get(side).push(cell); | ||
| 107 | } | ||
| 93 | 108 | const w = start(); | |
| 94 | 109 | if (!w) return; | |
| 95 | 110 | block.dataset.highlighted = 'queued'; | |
| 96 | const id = ++nextId; | ||
| 97 | pending.set(id, { block, cells }); | ||
| 98 | w.postMessage({ id, lang, code: cells.map((cell) => cell.textContent).join('\n') }); | ||
| 111 | const state = { pending: groups.size }; | ||
| 112 | for (const group of groups.values()) { | ||
| 113 | const id = ++nextId; | ||
| 114 | pending.set(id, { block, cells: group, state }); | ||
| 115 | w.postMessage({ id, lang, code: group.map((cell) => cell.textContent).join('\n') }); | ||
| 116 | } | ||
| 99 | 117 | }; | |
| 100 | 118 | ||
| 101 | 119 | const init = (scope = document) => { | |