diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/config.zig | 7 | ||||
| -rw-r--r-- | src/layout.zig | 6 | ||||
| -rw-r--r-- | src/panes.zig | 1 | ||||
| -rw-r--r-- | src/pardes.zig | 109 | ||||
| -rw-r--r-- | src/tag_line.zig | 1 |
5 files changed, 71 insertions, 53 deletions
diff --git a/src/config.zig b/src/config.zig index 555173e4..fea22609 100644 --- a/src/config.zig +++ b/src/config.zig @@ -203,7 +203,8 @@ pub const jump_keys = [_]struct { chord: Chord, cmd: Builtin }{ .{ .chord = .{ .cp = 'i', .ctrl = true }, .cmd = .Forward }, }; -pub const topbar_down: []const Chord = &.{.{ .cp = 'j' }}; +pub const topbar_down: []const Chord = &.{ .{ .cp = 'J' }, .{ .cp = Key.down } }; +pub const topbar_up: []const Chord = &.{ .{ .cp = 'K' }, .{ .cp = Key.up } }; pub const topbar_left: []const Chord = &.{.{ .cp = 'h' }}; pub const topbar_right: []const Chord = &.{.{ .cp = 'l' }}; @@ -423,8 +424,8 @@ pub const line_end: []const Chord = &.{ .{ .cp = '$' }, .{ .cp = Key.end } }; pub const line_first_nonws: []const Chord = &.{.{ .cp = '^' }}; // Bare G does nothing; ge reaches the last line. pub const goto_line: []const Chord = &.{.{ .cp = 'G' }}; -pub const line_move_left: []const Chord = &.{.{ .cp = Key.left }}; -pub const line_move_right: []const Chord = &.{.{ .cp = Key.right }}; +pub const line_move_left: []const Chord = move_left; +pub const line_move_right: []const Chord = move_right; pub const half_page_down: []const Chord = &.{.{ .cp = 'd', .ctrl = true }}; pub const half_page_up: []const Chord = &.{.{ .cp = 'u', .ctrl = true }}; diff --git a/src/layout.zig b/src/layout.zig index 6c5f4474..2df64594 100644 --- a/src/layout.zig +++ b/src/layout.zig @@ -777,6 +777,7 @@ pub fn removePane(p: *Pardes, id: usize) void { while (k + 1 < p.col_n[c]) : (k += 1) p.col_panes[c][k] = p.col_panes[c][k + 1]; p.col_n[c] -= 1; if (p.col_n[c] == 0) { + p.exitHeader(); if (p.ncol > 1) p.col_weight[if (c > 0) c - 1 else c + 1] +|= p.col_weight[c]; var j = c; while (j + 1 < p.ncol) : (j += 1) { @@ -787,7 +788,6 @@ pub fn removePane(p: *Pardes, id: usize) void { } p.ncol -= 1; p.col_tags[p.ncol] = .{}; - p.topbar_col = null; } } @@ -795,6 +795,7 @@ pub fn joinCol(p: *Pardes) void { const f = findPane(p, p.active) orelse return; const c = f.col; if (c + 1 >= p.ncol) return; + p.exitHeader(); const dst = c + 1; p.col_weight[dst] +|= p.col_weight[c]; for (0..p.col_n[c]) |k| p.col_panes[dst][p.col_n[dst] + k] = p.col_panes[c][k]; @@ -808,7 +809,6 @@ pub fn joinCol(p: *Pardes) void { } p.ncol -= 1; p.col_tags[p.ncol] = .{}; - p.topbar_col = null; } pub fn canSplitColumn(p: *Pardes, source_id: usize) bool { @@ -847,6 +847,7 @@ pub fn splitColumn(p: *Pardes, source_id: usize, id: usize, before: bool) bool { const new_weight = old_weight - source_weight; p.col_weight[source_col] = source_weight; const c = source_col + @intFromBool(!before); + p.exitHeader(); var j = p.ncol; while (j > c) : (j -= 1) { p.col_panes[j] = p.col_panes[j - 1]; @@ -856,7 +857,6 @@ pub fn splitColumn(p: *Pardes, source_id: usize, id: usize, before: bool) bool { } p.col_weight[c] = new_weight; p.col_tags[c] = .{}; - p.topbar_col = null; p.col_panes[c][0] = id; p.col_n[c] = 1; p.ncol += 1; diff --git a/src/panes.zig b/src/panes.zig index 1487e0ef..ea733ea1 100644 --- a/src/panes.zig +++ b/src/panes.zig @@ -147,6 +147,7 @@ pub const Pane = struct { /// restore it on exit so clicking the tag never changes the pane's mode tag_mode: Mode = .normal, tag_col: u16 = 0, + tag_saved_col: ?u16 = null, tag_anchor: u16 = 0, /// Filename edits are staged separately from the live buffer identity. /// Enter commits the name; leaving tag editing discards the draft. diff --git a/src/pardes.zig b/src/pardes.zig index 3c0205d2..dd7fc5ed 100644 --- a/src/pardes.zig +++ b/src/pardes.zig @@ -7128,6 +7128,7 @@ pub const Pardes = struct { } fn enterTagEdit(p: *Pardes, pane: *Pane, col: i32) void { + if (pane.tag_edit and pane.prompt == .none) pane.tag_saved_col = pane.tag_col; const edit0: i32 = @intCast((p.tagPrefix(pane) catch return).len); p.seedTail(pane); if (!pane.tag_init) return; @@ -7141,14 +7142,18 @@ pub const Pardes = struct { const tail = pane.tagSlice(); const lead: i32 = @intCast(tail.len - std.mem.trimStart(u8, tail, " ").len); if (col < 0) { - pane.tag_col = @intCast(@min(edit0 + lead, end)); const text = p.tagText(p.scratch.allocator(), pane) catch return; - if (p.paneIdOf(pane)) |id| { + if (pane.tag_saved_col) |saved| { + pane.tag_col = @intCast(modal.graphemeStart(text, @min(saved, text.len))); + } else { + pane.tag_col = @intCast(@min(edit0 + lead, end)); + } + if (pane.tag_saved_col == null) if (p.paneIdOf(pane)) |id| { const width = @max(1, p.rects[id].w -| config.GUTTER); // Reveal the suffix together with its commands, not merely the // first command letter at the viewport's far right edge. pane.tag_scroll = @intCast(panes.File.displayWidth(text) + 1 -| width); - } + }; } else { const text = p.tagText(p.scratch.allocator(), pane) catch return; pane.tag_col = @intCast(@min(text.len, panes.File.rawAtDisplay(text, @as(usize, @intCast(col)) + pane.tag_scroll))); @@ -7156,6 +7161,7 @@ pub const Pardes = struct { } fn exitTagEdit(pane: *Pane) void { + if (pane.tag_edit and pane.prompt == .none) pane.tag_saved_col = pane.tag_col; if (pane.isTerminal()) { if (pane.tag_edit) pane.mode = pane.tag_mode; } else pane.mode = .normal; @@ -7334,7 +7340,7 @@ pub const Pardes = struct { const text = p.tagText(p.scratch.allocator(), pane) catch return; const cur: usize = @min(@as(usize, pane.tag_col), text.len); // Esc abandons the command line: back to the body, tail kept as text - if (hit(key, config.escape)) return exitTagEdit(pane); + if (hit(key, config.escape) or hit(key, &.{.{ .cp = 'j' }})) return exitTagEdit(pane); if (hit(key, config.look_key) or hit(key, config.exec_key)) { const cmd = if (hit(key, config.look_key)) config.look_cmd else config.exec_cmd; const txt = p.tagChordText(pane) orelse return; @@ -7364,7 +7370,9 @@ pub const Pardes = struct { return; } const dir: ?Builtin = for (config.window_keys) |wk| { - if (hit(key, &.{wk.letter})) break wk.cmd; + var letter = wk.letter; + letter.cp = std.ascii.toUpper(@intCast(letter.cp)); + if (hit(key, &.{letter})) break wk.cmd; } else null; if (dir) |d| { const from = p.active; @@ -7372,17 +7380,13 @@ pub const Pardes = struct { if (p.active == from) { if (d == .Up) { exitTagEdit(pane); - p.topbar_col = 0; - p.header_column = if (p.columnBarHeight() > 0) (layout.findPane(p, from) orelse return).col else null; - p.header_insert = false; - p.header_anchor = null; - p.header_drag = false; + p.enterHeader(if (p.columnBarHeight() > 0) (layout.findPane(p, from) orelse return).col else null); } return; } const dest = p.panes[p.active] orelse return; exitTagEdit(pane); // the pane we left is fully out of its tag... - p.enterTagEdit(dest, -1); // ...and the tail's start is the new park + p.enterTagEdit(dest, -1); if (dest.tag_edit) dest.mode = .normal; return; } @@ -7421,23 +7425,46 @@ pub const Pardes = struct { return modal.moveWord(text, .{ .anchor = cur, .head = modal.nextGrapheme(text, cur) }, 1, t); } + fn rememberHeader(p: *Pardes) void { + const col = p.topbar_col orelse return; + const line = if (p.header_column) |c| &p.col_tags[c] else &p.global_tag; + line.saved_col = col; + } + + pub fn exitHeader(p: *Pardes) void { + p.rememberHeader(); + p.topbar_col = null; + p.header_column = null; + p.header_anchor = null; + p.header_drag = false; + p.header_scroll = 0; + } + + fn enterHeader(p: *Pardes, column: ?usize) void { + p.rememberHeader(); + p.header_column = column; + const line = if (column) |c| &p.col_tags[c] else &p.global_tag; + const text = p.headerText(); + p.topbar_col = @intCast(modal.graphemeStart(text, @min(line.saved_col orelse 0, text.len))); + p.header_insert = false; + p.header_anchor = null; + p.header_drag = false; + p.header_scroll = 0; + } + fn topbarKey(p: *Pardes, key: Key) void { const bar = p.headerText(); // the line GROWS a `Restore <path>` word the moment a dump lands, so // the column is clamped against the live line on every use const cur: usize = @min(@as(usize, p.topbar_col orelse return), bar.len); - if (hit(key, config.escape)) { - p.topbar_col = null; // the active pane still has its body focus - p.header_column = null; - p.header_anchor = null; - p.header_drag = false; - p.header_scroll = 0; + if (hit(key, config.escape) or (!p.header_insert and hit(key, &.{.{ .cp = 'j' }}))) { + p.exitHeader(); return; } if (hit(key, config.look_key) or hit(key, config.exec_key)) { const a = p.header_anchor orelse @as(u16, @intCast(cur)); const word = if (a != cur) bar[@min(a, cur)..@max(a, cur)] else wordAtCol(bar, cur); - p.topbar_col = null; + p.exitHeader(); if (word.len > 0) { if (!p.header_insert) { // Preserve the workspace tag's keyboard command semantics. @@ -7490,9 +7517,7 @@ pub const Pardes = struct { } if (hit(key, config.topbar_down)) { if (p.header_column == null and p.columnBarHeight() > 0) { - p.header_column = if (layout.findPane(p, p.active)) |f| f.col else 0; - p.header_scroll = 0; - p.topbar_col = 0; + p.enterHeader(if (layout.findPane(p, p.active)) |f| f.col else 0); return; } var dest: ?usize = null; @@ -7507,17 +7532,14 @@ pub const Pardes = struct { } const d = dest orelse return; const pane = p.panes[d].?; - p.topbar_col = null; - p.header_column = null; + p.exitHeader(); p.active = d; p.enterTagEdit(pane, -1); if (pane.tag_edit) pane.mode = .normal; return; } - if (key.cp == 'k' or key.cp == Key.up) { - p.header_column = null; - p.header_anchor = null; - p.topbar_col = 0; + if (hit(key, config.topbar_up)) { + p.enterHeader(null); return; } const col: ?usize = if (hit(key, config.topbar_left)) @@ -7905,7 +7927,7 @@ pub const Pardes = struct { const pane = p.panes[id] orelse return; if (!pane.isTerminal()) return; p.active = id; - p.topbar_col = null; + p.exitHeader(); if (pane.tag_edit) exitTagEdit(pane); if (pane.mode == .tty) { pane.mode = .normal; @@ -10948,6 +10970,9 @@ pub const Pardes = struct { if (mrow < TOPBAR_H + p.columnBarHeight() and m.kind == .press and (m.button == config.select_button or m.button == config.exec_button or m.button == config.look_button)) { + if (m.button == config.select_button) if (p.panes[p.active]) |pane| { + if (pane.tag_edit) exitTagEdit(pane); + }; var column: ?usize = null; var x: u16 = 0; if (mrow >= TOPBAR_H) { @@ -10962,6 +10987,7 @@ pub const Pardes = struct { if (column == null) return; } if (p.header_column != column) { + p.rememberHeader(); p.topbar_col = null; p.header_anchor = null; p.header_scroll = 0; @@ -10970,7 +10996,6 @@ pub const Pardes = struct { const bar = p.headerText(); const at = @min(bar.len, panes.File.rawAtDisplay(bar, (mcol -| x) + p.header_scroll)); if (m.button == config.select_button) { - if (p.panes[p.active]) |pane| exitTagEdit(pane); p.topbar_col = @intCast(at); p.header_anchor = @intCast(at); p.header_insert = true; @@ -10981,7 +11006,7 @@ pub const Pardes = struct { break :blk if (at >= @min(a, cur) and at < @max(a, cur)) bar[@min(a, cur)..@max(a, cur)] else wordAtCol(bar, at); } else wordAtCol(bar, at); const word = p.scratch.allocator().dupe(u8, selected) catch return; - p.topbar_col = null; + p.exitHeader(); if (word.len > 0) { if (m.button == config.exec_button) { const named = std.meta.stringToEnum(Builtin, word); @@ -10993,10 +11018,7 @@ pub const Pardes = struct { return; } if (m.kind == .press) { - p.topbar_col = null; - p.header_column = null; - p.header_anchor = null; - p.header_drag = false; + p.exitHeader(); } switch (m.button) { @@ -11083,6 +11105,9 @@ pub const Pardes = struct { const id = hovered orelse return; const r = p.rects[id]; const pane = p.panes[id] orelse return; + if (id != p.active) if (p.panes[p.active]) |previous| { + if (previous.tag_edit) exitTagEdit(previous); + }; const tag_y = if (p.settings.tag_bottom) r.y + r.h -| BOX_H else r.y; const body_y = if (p.settings.tag_bottom) r.y else r.y + BOX_H; if (mcol < r.x + config.GUTTER) { @@ -11992,13 +12017,7 @@ pub const Pardes = struct { const tagline_metrics_changed = setting.action == .tagline_size and p.settings.font.tagline_percent != previous_tagline_percent; if (p.settings.column_tags != previous_column_tags) { - if (p.header_column != null) { - p.topbar_col = null; - p.header_column = null; - p.header_anchor = null; - p.header_drag = false; - p.header_scroll = 0; - } + if (p.header_column != null) p.exitHeader(); } if (transition_changed or tagline_metrics_changed or p.settings.column_tags != previous_column_tags) { p.abandonPanelAnimations(); @@ -12474,11 +12493,7 @@ pub const Pardes = struct { /// deferred greetings. The mirror of the prototype's loop epilogue. fn sync(p: *Pardes) void { if (p.header_column != null and p.columnBarHeight() == 0) { - p.topbar_col = null; - p.header_column = null; - p.header_anchor = null; - p.header_drag = false; - p.header_scroll = 0; + p.exitHeader(); } p.reapPanes(); layout.compute(p); @@ -12486,7 +12501,7 @@ pub const Pardes = struct { p.trackJump(); for (&p.panes, 0..) |*slot, id| { const pane = slot.* orelse continue; - if (pane.tag_name_len != null and (id != p.active or p.topbar_col != null)) + if (pane.tag_edit and pane.prompt == .none and (id != p.active or p.topbar_col != null)) exitTagEdit(pane); if (comptime terminal_panes) if (pane.terminal) |state| { var off: u16 = 0; diff --git a/src/tag_line.zig b/src/tag_line.zig index d1a9d166..d3bdf59c 100644 --- a/src/tag_line.zig +++ b/src/tag_line.zig @@ -5,6 +5,7 @@ pub const Line = struct { text: [@import("memory.zig").limits.max_tag_tail]u8 = undefined, len: usize = 0, custom: bool = false, + saved_col: ?u16 = null, pub fn slice(self: *const Line, fallback: []const u8) []const u8 { return if (self.custom) self.text[0..self.len] else fallback; |
