diff options
| author | _ov4 <ov4@ov4.org> | 2026-10-22 01:45:15 +0000 |
|---|---|---|
| committer | _ov4 <ov4@ov4.org> | 2026-10-22 01:45:15 +0000 |
| commit | 660d618f8b0df318d636cd780e547890d0248c0c (patch) | |
| tree | ea3f7746105d271558287ccd7268834245df68fd /bundle/src/main.js | |
| parent | 427acc420c6fce10fa77e603f7696f3e89d342d6 (diff) | |
feat(bundle): color compiler diagnostics in Output
The runner now passes -fdiagnostics-color=always, since auto only sees a
pipe. src/ansi.js turns stderr's SGR sequences into bold and colored
runs, keeping style and cut-off sequences across chunks and dropping
other sequences such as gcc's erase-line. Program stdout stays verbatim.
Colors come from --vscode-terminal-ansi* variables. 2026 Dark leaves
them unset, so vendor/vscode/terminal-colors.json pins VS Code's dark
terminal defaults from the same upstream commit as the themes; a theme
value still wins.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'bundle/src/main.js')
| -rw-r--r-- | bundle/src/main.js | 32 |
1 files changed, 23 insertions, 9 deletions
diff --git a/bundle/src/main.js b/bundle/src/main.js index b71ed27..991ef06 100644 --- a/bundle/src/main.js +++ b/bundle/src/main.js @@ -3,7 +3,8 @@ import { getCurrentWindow } from '@tauri-apps/api/window'; import { open, save, message } from '@tauri-apps/plugin-dialog'; import { KeyCode, KeyMod } from 'monaco-editor/editor/editor.api.js'; import { StandardKeyboardEvent } from 'monaco-editor/base/browser/keyboardEvent.js'; -import { createEditor } from './editor.js'; +import { createEditor, themeVariable } from './editor.js'; +import { createAnsiParser } from './ansi.js'; import './style.css'; const status = document.querySelector('#status'); @@ -34,26 +35,39 @@ async function runFile() { output.textContent = ''; status.textContent = 'Running'; const decoders = { stdout: new TextDecoder(), stderr: new TextDecoder() }; - let pending = ''; + // Only compiler diagnostics on stderr are colored; program output stays verbatim. + const diagnostics = createAnsiParser(); + const pending = document.createDocumentFragment(); let frame = null; function flush() { frame = null; - if (!pending) return; - output.append(document.createTextNode(pending)); - pending = ''; + if (!pending.hasChildNodes()) return; + output.append(pending); output.scrollTop = output.scrollHeight; } - function append(text) { - pending += text; + function append(stream, text) { + for (const run of stream === 'stderr' ? diagnostics(text) : [{ text }]) { + if (!run.text) continue; + if (!run.bold && !run.color) { + if (pending.lastChild?.nodeType === Node.TEXT_NODE) pending.lastChild.appendData(run.text); + else pending.append(run.text); + continue; + } + const span = document.createElement('span'); + if (run.bold) span.style.fontWeight = 'bold'; + if (run.color) span.style.color = `var(${themeVariable(run.color)})`; + span.textContent = run.text; + pending.append(span); + } if (frame === null) frame = requestAnimationFrame(flush); } let finish; const finished = new Promise(resolve => { finish = resolve; }); const channel = new Channel(event => { if (event.event === 'data') { - append(decoders[event.stream].decode(Uint8Array.from(event.bytes), { stream: true })); + append(event.stream, decoders[event.stream].decode(Uint8Array.from(event.bytes), { stream: true })); } else if (event.event === 'finished') { - for (const decoder of Object.values(decoders)) append(decoder.decode()); + for (const [stream, decoder] of Object.entries(decoders)) append(stream, decoder.decode()); if (frame !== null) cancelAnimationFrame(frame); flush(); status.textContent = event.stopped ? 'Stopped' : `Exit code: ${event.code ?? 'terminated'}`; |
