summaryrefslogtreecommitdiff
path: root/src/exec.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/exec.zig')
-rw-r--r--src/exec.zig128
1 files changed, 77 insertions, 51 deletions
diff --git a/src/exec.zig b/src/exec.zig
index f3819a83..2a6bd55c 100644
--- a/src/exec.zig
+++ b/src/exec.zig
@@ -108,6 +108,25 @@ pub const PointerOperand = struct {
};
pub fn pointerOperand(p: *Pardes, pane: *Pane, clicked: Pane.Sel) PointerOperand {
+ return operandAt(p, pane, clicked, true, true);
+}
+
+/// The body's explicit selection, else what a look at its cursor takes.
+pub fn currentOperand(p: *Pardes, pane: *Pane) ?[]const u8 {
+ if (pane.file == null and !pane.isTerminal()) return null;
+ if ((pane.body.vsel.active and pane.body.vsel.explicit) or pane.body.msel.active) return edit.currentSelText(p, pane);
+ return operandAt(p, pane, look.cursorWordSel(p, pane), false, true).text;
+}
+
+/// What a look or an exec at `clicked` takes: the ONE expansion every
+/// entry point uses (a B2/B3 click, Enter and Tab in normal mode, a bare
+/// Look), so a diff line, a hunk line's code word and a file word expand
+/// alike however they are reached (look.zig's "every entry point" test).
+/// `borrow`: a gesture inside a kept or modal selection takes it; the
+/// keyboard has said which selection it means already. `for_look`: only a
+/// look takes a diff line's address; an exec there takes the word, as
+/// anywhere else.
+pub fn operandAt(p: *Pardes, pane: *Pane, clicked: Pane.Sel, borrow: bool, for_look: bool) PointerOperand {
const visible = clicked.r0 - @as(i32, pane.tag_rows);
const wrapped = pane.wrapAt(visible);
const raw = pane.isTerminal() and pane.body.mode == .tty and clicked.r0 >= pane.tag_rows;
@@ -126,48 +145,49 @@ pub fn pointerOperand(p: *Pardes, pane: *Pane, clicked: Pane.Sel) PointerOperand
else
clicked.c0;
var result: PointerOperand = .{ .row = row, .col = col };
-
- const kept = pane.sel[sel_slot];
- const inside_kept = if (pane.pointerSelection(kept)) |rows| inside: {
- if (clicked.r0 < pane.tag_rows) break :inside false;
- if (pane.file != null and clicked.c0 < panes.File.gutterWidth(pane)) break :inside false;
- const source_col: usize = @intCast(@max(0, col));
- for (rows) |span| {
- const target = if (span.raw_terminal)
- (if (raw) span.row else pane.surfRow(span.row))
- else
- (if (raw) pane.gridRow(span.row) else span.row);
- var lo = span.lo;
- var hi = span.hi;
- if (span.raw_terminal and !raw) {
- lo -|= span.prompt_bytes;
- hi -|= span.prompt_bytes;
- } else if (!span.raw_terminal and raw) {
- const prefix = panes.terminal.promptPrefixBytes(pane, row, raw_line);
- lo += prefix;
- hi += prefix;
+ if (borrow) {
+ const kept = pane.sel[sel_slot];
+ const inside_kept = if (pane.pointerSelection(kept)) |rows| inside: {
+ if (clicked.r0 < pane.tag_rows) break :inside false;
+ if (pane.file != null and clicked.c0 < panes.File.gutterWidth(pane)) break :inside false;
+ const source_col: usize = @intCast(@max(0, col));
+ for (rows) |span| {
+ const target = if (span.raw_terminal)
+ (if (raw) span.row else pane.surfRow(span.row))
+ else
+ (if (raw) pane.gridRow(span.row) else span.row);
+ var lo = span.lo;
+ var hi = span.hi;
+ if (span.raw_terminal and !raw) {
+ lo -|= span.prompt_bytes;
+ hi -|= span.prompt_bytes;
+ } else if (!span.raw_terminal and raw) {
+ const prefix = panes.terminal.promptPrefixBytes(pane, row, raw_line);
+ lo += prefix;
+ hi += prefix;
+ }
+ if (target == row and source_col >= lo and source_col < hi)
+ break :inside true;
}
- if (target == row and source_col >= lo and source_col < hi)
- break :inside true;
+ break :inside false;
+ } else edit.spanHas(clicked.r0, clicked.c0, kept.r0, kept.c0, kept.r1, kept.c1);
+ if (kept.state == .done and inside_kept) {
+ result.text = edit.selectionText(p, pane, kept) catch null;
+ result.preview = kept;
+ return result;
+ }
+ if (pane.body.vsel.active and pane.body.vsel.explicit and
+ edit.spanHas(row, col, pane.body.vsel.row, pane.body.vsel.col, pane.body.cur_row, pane.body.cur_col))
+ {
+ result.text = edit.currentSelText(p, pane);
+ return result;
+ }
+ if (pane.body.msel.active and row >= @min(pane.body.msel.r0, pane.body.msel.r1) and
+ row <= @max(pane.body.msel.r0, pane.body.msel.r1))
+ {
+ result.text = edit.currentSelText(p, pane);
+ return result;
}
- break :inside false;
- } else edit.spanHas(clicked.r0, clicked.c0, kept.r0, kept.c0, kept.r1, kept.c1);
- if (kept.state == .done and inside_kept) {
- result.text = edit.selectionText(p, pane, kept) catch null;
- result.preview = kept;
- return result;
- }
- if (pane.body.vsel.active and pane.body.vsel.explicit and
- edit.spanHas(row, col, pane.body.vsel.row, pane.body.vsel.col, pane.body.cur_row, pane.body.cur_col))
- {
- result.text = edit.currentSelText(p, pane);
- return result;
- }
- if (pane.body.msel.active and row >= @min(pane.body.msel.r0, pane.body.msel.r1) and
- row <= @max(pane.body.msel.r0, pane.body.msel.r1))
- {
- result.text = edit.currentSelText(p, pane);
- return result;
}
// In a diff, a file or hunk header, and a hunk line pointed at on its
// `+`/`-`/` ` prefix, expand to the whole line (the hover shows it) and
@@ -175,7 +195,7 @@ pub fn pointerOperand(p: *Pardes, pane: *Pane, clicked: Pane.Sel) PointerOperand
// look from there. A hunk line's code is words, looked at as ever:
// the code's, the prefix never one of them.
var word_from: usize = 0;
- if (clicked.r0 >= pane.tag_rows and clicked.r0 == clicked.r1 and clicked.c0 == clicked.c1 and row >= 0) diff: {
+ if (for_look and clicked.r0 >= pane.tag_rows and clicked.r0 == clicked.r1 and clicked.c0 == clicked.c1 and row >= 0) diff: {
var address_buf: [4096 + 32]u8 = undefined;
const at: usize = @intCast(row);
const click = look.diffClick(p, pane, at, @intCast(@max(0, col)), &address_buf) orelse break :diff;
@@ -925,21 +945,16 @@ pub fn namesBuiltin(txt: []const u8) bool {
pub const acme_words = [_]struct { acme: []const u8, pardes: ?[]const u8, hint: []const u8 = "" }{
.{ .acme = "Put", .pardes = "Save" },
.{ .acme = "Delete", .pardes = "Del" },
- .{ .acme = "Get", .pardes = null, .hint = ": write get to the pane's ctl" },
- .{ .acme = "Putall", .pardes = null, .hint = ": Save each pane" },
.{ .acme = "Snarf", .pardes = null, .hint = ": y yanks the selection" },
.{ .acme = "Cut", .pardes = null, .hint = ": d deletes the selection into a register" },
.{ .acme = "Paste", .pardes = null, .hint = ": p puts a register" },
- .{ .acme = "Zerox", .pardes = null },
.{ .acme = "Sort", .pardes = null },
- .{ .acme = "Load", .pardes = null, .hint = ": Restore loads a dump" },
+ .{ .acme = "Load", .pardes = "Restore" },
.{ .acme = "ID", .pardes = null },
.{ .acme = "Send", .pardes = null },
- .{ .acme = "Tab", .pardes = null },
- .{ .acme = "Indent", .pardes = null },
+ .{ .acme = "Indent", .pardes = null, .hint = ": Enter, o and O keep the indent always" },
.{ .acme = "Local", .pardes = null },
- .{ .acme = "Incl", .pardes = null },
- .{ .acme = "Abort", .pardes = null },
+ .{ .acme = "Abort", .pardes = null, .hint = ": a debugging word that crashes acme" },
};
pub fn executeBuiltinLine(p: *Pardes, id: usize, txt: []const u8) bool {
@@ -1006,7 +1021,9 @@ fn applyInitText(p: *Pardes, text: []const u8, settings_only: bool) void {
// (`#ff0000`).
if (word.len == 0 or isComment(word)) continue;
const setting = config.Runtime.find(word[0 .. std.mem.indexOfAny(u8, word, " \t") orelse word.len]);
- if (settings_only and setting == null) continue;
+ // Saved in a pane, its settings run again and its commands do not:
+ // a word that is neither (a typo) is still said, as at startup.
+ if (settings_only and setting == null and std.meta.stringToEnum(Builtin, word[0 .. std.mem.indexOfAny(u8, word, " \t+") orelse word.len]) != null) continue;
// One init file serves the window and the terminal: a setting only
// the other has (Font, GripWidth in a terminal) is no failure here.
if (setting) |s| if (!s.enabled(builtins.capabilities)) continue;
@@ -1103,6 +1120,15 @@ pub fn applySettingBuiltin(p: *Pardes, setting: config.Runtime.Setting, arg: ?[]
}
if (!p.settings.apply(setting, want)) p.reportFailure(p.active, "Shell: takes " ++ comptime config.Runtime.takes(.shell).?);
},
+ // Bare, it says the width, as acme's Tab does.
+ .tab => if (arg == null) {
+ var said: [32]u8 = undefined;
+ return p.setMessage(p.active, std.fmt.bufPrint(&said, "Tab {d}", .{p.settings.tab}) catch "Tab");
+ } else if (!p.settings.apply(setting, arg)) {
+ p.reportFailure(p.active, "Tab: invalid tab width: takes " ++ comptime config.Runtime.takes(.tab).?);
+ } else {
+ config.tab_now = p.settings.tab;
+ },
// Said with what it takes: its words, its range.
else => if (!p.settings.apply(setting, arg)) {
var text: [160]u8 = undefined;
@@ -1116,7 +1142,7 @@ pub fn applySettingBuiltin(p: *Pardes, setting: config.Runtime.Setting, arg: ?[]
if (pane.file) |*file| file.syntax_dirty = true;
}
}
- if (p.settings.tree_context_tag_style != previous_context_style) {
+ if (p.settings.tree_context_tag_style != previous_context_style or setting.action == .tab) {
for (p.panes, 0..) |slot, id| {
p.surface.body_layers[id].rows = 0;
const pane = slot orelse continue;