summaryrefslogtreecommitdiff
path: root/src/pardes.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/pardes.zig')
-rw-r--r--src/pardes.zig50
1 files changed, 45 insertions, 5 deletions
diff --git a/src/pardes.zig b/src/pardes.zig
index c9d93979..87041f39 100644
--- a/src/pardes.zig
+++ b/src/pardes.zig
@@ -18,6 +18,7 @@ pub const edit = @import("edit.zig");
pub const body_layer = @import("body_layer.zig");
pub const Messages = @import("Messages.zig");
pub const Registers = @import("Registers.zig");
+pub const Macro = @import("Macro.zig");
pub const Pipe = @import("Pipe.zig");
pub const colors = @import("colors.zig");
pub const Theme = colors.Theme;
@@ -455,6 +456,7 @@ test {
_ = @import("edit.zig");
_ = @import("Messages.zig");
_ = @import("Registers.zig");
+ _ = @import("Macro.zig");
_ = @import("selection_pipe.zig");
_ = @import("Pipe.zig");
_ = @import("colors.zig");
@@ -4096,6 +4098,7 @@ pub const Pardes = struct {
in_len: usize = 0,
registers: Registers = .{},
+ macro: Macro = .{},
/// an edited text nothing kept (a tag's shown text): see edit.retireEditText
retired_edit: ?[]u8 = null,
clip_pending: ?edit.ClipRequest = null,
@@ -4269,6 +4272,7 @@ pub const Pardes = struct {
for (&p.col_tags) |*t| t.deinit(gpa);
p.global_tag.deinit(gpa);
p.registers.deinit(gpa);
+ p.macro.deinit(gpa);
if (p.retired_edit) |r| gpa.free(r);
if (p.dump_out) |d| gpa.free(d);
p.fallback.deinit();
@@ -5125,7 +5129,21 @@ pub const Pardes = struct {
} else panes.Terminal.enterTty(p, id);
}
+ /// Type `keys` again (a macro, `.`): through the modal handling as
+ /// typed, except that no key leaves the text for pardes's own acts --
+ /// Esc does not hop panes, and Enter and Tab neither look nor execute.
+ pub fn replayKeys(p: *Pardes, keys: []const Macro.Stored) void {
+ if (p.macro.replaying >= 8) return; // a macro typing itself
+ p.macro.replaying += 1;
+ defer p.macro.replaying -= 1;
+ for (keys) |stored| {
+ var buf: [4]u8 = undefined;
+ p.handleKey(stored.typed(&buf));
+ }
+ }
+
fn handleKey(p: *Pardes, key: Key) void {
+ if (p.macro.recording != 0 and p.macro.replaying == 0) Macro.append(&p.macro.recorded, p.gpa, key);
// A question waiting on a pane takes the next key, whatever mode the
// pane or a header edit is in: that key is its answer.
for (p.panes, 0..) |slot, id| {
@@ -5266,7 +5284,7 @@ pub const Pardes = struct {
if (normal.selRegexArmed(pane)) |a| if (p.promptTarget(pane)) |answered| normal.applySelRegex(p, pane, answered, a.pat, a.split);
return;
}
- if (t.mode == .normal and (hit(key, config.look_key) or hit(key, config.exec_key))) {
+ if (t.mode == .normal and p.macro.replaying == 0 and (hit(key, config.look_key) or hit(key, config.exec_key))) {
// A header's words are the workspace's and column's commands, so
// Enter runs them there, as the execute key does.
const header_word = t.what == .column or t.what == .workspace;
@@ -5320,14 +5338,36 @@ pub const Pardes = struct {
defer if (t.refused) {
t.cur_row, t.cur_col, t.vsel, t.msel, t.sels, t.nsel = kept;
};
+ const was = t.mode;
switch (t.mode) {
.normal => normal.handleNormal(p, t, key),
- .insert => {
- if (hit(key, config.escape) and !t.reg_wait) return edit.exitInsert(p, t);
- edit.handleInsert(p, t, key);
- },
+ .insert => if (hit(key, config.escape) and !t.reg_wait)
+ edit.exitInsert(p, t)
+ else
+ edit.handleInsert(p, t, key),
.tty => panes.Terminal.forwardKey(p, p.active, key),
}
+ // `.`'s log: the keys of the normal command that entered insert
+ // mode, then everything typed until it is left
+ if (p.macro.repeating) return;
+ const m = &p.macro;
+ switch (was) {
+ .normal => {
+ Macro.append(&m.command, p.gpa, key);
+ if (t.mode == .insert) {
+ m.last_insert.clearRetainingCapacity();
+ m.last_insert.appendSlice(p.gpa, m.command.items) catch {};
+ m.typed_from = m.last_insert.items.len;
+ m.inserting = true;
+ m.command.clearRetainingCapacity();
+ } else if (std.meta.eql(t.normal, modal.Normal.State{})) m.command.clearRetainingCapacity();
+ },
+ .insert => if (m.inserting) {
+ Macro.append(&m.last_insert, p.gpa, key);
+ if (t.mode != .insert) m.inserting = false;
+ },
+ .tty => {},
+ }
}
fn leaderKey(p: *Pardes, key: Key) void {