From ea228d1b832401db8efe33a5db4099ba41d42509 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Fri, 31 Jul 2026 00:26:12 -0300 Subject: config.zig: every binding and every piece of Look syntax in one file Retargeting a key, a mouse chord or a spelling is now an edit to src/config.zig and nothing else. Three parts in reading order: pardes's own bindings (where a reader lands), the Look/Exec syntax, then the helix keymap under a banner saying hxdiff/hxparity are differential suites against real helix, so a key moved there is a divergence and not a tweak. The bindings are data a comptime loop can walk, because the builtin index is going to walk them. Duplication this cut: is/isC/isA became one hit(key, chords) over ~132 call sites, and a binding being a LIST collapses the letter-or-arrow chains; swap_enter_tab is gone, replaced by look_key/exec_key that can point anywhere; the four focus builtins' h/j/k/l lived hardcoded in two places and is now one table. No golden moved. --- src/look.zig | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) (limited to 'src/look.zig') diff --git a/src/look.zig b/src/look.zig index 556b8ca7..c4d04576 100644 --- a/src/look.zig +++ b/src/look.zig @@ -1,5 +1,9 @@ //! What a click on text MEANS. The acme "look" (right click / Enter): expand //! the click to a file-ish word, then resolve it against the pane's directory. +//! How a word is SPELLED — the isfilec set, the `:LINE:COL` suffix, `@pN`, the +//! URL schemes, the image extensions — is config.zig; this file is only what +//! the spelling RESOLVES to. +//! //! This is the one deliberately platform-divergent file — the divergence is a //! comptime switch on pardes.platform, used the way the stdlib switches on //! os.tag, so every platform's behavior sits in the same screenful: @@ -11,6 +15,7 @@ const std = @import("std"); const builtin = @import("builtin"); const libc = std.c; const pardes = @import("pardes.zig"); +const config = @import("config.zig"); const embedded_sources = if (pardes.platform == .web) @import("embedded_sources") else struct { pub const Source = struct { path: []const u8, contents: []const u8 }; pub const all = [_]Source{}; @@ -50,14 +55,6 @@ pub fn openLink(url: []const u8) void { _ = libc.waitpid(pid, null, 0); } -/// file-ish word chars (acme isfilec): alnum + . - + / : @ _ ~ -pub fn isFileChar(c: u8) bool { - return std.ascii.isAlphanumeric(c) or switch (c) { - '.', '-', '+', '/', ':', '@', '_', '~' => true, - else => false, - }; -} - /// peel a trailing :LINE[:COL] suffix (both 1-based, 0 = absent): /// main.zig:100 -> {main.zig, 100, 0} /// main.zig:100:7 -> {main.zig, 100, 7} @@ -65,18 +62,18 @@ pub fn isFileChar(c: u8) bool { pub fn parsePathLine(tok: []const u8) struct { path: []const u8, line: usize, col: usize } { var sep: usize = 0; while (sep < tok.len) : (sep += 1) { - if (tok[sep] != ':') continue; + if (tok[sep] != config.line_col_sep) continue; var j = sep + 1; var line: usize = 0; while (j < tok.len and std.ascii.isDigit(tok[j])) : (j += 1) line = line * 10 + (tok[j] - '0'); if (j == sep + 1) continue; // no digits after ':' - if (j < tok.len and tok[j] != ':') continue; // junk after the number + if (j < tok.len and tok[j] != config.line_col_sep) continue; // junk after the number var col: usize = 0; if (j < tok.len) { var k = j + 1; while (k < tok.len and std.ascii.isDigit(tok[k])) : (k += 1) col = col * 10 + (tok[k] - '0'); // digits, and nothing but a delimiter after them, or no column - if (k == j + 1 or (k < tok.len and tok[k] != ':')) col = 0; + if (k == j + 1 or (k < tok.len and tok[k] != config.line_col_sep)) col = 0; } return .{ .path = tok[0..sep], .line = line, .col = col }; } @@ -95,10 +92,8 @@ pub const Target = union(enum) { pane: struct { id: usize, line: usize, col: usize }, }; -const image_exts = [_][]const u8{ ".png", ".jpg", ".jpeg", ".gif", ".bmp", ".ppm", ".pgm", ".tga" }; - pub fn isImagePath(path: []const u8) bool { - for (image_exts) |ext| { + for (config.image_exts) |ext| { if (std.ascii.endsWithIgnoreCase(path, ext)) return true; } return false; @@ -114,9 +109,9 @@ pub fn resolve(word_raw: []const u8, cwd: []const u8, realbuf: *[4096]u8) Target if (word.len == 0) return .none; // `@pN` addresses a pane, not a path: every platform, before the fs. - if (word.len > 2 and word[0] == '@' and word[1] == 'p') { + if (word.len > config.pane_addr.len and std.mem.startsWith(u8, word, config.pane_addr)) { var id: usize = 0; - for (word[2..]) |c| { + for (word[config.pane_addr.len..]) |c| { if (!std.ascii.isDigit(c)) break; id = id * 10 + (c - '0'); } else return .{ .pane = .{ .id = id, .line = pl.line, .col = pl.col } }; @@ -124,8 +119,9 @@ pub fn resolve(word_raw: []const u8, cwd: []const u8, realbuf: *[4096]u8) Target // a URL is a URL everywhere: no filesystem can answer it, so it leaves the // app (browser tab on web, xdg-open/open on the desktop). - if (std.mem.startsWith(u8, trimmed, "http://") or std.mem.startsWith(u8, trimmed, "https://")) - return .{ .url = trimmed }; + for (config.url_schemes) |scheme| { + if (std.mem.startsWith(u8, trimmed, scheme)) return .{ .url = trimmed }; + } if (platform_has_fs) { var joinbuf: [2048]u8 = undefined; -- cgit v1.3