diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/ninep/events.zig | 30 |
1 files changed, 29 insertions, 1 deletions
diff --git a/src/ninep/events.zig b/src/ninep/events.zig index 63405781..fa90fe71 100644 --- a/src/ninep/events.zig +++ b/src/ninep/events.zig @@ -688,6 +688,15 @@ fn scanNumber(data: []const u8, i: *usize) ?u32 { } /// Writing a Look or Exec record back performs the action it names. +/// The word a click at `at` expands to: the file name or word run under it +/// on its line (look.expandedWord), else nothing. +fn expandAt(whole: []const u8, at: usize) []const u8 { + const start = if (std.mem.lastIndexOfScalar(u8, whole[0..at], '\n')) |nl| nl + 1 else 0; + const end = std.mem.indexOfScalarPos(u8, whole, at, '\n') orelse whole.len; + const b = look.expandedWord(whole[start..end], at - start) orelse return ""; + return whole[start + b.lo .. start + b.hi]; +} + pub fn writeEvent(p: *Pardes, asked: Req, id: usize) Reply { const pane0 = p.panes[id] orelse return Reply.fail(asked.tag, E.NOENT); // Its last record needs no newline, as every other line file's last @@ -723,7 +732,10 @@ pub fn writeEvent(p: *Pardes, asked: Req, id: usize) Reply { const whole = if (r.action.onTag()) pane_files.tagOf(p, pane) else pane_files.bodyOf(pane); const lo = @min(@as(usize, r.q0), whole.len); const hi = @max(lo, @min(@as(usize, r.q1), whole.len)); - const said = if (r.q0 == r.q1) r.text orelse whole[lo..hi] else whole[lo..hi]; + // An empty range with no text is a click at that point: expanded as + // the click would be, to the word or file name under it (acme's + // expand, look.c). + const said = if (r.q0 == r.q1) r.text orelse expandAt(whole, lo) else whole[lo..hi]; const text = p.scratch.allocator().dupe(u8, said) catch continue; switch (r.action) { // A body's click written back is done as the click would have @@ -1237,6 +1249,22 @@ test "a pane named before it is announced has its new under that name, and no re try testing.expect(std.mem.indexOf(u8, after.bytes, try std.fmt.bufPrint(&want, "rename {d} ", .{shell.serial})) == null); } +test "an empty range written back is expanded as a click at that point would be" { + const p = try withFile(testing.allocator, "abc def abc\nx Newcol y\n"); + defer p.deinit(); + const serial = serialOf(p); + const pane = p.panes[p.paneBySerial(serial).?].?; + const event = Node.of(serial, .event); + // A look at 1 is a look at `abc`: from dot, past the first, the next. + pane_files.setDot(pane, .{ .q0 = 4, .q1 = 4 }); + try testing.expectEqual(Status.ok, wr(p, event, "ML1 1\n").reply.status); + try testing.expectEqual(pane_files.State.Range{ .q0 = 8, .q1 = 11 }, pane_files.dotOf(pane)); + // An exec at 15 runs the word there, `Newcol`. + const cols = p.ncol; + try testing.expectEqual(Status.ok, wr(p, event, "MX15 15\n").reply.status); + try testing.expectEqual(cols + 1, p.ncol); +} + test "opens of the log share the open records, and a closed one frees its record" { const gpa = testing.allocator; const p = try withFile(gpa, "x\n"); |
