Color status panels by source state

This commit is contained in:
slhx agent
2026-06-21 22:01:13 +02:00
parent a9b4eff8a6
commit 7f6d37d4a3
2 changed files with 69 additions and 1 deletions
+60 -1
View File
@@ -430,7 +430,7 @@ pub const Client = struct {
}
for (list_rows) |row| {
if (body_lines_used >= max_body_lines) break;
try appendThemedRow(allocator, &out, row, self.viewport.width, ansi_text);
try appendPanelRow(allocator, &out, row, self.viewport.width);
try out.append(allocator, '\n');
body_lines_used += 1;
}
@@ -2586,6 +2586,13 @@ const ansi_syntax_builtin = "\x1b[38;2;121;155;224;48;2;25;22;33m";
const ansi_syntax_string = "\x1b[38;2;116;196;171;48;2;25;22;33m";
const ansi_syntax_comment = "\x1b[38;2;103;93;122;48;2;25;22;33m";
const ansi_syntax_number = "\x1b[38;2;231;168;78;48;2;25;22;33m";
const ansi_diag_error = "\x1b[38;2;238;84;88;48;2;25;22;33m";
const ansi_diag_warning = "\x1b[38;2;231;168;78;48;2;25;22;33m";
const ansi_diag_info = "\x1b[38;2;121;155;224;48;2;25;22;33m";
const ansi_diag_hint = "\x1b[38;2;116;196;171;48;2;25;22;33m";
const ansi_success = "\x1b[38;2;116;196;171;48;2;25;22;33m";
const ansi_stale = "\x1b[38;2;103;93;122;48;2;25;22;33m";
const ansi_provider = "\x1b[38;2;235;119;84;48;2;25;22;33m";
const ansi_status = "\x1b[38;2;25;22;33;48;2;235;119;84m";
fn appendEditorLine(
@@ -2768,6 +2775,28 @@ fn appendThemedRow(allocator: std.mem.Allocator, out: *std.ArrayList(u8), text:
try out.appendSlice(allocator, ansi_reset);
}
fn appendPanelRow(allocator: std.mem.Allocator, out: *std.ArrayList(u8), row: []const u8, width: usize) !void {
try appendThemedRow(allocator, out, row, width, panelRowStyle(row));
}
fn panelRowStyle(row: []const u8) []const u8 {
if (startsAny(row, &.{ "job:status:exit_0", "terminal:status:exit_0", "ok:", "success:" })) return ansi_success;
if (startsAny(row, &.{ "error:", "stderr:", "diagnostic:error", "job:status:exit_", "job:status:spawn_error_", "terminal:status:exit_" })) return ansi_diag_error;
if (startsAny(row, &.{ "warning:", "warn:", "diagnostic:warning", "timeout:", "job:status:timeout", "terminal:status:timeout" })) return ansi_diag_warning;
if (startsAny(row, &.{ "info:", "diagnostic:info", "stdout:", "job:status:running", "terminal:status:running" })) return ansi_diag_info;
if (startsAny(row, &.{ "hint:", "diagnostic:hint" })) return ansi_diag_hint;
if (startsAny(row, &.{ "provider:", "lsp-provider:", "language-provider:" })) return ansi_provider;
if (startsAny(row, &.{ "missing:", "conflict:", "stale:", "job_status:idle", "terminal_status:idle" })) return ansi_stale;
return ansi_text;
}
fn startsAny(row: []const u8, prefixes: []const []const u8) bool {
for (prefixes) |prefix| {
if (std.mem.startsWith(u8, row, prefix)) return true;
}
return false;
}
fn appendStatusLine(allocator: std.mem.Allocator, out: *std.ArrayList(u8), status: []const u8, width: usize) !void {
const line_start = out.items.len;
try out.appendSlice(allocator, ansi_status);
@@ -5550,3 +5579,33 @@ test "regular: plain text without syntax spans stays normal themed text" {
try std.testing.expect(std.mem.indexOf(u8, frame, ansi_text) != null);
try std.testing.expect(std.mem.indexOf(u8, frame, ansi_syntax_keyword) == null);
}
test "regular: panel rows style diagnostic, job, provider, and stale states" {
try std.testing.expectEqualStrings(ansi_diag_error, panelRowStyle("error:2:5:file:///repo/src/main.zig:expected_semicolon"));
try std.testing.expectEqualStrings(ansi_diag_warning, panelRowStyle("warning:3:1:file:///repo/src/main.zig:unused_var"));
try std.testing.expectEqualStrings(ansi_diag_info, panelRowStyle("info:1:1:file:///repo/src/main.zig:note"));
try std.testing.expectEqualStrings(ansi_diag_hint, panelRowStyle("hint:1:1:file:///repo/src/main.zig:tip"));
try std.testing.expectEqualStrings(ansi_diag_error, panelRowStyle("job:status:exit_1"));
try std.testing.expectEqualStrings(ansi_success, panelRowStyle("job:status:exit_0"));
try std.testing.expectEqualStrings(ansi_diag_error, panelRowStyle("job:status:spawn_error_FileNotFound"));
try std.testing.expectEqualStrings(ansi_diag_warning, panelRowStyle("timeout:recoverable"));
try std.testing.expectEqualStrings(ansi_provider, panelRowStyle("provider:zig:zls:available"));
try std.testing.expectEqualStrings(ansi_stale, panelRowStyle("missing:provider:zls"));
try std.testing.expectEqualStrings(ansi_text, panelRowStyle("plain row"));
}
test "regular: appended panel rows carry semantic color identity" {
var out: std.ArrayList(u8) = .empty;
defer out.deinit(std.testing.allocator);
try appendPanelRow(std.testing.allocator, &out, "error:2:5:file:///repo/src/main.zig:expected_semicolon", 72);
try std.testing.expect(std.mem.indexOf(u8, out.items, ansi_diag_error) != null);
out.clearRetainingCapacity();
try appendPanelRow(std.testing.allocator, &out, "job:status:exit_0", 72);
try std.testing.expect(std.mem.indexOf(u8, out.items, ansi_success) != null);
out.clearRetainingCapacity();
try appendPanelRow(std.testing.allocator, &out, "provider:zig:zls:available", 72);
try std.testing.expect(std.mem.indexOf(u8, out.items, ansi_provider) != null);
}
+9
View File
@@ -101,6 +101,12 @@ def classify_sgr(params: str) -> str:
return "syntax-comment"
if "38;2;231;168;78" in joined:
return "syntax-number"
if "38;2;238;84;88" in joined:
return "diag-error"
if "38;2;116;196;171" in joined:
return "diag-hint"
if "38;2;121;155;224" in joined:
return "diag-info"
if "48;2;20;18;26" in joined:
return "current-line"
if "48;2;235;119;84" in joined:
@@ -648,6 +654,9 @@ def run_one(mim: Path, root_out: Path, scenario: Scenario) -> tuple[str, Path]:
("38;2;116;196;171", "syntax-string"),
("38;2;103;93;122", "syntax-comment"),
("38;2;231;168;78", "syntax-number"),
("38;2;238;84;88", "diag-error"),
("38;2;116;196;171", "diag-hint"),
("38;2;121;155;224", "diag-info"),
):
if sgr in raw_text:
raw_attrs.add(attr)