refactor: localize keyword check and simplify command tests

This commit is contained in:
Adrià Arrufat
2026-05-11 20:08:38 +02:00
parent c41955ade3
commit 45da2c8196
3 changed files with 16 additions and 39 deletions

View File

@@ -5,7 +5,6 @@ const Config = lp.Config;
const Command = lp.script.Command;
const SlashCommand = @import("SlashCommand.zig");
const Spinner = @import("Spinner.zig");
const string = @import("../string.zig");
const c = @cImport({
@cInclude("isocline.h");
});
@@ -428,7 +427,7 @@ fn highlighterCallback(henv: ?*c.ic_highlight_env_t, input: [*c]const u8, _: ?*a
// ALL CAPS but unknown → typo (red); lowercase/mixed → natural language (unstyled).
const style: ?[*:0]const u8 = if (isKnownCommand(cmd))
style_cmd
else if (string.isAllUpper(cmd))
else if (looksLikeKeyword(cmd))
style_err
else
null;
@@ -437,6 +436,14 @@ fn highlighterCallback(henv: ?*c.ic_highlight_env_t, input: [*c]const u8, _: ?*a
}
}
fn looksLikeKeyword(s: []const u8) bool {
if (s.len == 0) return false;
for (s) |ch| {
if (!std.ascii.isUpper(ch) and !std.ascii.isDigit(ch) and ch != '_') return false;
}
return true;
}
fn isKnownCommand(name: []const u8) bool {
for (Command.keywords) |kw| {
if (std.mem.eql(u8, kw.name, name)) return true;

View File

@@ -599,6 +599,11 @@ fn getJsonI32(o: std.json.ObjectMap, key: []const u8, default: i32) i32 {
// --- Tests ---
fn testUpcase(ar: std.mem.Allocator, input: []const u8) []const u8 {
const out = ar.alloc(u8, input.len) catch return input;
return std.ascii.upperString(out, input);
}
test "parse GOTO" {
const cmd = parse("GOTO https://example.com");
try std.testing.expectEqualStrings("https://example.com", cmd.goto);
@@ -1188,15 +1193,7 @@ test "toToolCall: substitute callback applied to selector fields" {
defer arena.deinit();
const a = arena.allocator();
const upcase = struct {
fn f(ar: std.mem.Allocator, input: []const u8) []const u8 {
const out = ar.alloc(u8, input.len) catch return input;
for (input, 0..) |c, i| out[i] = std.ascii.toUpper(c);
return out;
}
}.f;
const tc = toToolCall(a, .{ .click = "abc" }, upcase).?;
const tc = toToolCall(a, .{ .click = "abc" }, testUpcase).?;
try std.testing.expectEqualStrings("click", tc.name);
try std.testing.expectEqualStrings("{\"selector\":\"ABC\"}", tc.args_json);
}
@@ -1206,15 +1203,7 @@ test "toToolCall: type_cmd value is NOT substituted" {
defer arena.deinit();
const a = arena.allocator();
const upcase = struct {
fn f(ar: std.mem.Allocator, input: []const u8) []const u8 {
const out = ar.alloc(u8, input.len) catch return input;
for (input, 0..) |c, i| out[i] = std.ascii.toUpper(c);
return out;
}
}.f;
const tc = toToolCall(a, .{ .type_cmd = .{ .selector = "abc", .value = "$LP_PASSWORD" } }, upcase).?;
const tc = toToolCall(a, .{ .type_cmd = .{ .selector = "abc", .value = "$LP_PASSWORD" } }, testUpcase).?;
try std.testing.expectEqualStrings("fill", tc.name);
// selector substituted, value preserved as $LP_* reference
try std.testing.expectEqualStrings("{\"selector\":\"ABC\",\"value\":\"$LP_PASSWORD\"}", tc.args_json);

View File

@@ -311,14 +311,6 @@ pub fn isAllWhitespace(text: []const u8) bool {
} else true;
}
pub fn isAllUpper(s: []const u8) bool {
if (s.len == 0) return false;
for (s) |ch| {
if (!std.ascii.isUpper(ch) and !std.ascii.isDigit(ch) and ch != '_') return false;
}
return true;
}
// Discriminatory type that signals the bridge to use arena instead of call_arena
// Use this for strings that need to persist beyond the current call
// The caller can unwrap and store just the underlying .str field
@@ -341,17 +333,6 @@ fn asUint(comptime string: anytype) std.meta.Int(
const testing = @import("testing.zig");
test "isAllUpper" {
try testing.expectEqual(false, isAllUpper(""));
try testing.expectEqual(true, isAllUpper("GOTO"));
try testing.expectEqual(true, isAllUpper("ACCEPT_COOKIES"));
try testing.expectEqual(true, isAllUpper("X1"));
try testing.expectEqual(true, isAllUpper("_"));
try testing.expectEqual(false, isAllUpper("Goto"));
try testing.expectEqual(false, isAllUpper("goto"));
try testing.expectEqual(false, isAllUpper("GO TO"));
}
test "String" {
const other_short = try String.init(undefined, "other_short", .{});
const other_long = try String.init(testing.allocator, "other_long" ** 100, .{});