From 3ac80cfe99a701652de61c3ca1e1ecfa13b5f21e Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Tue, 20 Oct 2026 20:08:09 +0200 Subject: [PATCH] ui: yellow for warnings --- CHANGELOG.md | 2 +- src/ide/views/editors.ts | 4 ++-- src/ide/views/gutter.ts | 6 ++++-- src/ide/views/visuals.ts | 33 ++++++++++++++++++++++++--------- src/themes/editorTheme.ts | 4 ++++ 5 files changed, 35 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 74080bf5..df5c505f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ See [extension/CHANGELOG.md](extension/CHANGELOG.md). - Reset clears the call graph; breakpoints are re-armed after a build. - Click the hex offset in the gutter to run to that line. - Probe Log no longer shows a blank line for every clock tick with no event; only recorded events get a line, so instructions and their bus accesses stay together. -- Compiler warnings no longer fail a build or get lost. cc65/ca65/ld65 and SDCC warnings show as gutter markers in the IDE (not in the error dialog), as warnings in the VS Code Problems panel, and on stderr from `8bws` (`--no-warnings` hides them). +- Compiler warnings no longer fail a build or get lost. cc65/ca65/ld65 and SDCC warnings show as yellow gutter markers and yellow squiggles on the source line in the IDE (not in the error dialog), as warnings in the VS Code Problems panel, and on stderr from `8bws` (`--no-warnings` hides them). - Emulators now run on a timeline (CLI and extension only for now): step back, seek, reverse run, `rbreak`, and deterministic replay with a replay-verification test that found determinism bugs in several platforms. ### `8bws` CLI diff --git a/src/ide/views/editors.ts b/src/ide/views/editors.ts index 608a802c..cb457ccd 100644 --- a/src/ide/views/editors.ts +++ b/src/ide/views/editors.ts @@ -524,7 +524,7 @@ export class SourceEditor implements ProjectView { this.clearErrors(); errors = errors.slice(0, MAX_ERRORS); const newErrors = new Map(); - const spans: { line: number, start: number, end: number }[] = []; + const spans: { line: number, start: number, end: number, warning?: boolean }[] = []; for (var info of errors) { // only mark errors with this filename, or without any filename if (!info.path || this.path.endsWith(info.path)) { @@ -537,7 +537,7 @@ export class SourceEditor implements ProjectView { } // collect column-level spans if (info.start != null && info.end != null && info.end > info.start) { - spans.push({ line, start: info.start, end: info.end }); + spans.push({ line, start: info.start, end: info.end, warning: info.severity === 'warning' }); } } } diff --git a/src/ide/views/gutter.ts b/src/ide/views/gutter.ts index 4d7ce117..a55e5fd5 100644 --- a/src/ide/views/gutter.ts +++ b/src/ide/views/gutter.ts @@ -17,7 +17,7 @@ const setErrors = StateEffect.define>(); const setCurrentPc = StateEffect.define(); const runToLineEffect = StateEffect.define(); -const showErrorMessage = StateEffect.define<{ line: number, msg: string, toggle?: boolean } | null>(); +const showErrorMessage = StateEffect.define<{ line: number, msg: string, warning?: boolean, toggle?: boolean } | null>(); const offsetField = StateField.define>({ create() { return RangeSet.empty; }, @@ -273,13 +273,15 @@ const statusGutter = gutter({ if (errorMarkers.size > 0) { const pos = line.from; let msg = ""; + let warning = false; errorMarkers.between(pos, pos, (from, to, marker: ErrorMarker) => { msg = marker.msg; + warning = marker.warning === true; }); if (msg) { const lineNum = view.state.doc.lineAt(line.from).number; view.dispatch({ - effects: showErrorMessage.of({ line: lineNum, msg, toggle: true }) + effects: showErrorMessage.of({ line: lineNum, msg, warning, toggle: true }) }); } } else if (canUseBreakpoints()) { diff --git a/src/ide/views/visuals.ts b/src/ide/views/visuals.ts index 36b0efcd..bf27a59d 100644 --- a/src/ide/views/visuals.ts +++ b/src/ide/views/visuals.ts @@ -33,12 +33,19 @@ const currentPcLineField = StateField.define({ // Error message widget to show below error lines class ErrorMessageWidget extends WidgetType { - constructor(readonly message: string) { super() } + constructor(readonly message: string, readonly warning?: boolean) { super() } toDOM() { let div = document.createElement("div"); div.textContent = this.message; - div.style.cssText = ` + div.style.cssText = this.warning ? ` + color: #ffcc44; + background-color: #332900; + padding: 4px 8px; + margin: 2px 0; + border-left: 3px solid #ffcc44; + font-family: monospace; + ` : ` color: #ff6666; background-color: #330000; padding: 4px 8px; @@ -50,6 +57,8 @@ class ErrorMessageWidget extends WidgetType { } } +interface ErrorMessageInfo { msg: string, warning?: boolean } + // State field to manage error message decorations (supports multiple messages) const errorMessageField = StateField.define({ create() { return Decoration.none; }, @@ -67,12 +76,13 @@ const errorMessageField = StateField.define({ const widgets: any[] = []; // Collect existing decorations - const existingMessages = new Map(); + const existingMessages = new Map(); decorations.between(0, tr.state.doc.length, (from, to, value) => { try { const lineNum = tr.state.doc.lineAt(to).number; if (value.spec.widget instanceof ErrorMessageWidget) { - existingMessages.set(lineNum, (value.spec.widget as ErrorMessageWidget).message); + const widget = value.spec.widget as ErrorMessageWidget; + existingMessages.set(lineNum, { msg: widget.message, warning: widget.warning }); } } catch { } }); @@ -85,7 +95,7 @@ const errorMessageField = StateField.define({ existingMessages.delete(e.value.line); } else { // Add the new message - existingMessages.set(e.value.line, e.value.msg); + existingMessages.set(e.value.line, { msg: e.value.msg, warning: e.value.warning }); } // Build sorted decorations @@ -93,9 +103,10 @@ const errorMessageField = StateField.define({ sortedLines.forEach(lineNum => { try { const line = tr.state.doc.line(lineNum); + const info = existingMessages.get(lineNum)!; widgets.push( Decoration.widget({ - widget: new ErrorMessageWidget(existingMessages.get(lineNum)!), + widget: new ErrorMessageWidget(info.msg, info.warning), block: true, side: 1 }).range(line.to) @@ -159,13 +170,17 @@ const showValueDecorationField = StateField.define({ provide: f => EditorView.decorations.from(f), }); -// Error span decorations for inline column-range highlighting -const errorSpansEffect = StateEffect.define<{ line: number, start: number, end: number }[] | null>(); +// Error/warning span decorations for inline column-range highlighting +const errorSpansEffect = StateEffect.define<{ line: number, start: number, end: number, warning?: boolean }[] | null>(); const errorSpanDecoration = Decoration.mark({ attributes: { class: "cm-error-span" } }); +const warningSpanDecoration = Decoration.mark({ + attributes: { class: "cm-warning-span" } +}); + const errorSpansField = StateField.define({ create() { return Decoration.none }, update(decorations, tr) { @@ -183,7 +198,7 @@ const errorSpansField = StateField.define({ const to = line.from + span.end; // Clamp to line boundaries if (from >= line.from && to <= line.to && from < to) { - ranges.push(errorSpanDecoration.range(from, to)); + ranges.push((span.warning ? warningSpanDecoration : errorSpanDecoration).range(from, to)); } } catch { // Line doesn't exist, skip diff --git a/src/themes/editorTheme.ts b/src/themes/editorTheme.ts index ce4fa52f..9e7de62a 100644 --- a/src/themes/editorTheme.ts +++ b/src/themes/editorTheme.ts @@ -35,6 +35,10 @@ export const editorTheme = EditorView.theme({ textDecoration: "underline wavy red", backgroundColor: "rgba(255, 0, 0, 0.15)", }, + ".cm-warning-span": { + textDecoration: "underline wavy #ffcc44", + backgroundColor: "rgba(255, 204, 68, 0.15)", + }, ".gutter-offset .cm-gutterElement": { paddingRight: "0.25em", },