summaryrefslogtreecommitdiff
path: root/src/look.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/look.zig')
-rw-r--r--src/look.zig37
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;
}
}