diff options
Diffstat (limited to 'src/look.zig')
| -rw-r--r-- | src/look.zig | 32 |
1 files changed, 32 insertions, 0 deletions
diff --git a/src/look.zig b/src/look.zig index 6cf01dd2..537a77fa 100644 --- a/src/look.zig +++ b/src/look.zig @@ -10,6 +10,7 @@ const platform_has_fs = fs.platform_has_fs; const layout = @import("layout.zig"); const animation = @import("animation.zig"); const modal = @import("modal.zig"); +const tagline = @import("tagline.zig"); const filesystem = @import("fs.zig"); const panes = @import("panes.zig"); const mouse = @import("mouse.zig"); @@ -466,6 +467,7 @@ pub fn expandedWord(line: []const u8, col: usize) ?ExpandedWord { pub fn expandedSel(p: *Pardes, pane: *Pane, at: Pane.Sel) ?Pane.Sel { var sl = at; if (sl.c0 != sl.c1 or sl.r0 != sl.r1) return sl; + if (sl.r0 >= 0 and sl.r0 < pane.tag_rows) return tagWordSel(p, pane, sl); const text = edit.paneText(p, pane) catch return null; var it = std.mem.splitAny(u8, text, "\n"); var v: i32 = 0; @@ -481,6 +483,34 @@ pub fn expandedSel(p: *Pardes, pane: *Pane, at: Pane.Sel) ?Pane.Sel { return null; } +/// A tag word is expanded in the tag's own text, so a word the wrapping +/// broke across rows is still one word, as acme's frame wraps a tag +/// without breaking its text. +fn tagWordSel(p: *Pardes, pane: *Pane, at: Pane.Sel) ?Pane.Sel { + const arena = p.scratch.allocator(); + const shown = tagline.shownTag(p, arena, pane) catch return null; + const whole = tagline.tagText(p, arena, pane) catch return null; + const row: usize = @as(usize, @intCast(at.r0)) + pane.tag_top; + const shown_line = modal.lineSlice(shown.text, row); + const real = shown.toReal(modal.lineStartOffset(shown.text, row) + panes.File.rawAtDisplay(shown_line, @intCast(@max(0, at.c0)))); + const start = if (std.mem.lastIndexOfScalar(u8, whole[0..@min(real, whole.len)], '\n')) |nl| nl + 1 else 0; + const end = std.mem.indexOfScalarPos(u8, whole, start, '\n') orelse whole.len; + const b = expandedWord(whole[start..end], real - start) orelse return null; + const lo = shownCell(shown.text, shown.fromReal(start + b.lo), pane.tag_top); + const hi = shownCell(shown.text, shown.fromReal(start + b.hi - 1), pane.tag_top); + return .{ .state = at.state, .r0 = lo.row, .c0 = lo.col, .r1 = hi.row, .c1 = hi.col }; +} + +fn shownCell(text: []const u8, off: usize, top: usize) struct { row: i32, col: i32 } { + const before = text[0..@min(off, text.len)]; + const row = std.mem.count(u8, before, "\n"); + const line_start = if (std.mem.lastIndexOfScalar(u8, before, '\n')) |nl| nl + 1 else 0; + return .{ + .row = @as(i32, @intCast(row)) - @as(i32, @intCast(top)), + .col = @intCast(panes.File.rawDisplayCol(text[line_start..], off - line_start)), + }; +} + /// the word under the modal cursor as a pane-local selection (paneText /// coords: row 0 is the tag; file panes carry the line-number prefix) pub fn cursorWordSel(p: *Pardes, pane: *Pane) Pane.Sel { @@ -1347,6 +1377,8 @@ test "Look hover waits without mutating the pane and input cancels it" { defer p.deinit(); while (p.nextEffect()) |_| {} const pane = try p.setTestFile("alpha beta gamma\n"); + pane.tag_expanded = false; // a one-row tag keeps this small screen's rows + p.sync(); while (p.nextEffect()) |_| {} const other_id = p.freeSlot() orelse return error.NoSparePaneForHoverTest; _ = try p.newShell(other_id, ""); |
