From ef1dde49a60522d8e17dc1b137584bd26d8afcee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Arrufat?= Date: Tue, 12 May 2026 11:29:34 +0200 Subject: [PATCH] terminal: improve command highlighting with prefix checking --- src/agent/Terminal.zig | 44 +++++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/src/agent/Terminal.zig b/src/agent/Terminal.zig index bd87d1fe..87213db6 100644 --- a/src/agent/Terminal.zig +++ b/src/agent/Terminal.zig @@ -431,21 +431,29 @@ fn highlighterCallback(henv: ?*c.ic_highlight_env_t, input: [*c]const u8, _: ?*a const cmd_start = i; while (i < text.len and !std.ascii.isWhitespace(text[i])) i += 1; const cmd = text[cmd_start..i]; - // Don't flag mid-typed prefixes as wrong — only commit to red once the - // user has moved past the token. + // Commit to red once the user has moved past the token, OR as soon as the + // prefix cannot complete to any known name. const closed = i < text.len; if (cmd.len > 0 and cmd[0] == '/') { - const known = isKnownSlashName(cmd[1..]); - const style: ?[*:0]const u8 = if (known) style_slash else if (closed) style_err else null; - if (style) |s| c.ic_highlight(henv, @intCast(cmd_start), @intCast(cmd.len), s); + c.ic_highlight(henv, @intCast(cmd_start), 1, style_slash.ptr); + if (cmd.len > 1) { + const name = cmd[1..]; + const style: ?[*:0]const u8 = if (isKnownSlashName(name)) + style_slash + else if (closed or !slashHasPrefix(name)) + style_err + else + null; + if (style) |s| c.ic_highlight(henv, @intCast(cmd_start + 1), @intCast(cmd.len - 1), s); + } highlightSlashArgs(henv, text, i); } else { - const style: ?[*:0]const u8 = if (isKnownCommand(cmd)) - style_cmd - else if (closed and looksLikeKeyword(cmd)) - style_err - else - null; + const style: ?[*:0]const u8 = blk: { + if (isKnownCommand(cmd)) break :blk style_cmd; + if (!looksLikeKeyword(cmd)) break :blk null; + if (closed or !keywordHasPrefix(cmd)) break :blk style_err; + break :blk null; + }; if (style) |s| c.ic_highlight(henv, @intCast(cmd_start), @intCast(cmd.len), s); highlightPandaArgs(henv, text, i); } @@ -480,6 +488,20 @@ fn isKnownSlashName(name: []const u8) bool { return false; } +fn slashHasPrefix(name: []const u8) bool { + for (all_slash_names) |n| { + if (std.ascii.startsWithIgnoreCase(n, name)) return true; + } + return false; +} + +fn keywordHasPrefix(name: []const u8) bool { + for (Command.keywords) |kw| { + if (std.mem.startsWith(u8, kw.name, name)) return true; + } + return false; +} + fn slashHasParams(schemas: []const SlashCommand.SchemaInfo, name: []const u8) bool { const s = SlashCommand.findSchema(schemas, name) orelse return false; return s.hints.len > 0;