diff options
Diffstat (limited to 'src/exec.zig')
| -rw-r--r-- | src/exec.zig | 128 |
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; |
