Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

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) => {