summaryrefslogtreecommitdiff
path: root/src/look.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-30 16:51:34 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:17 -0300
commit6122159572f1cc950666df484e2074cf32f22eb5 (patch)
tree62e0d2f960af827826e1b615777cab9a243f506d /src/look.zig
parenta15eabc3b9d98ef5f659ca82a2b0f2afb7451a39 (diff)
downloadpardes-6122159572f1cc950666df484e2074cf32f22eb5.tar.gz
pardes-6122159572f1cc950666df484e2074cf32f22eb5.zip
A 9P look keeps its leading blanks: an indented line, or blanks alone, is looked for as written
Every line written to look or exec went through Messages.next, which trimmed blanks from both ends. A look of ` indented` then found the first bare `indented`, and a diff's blank context line ` ` looked at nothing. A look's line is now its text: only its `\r` and newline go. lookAt keeps the blanks for the word search too; it expands `~` only for a look that starts with one, and the path, address and command readings trim for themselves as before. runSearch keeps a pattern's blanks, since a builtin's argument comes trimmed already. An exec line is trimmed as before. fs.md says so. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/look.zig')
-rw-r--r--src/look.zig25
1 files changed, 17 insertions, 8 deletions
diff --git a/src/look.zig b/src/look.zig
index 76b58fbe..0f99722d 100644
--- a/src/look.zig
+++ b/src/look.zig
@@ -580,8 +580,10 @@ fn lookFirstHit(p: *Pardes, id: usize) void {
/// `open_empty` false a text search that found nothing opens no buffer.
pub fn runSearch(p: *Pardes, id: usize, pat_raw: []const u8, kind: Search, start: SearchStart, open_empty: bool) !usize {
const pane = p.panes[id] orelse return 0;
- const pat = std.mem.trim(u8, pat_raw, " \t\r\n");
- if (pat.len == 0) return 0;
+ // Blanks at either end are the pattern's (a look of an indented line);
+ // a builtin's argument comes trimmed already.
+ const pat = std.mem.trimEnd(u8, pat_raw, "\r\n");
+ if (std.mem.trim(u8, pat, " \t").len == 0) return 0;
const arena = p.scratch.allocator();
// where the pane lives: a file's directory, a shell's cwd — the walk
// root, and the directory the results buffer is named in. From a
@@ -1325,7 +1327,9 @@ fn addressed(tok: []const u8) ?struct { path: []const u8, expr: []const u8 } {
/// A look at text that names nothing to open: a `./` name is a missing
/// file, anything else a search for the text as written.
-fn lookUnresolved(p: *Pardes, id: usize, pane: *Pane, trimmed: []const u8) void {
+/// `text` is what a word search looks for: the look as written, its blanks
+/// kept; `trimmed` what a name is read from.
+fn lookUnresolved(p: *Pardes, id: usize, pane: *Pane, trimmed: []const u8, text: []const u8) void {
// A `./` or `../` name opened nothing: said, ENOENT, not searched
// for as a word. What is missing is the pane's directory when
// that is gone, else the file named.
@@ -1340,8 +1344,9 @@ fn lookUnresolved(p: *Pardes, id: usize, pane: *Pane, trimmed: []const u8) void
// A raw terminal's clicks are its program's; a look written over
// 9P is no keyboard input, and searches whatever the key mode.
if (pane.image != null or (pane.body.mode == .tty and !p.fs.serving)) return;
- if (p.settings.look_word == .search and pane.file != null) return lookWord(p, id, pane, trimmed);
- const rows = runSearch(p, id, trimmed, .text, .cursor, false) catch |err| {
+ if (text.len == 0) return;
+ if (p.settings.look_word == .search and pane.file != null) return lookWord(p, id, pane, text);
+ const rows = runSearch(p, id, text, .text, .cursor, false) catch |err| {
p.reportError(id, "search", err);
return;
};
@@ -1380,7 +1385,7 @@ fn lookAddress(p: *Pardes, id: usize, path: []const u8, expr: []const u8) void {
// A path that names nothing to open: the whole of what was written
// is looked for, as `zzq:2` is, and a miss quotes it as written.
if (!by_serial and resolveLookTarget(p, id, path, &realbuf) == .none)
- return lookUnresolved(p, id, p.panes[id] orelse return, path.ptr[0 .. path.len + 1 + expr.len]);
+ return lookUnresolved(p, id, p.panes[id] orelse return, path.ptr[0 .. path.len + 1 + expr.len], path.ptr[0 .. path.len + 1 + expr.len]);
const before = p.active;
const failures = p.fs.failures;
const was_missed = p.look_missed;
@@ -1598,8 +1603,12 @@ fn diffFile(base: []const u8, path: []const u8, out: *[4096]u8) ?[]const u8 {
pub fn lookAt(p: *Pardes, id: usize, operand_raw: []const u8) void {
const pane = p.panes[id] orelse return;
// `~/x`, `~/x:12`, `~/x:/re/`: home, before anything reads the text.
+ // Anything else as written: a look of ` return x` is of those blanks
+ // too (the path, address and command readings below trim for
+ // themselves).
var home_buf: [4096]u8 = undefined;
- const operand = filesystem.expandHome(std.mem.trim(u8, operand_raw, " \t\r\n"), &home_buf);
+ const stripped = std.mem.trim(u8, operand_raw, " \t\r\n");
+ const operand = if (stripped.len > 0 and stripped[0] == '~') filesystem.expandHome(stripped, &home_buf) else std.mem.trimEnd(u8, operand_raw, "\r\n");
// A +Recent row is a remembered place: reopened and clamped to the file
// as it is now, never a miss for a line the file no longer has.
if (isRecentList(pane)) {
@@ -1665,7 +1674,7 @@ pub fn lookAt(p: *Pardes, id: usize, operand_raw: []const u8) void {
pane.sel = @splat(.{});
}
switch (found) {
- .none => lookUnresolved(p, id, pane, trimmed),
+ .none => lookUnresolved(p, id, pane, trimmed, std.mem.trimEnd(u8, txt, "\r\n")),
.pane => |t| {
// `@p<serial>` names no open pane: a miss, said.
if (t.id >= MAX_PANES or p.panes[t.id] == null) return missed(p, id, "no pane {s}", .{pl.path[0..@min(pl.path.len, 32)]});