diff options
Diffstat (limited to 'src/look.zig')
| -rw-r--r-- | src/look.zig | 37 |
1 files changed, 30 insertions, 7 deletions
diff --git a/src/look.zig b/src/look.zig index fb74353f..f34fa367 100644 --- a/src/look.zig +++ b/src/look.zig @@ -26,8 +26,11 @@ pub fn isFileChar(c: u8) bool { }; } -/// peel a trailing :NNN (or :NNN:) line suffix: main.zig:100 -> {main.zig, 100} -pub fn parsePathLine(tok: []const u8) struct { path: []const u8, line: usize } { +/// 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} +/// main.zig:100: -> {main.zig, 100, 0} grep -n's trailing delimiter +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; @@ -36,17 +39,28 @@ pub fn parsePathLine(tok: []const u8) struct { path: []const u8, line: usize } { 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 - return .{ .path = tok[0..sep], .line = line }; + 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; + } + return .{ .path = tok[0..sep], .line = line, .col = col }; } - return .{ .path = tok, .line = 0 }; + return .{ .path = tok, .line = 0, .col = 0 }; } pub const Target = union(enum) { none, dir: []const u8, // resolved absolute path, in caller's buf - file: struct { path: []const u8, line: usize }, + file: struct { path: []const u8, line: usize, col: usize }, image: struct { path: []const u8 }, url: []const u8, + /// `@p7:10:5` — pane 7, line 10, column 5 (0 = unspecified). The one + /// target that names a live pane instead of a path, because terminals and + /// output buffers have no file for a location to point at. + pane: struct { id: usize, line: usize, col: usize }, }; const image_exts = [_][]const u8{ ".png", ".jpg", ".jpeg", ".gif", ".bmp", ".ppm", ".pgm", ".tga" }; @@ -67,6 +81,15 @@ pub fn resolve(word_raw: []const u8, cwd: []const u8, realbuf: *[4096]u8) Target const word = pl.path; 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') { + var id: usize = 0; + for (word[2..]) |c| { + if (!std.ascii.isDigit(c)) break; + id = id * 10 + (c - '0'); + } else return .{ .pane = .{ .id = id, .line = pl.line, .col = pl.col } }; + } + if (platform_has_fs) { var joinbuf: [2048]u8 = undefined; const joined: ?[:0]u8 = if (word[0] == '/') @@ -78,14 +101,14 @@ pub fn resolve(word_raw: []const u8, cwd: []const u8, realbuf: *[4096]u8) Target const resolved = std.mem.span(rp); if (isDir(rp)) return .{ .dir = resolved }; if (isImagePath(resolved)) return .{ .image = .{ .path = resolved } }; - return .{ .file = .{ .path = resolved, .line = pl.line } }; + return .{ .file = .{ .path = resolved, .line = pl.line, .col = pl.col } }; } else { // web: URLs leave the app; tracked Zig sources resolve inside the // build-generated, read-only source filesystem. if (std.mem.startsWith(u8, trimmed, "http://") or std.mem.startsWith(u8, trimmed, "https://")) return .{ .url = trimmed }; if (resolveEmbedded(word, cwd, realbuf)) |source| - return .{ .file = .{ .path = source.path, .line = pl.line } }; + return .{ .file = .{ .path = source.path, .line = pl.line, .col = pl.col } }; return .none; } } |
