Add source-patched syntax profiles

This commit is contained in:
slhx agent
2026-06-21 04:22:55 +02:00
parent 2ec9840837
commit a90c35e542
3 changed files with 244 additions and 0 deletions
+46
View File
@@ -7,6 +7,7 @@ const replay = @import("replay.zig");
const repo_mod = @import("repo.zig");
const session_mod = @import("session.zig");
const symbol_mod = @import("symbol.zig");
const syntax_mod = @import("syntax.zig");
// First terminal thin client surface, scriptable for E2E-style tests.
// req: session/001, session/003, ui/001, coding/001, testing/001, testing/002
@@ -146,6 +147,7 @@ pub const Client = struct {
if (std.mem.eql(u8, line, "terminal_status")) return self.openStaticJobRow("terminal-status", "terminal_status:idle");
if (std.mem.eql(u8, line, "terminal_cancel")) return self.openStaticJobRow("terminal-status", "terminal_cancel:no_running_terminal");
if (std.mem.eql(u8, line, "terminal_exit")) return self.closeTerminalPanel();
if (std.mem.startsWith(u8, line, "syntax_spans ")) return self.openSyntaxSpans(line[13..]);
if (std.mem.startsWith(u8, line, "panel_open ")) return self.applyProtocolCommand(line);
if (std.mem.startsWith(u8, line, "list_open ")) return self.applyProtocolCommand(line);
if (std.mem.startsWith(u8, line, "list_filter ")) return self.applyProtocolCommand(line);
@@ -406,6 +408,14 @@ pub const Client = struct {
self.message = null;
}
fn openSyntaxSpans(self: *Client, language: []const u8) !void {
const snap = try self.session.snapshot();
const rows = syntax_mod.rowsAlloc(self.allocator, language, snap.bytes) catch return Error.ProtocolRejected;
defer freeOwnedRows(self.allocator, rows);
try self.session.openListPanel("syntax-spans", rows);
self.message = null;
}
fn openStaticJobRow(self: *Client, title: []const u8, row: []const u8) !void {
try self.session.openListPanel(title, &.{row});
self.message = null;
@@ -1459,3 +1469,39 @@ test "adversarial: terminal command rejection does not corrupt editor" {
try std.testing.expect(std.mem.indexOf(u8, frame, "terminal:status:exit_127") != null);
try std.testing.expect(std.mem.indexOf(u8, frame, "stderr:") != null);
}
test "regular: syntax spans panel renders zig highlight classes" {
var client = try Client.init(std.testing.allocator, .{ .width = 80, .height = 7 });
defer client.deinit();
try client.handleTraceLine("open pub const answer = 42; // ok");
try client.handleTraceLine("syntax_spans zig");
const frame = try client.render(std.testing.allocator);
defer std.testing.allocator.free(frame);
try std.testing.expect(std.mem.indexOf(u8, frame, "syntax-spans") != null);
try std.testing.expect(std.mem.indexOf(u8, frame, "syntax:zig:keyword:0:3:pub") != null);
try std.testing.expect(std.mem.indexOf(u8, frame, "syntax:zig:keyword:4:9:const") != null);
try std.testing.expect(std.mem.indexOf(u8, frame, "syntax:zig:number:") != null);
try std.testing.expect(std.mem.indexOf(u8, frame, "syntax:zig:comment:") != null);
}
test "regular: syntax text fallback renders no spans row" {
var client = try Client.init(std.testing.allocator, .{ .width = 72, .height = 5 });
defer client.deinit();
try client.handleTraceLine("open pub_const_plain_text");
try client.handleTraceLine("syntax_spans text");
const frame = try client.render(std.testing.allocator);
defer std.testing.allocator.free(frame);
try std.testing.expect(std.mem.indexOf(u8, frame, "syntax:text:fallback:no_spans") != null);
}
test "adversarial: unsupported syntax language is rejected without changing buffer" {
var client = try Client.init(std.testing.allocator, .{ .width = 72, .height = 5 });
defer client.deinit();
try client.handleTraceLine("open safe");
try std.testing.expectError(Error.ProtocolRejected, client.handleTraceLine("syntax_spans python"));
const snap = try client.session.snapshot();
try std.testing.expectEqualStrings("safe", snap.bytes);
}