summaryrefslogtreecommitdiff
path: root/src/mouse.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/mouse.zig')
-rw-r--r--src/mouse.zig51
1 files changed, 47 insertions, 4 deletions
diff --git a/src/mouse.zig b/src/mouse.zig
index 6b58a489..9f054e2e 100644
--- a/src/mouse.zig
+++ b/src/mouse.zig
@@ -711,7 +711,7 @@ fn reportGesture(
text: []const u8,
on_tag: bool,
operand: exec.PointerOperand,
- chorded: bool,
+ chord_arg: ?[]const u8,
) bool {
const scripted = p.panes[id] orelse return false;
if (scripted.fs.readers == 0) return false;
@@ -726,8 +726,21 @@ fn reportGesture(
if (range.q0 == range.q1 and text.len > 0) flag |= ctlfs.events.flag_expansion;
if (is_look) {
if (!named and std.mem.indexOfAny(u8, text, "/.:") != null) flag |= ctlfs.events.flag_filename;
- } else if (chorded) flag |= ctlfs.events.flag_chorded;
- return ctlfs.events.noteAction(p, id, action, range.q0, range.q1, flag, text);
+ } else if (chord_arg != null) flag |= ctlfs.events.flag_chorded;
+ if (!ctlfs.events.noteAction(p, id, action, range.q0, range.q1, flag, text)) return false;
+ // A chorded exec's argument follows as two records, its text and where
+ // it came from (`file:#q0,#q1`, empty when that is not known), as acme
+ // sends them (exec.c:182-188); the pane keeps it for the write-back.
+ if (chord_arg) |a| {
+ ctlfs.events.noteFollow(p, id, action, a);
+ var where: [512]u8 = undefined;
+ ctlfs.events.noteFollow(p, id, action, exec.heldSelectionPlace(p, id, &where) orelse "");
+ const pf = &scripted.fs;
+ if (pf.chord_arg) |old| p.gpa.free(old);
+ pf.chord_arg = p.gpa.dupe(u8, a) catch null;
+ pf.chord_at = range;
+ }
+ return true;
}
fn gestureRange(p: *Pardes, id: usize, text: []const u8, on_tag: bool, operand: exec.PointerOperand) ctlfs.pane.State.Range {
@@ -790,7 +803,7 @@ fn dispatchPointerBuiltin(
// A click that takes no text does nothing, and tells an event reader
// nothing, as acme's look3 and execute return on an empty expansion.
const operand = text orelse return;
- if (gesture) |g| if (reportGesture(p, id, cmd, operand, g.on_tag, g.operand, arg != null)) return;
+ if (gesture) |g| if (reportGesture(p, id, cmd, operand, g.on_tag, g.operand, arg)) return;
// A middle click on a body is the exec a bound REPL may take.
exec.runBuiltinFrom(p, cmd, id, "", exec.withArg(p, operand, arg), gesture != null and !gesture.?.on_tag);
}
@@ -1635,3 +1648,33 @@ test "mouse thumb buttons navigate once and clear selection gestures" {
try std.testing.expect(!pane.body.vsel.active);
try std.testing.expectEqual(.none, p.drag);
}
+
+test "a chorded exec an event reader holds is told with its argument, and written back runs with it" {
+ const th = @import("ninep/testing.zig");
+ const p = try th.withFile(std.testing.allocator, "Msg said\narg-text\n");
+ defer p.deinit();
+ const serial = p.panes[0].?.serial;
+ const event = ctlfs.Node.of(serial, .event);
+ const h = th.call(p, .{ .tag = 1, .op = .open, .node = event, .omode = 2 }).reply.handle;
+ // The argument: a selection held in the pane (its second line).
+ ctlfs.pane.setDot(p.panes[0].?, .{ .q0 = 9, .q1 = 17 });
+ p.chord_arg = try p.gpa.dupe(u8, "arg-text");
+ dispatchPointerBuiltin(p, 0, config.exec_cmd, "Msg", .{ .on_tag = false, .operand = .{ .row = 0, .col = 1, .text = "Msg" } });
+ var records: [3][]const u8 = undefined;
+ var got: [3][64]u8 = undefined;
+ for (&records, &got) |*r, *g| {
+ const bytes = th.call(p, .{ .tag = 2, .op = .read, .node = event, .handle = h, .size = 64 }).bytes;
+ @memcpy(g[0..bytes.len], bytes);
+ r.* = g[0..bytes.len];
+ }
+ try std.testing.expect(std.mem.endsWith(u8, records[0], " 11 3 Msg\n")); // builtin|expansion|chorded
+ try std.testing.expectEqualStrings("X0 0 0 8 arg-text\n", records[1][1..]);
+ try std.testing.expect(std.mem.endsWith(u8, records[2], "test.txt:#9,#17\n"));
+ // Written back whole, it runs with its argument.
+ var back: [64]u8 = undefined;
+ const line = try std.fmt.bufPrint(&back, "M{s}", .{records[0][1..]});
+ try std.testing.expectEqual(ctlfs.Status.ok, th.call(p, .{ .tag = 3, .op = .write, .node = event, .handle = h, .data = line }).reply.status);
+ const pane = p.panes[0].?;
+ try std.testing.expectEqualStrings("arg-text", pane.msg[0..pane.msg_len]);
+ _ = th.call(p, .{ .tag = 4, .op = .release, .node = event, .handle = h });
+}