summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/panes.zig7
-rw-r--r--src/pardes.zig60
2 files changed, 44 insertions, 23 deletions
diff --git a/src/panes.zig b/src/panes.zig
index 8d4330e9..0b565676 100644
--- a/src/panes.zig
+++ b/src/panes.zig
@@ -344,6 +344,13 @@ pub const Pane = struct {
};
}
+ /// What an open prompt shows on its notice band: the one answer every
+ /// painter of the band reads.
+ pub fn promptText(p: *const Pane) ?[]const u8 {
+ const at = p.promptAt() orelse return null;
+ return p.tagSlice()[@min(at, p.tag_tail_len)..];
+ }
+
pub fn appendTag(p: *Pane, text: []const u8) bool {
if (text.len > p.tag_tail.len - p.tag_tail_len) return false;
@memcpy(p.tag_tail[p.tag_tail_len..][0..text.len], text);
diff --git a/src/pardes.zig b/src/pardes.zig
index 4786bd70..29f937d7 100644
--- a/src/pardes.zig
+++ b/src/pardes.zig
@@ -7356,6 +7356,17 @@ pub const Pardes = struct {
};
}
+ /// What notice band `i` says. The grid pass and the band layers both
+ /// read it here, so a terminal and a pixel shell show the same line.
+ fn noticeText(p: *const Pardes, pane: *const Pane, i: usize, leader_buf: *[16]u8) []const u8 {
+ return switch (pane.notices.kinds[i]) {
+ .message => pane.msg[0..pane.msg_len],
+ .older => pane.msg_older[pane.notices.index[i]].slice(),
+ .leader => p.leaderText(leader_buf),
+ .prompt => pane.promptText() orelse "",
+ };
+ }
+
/// The life of the message line notice `i` shows, if it shows one.
fn noticeLife(pane: *const Pane, i: usize) ?Pane.MessageLife {
return switch (pane.notices.kinds[i]) {
@@ -9943,8 +9954,7 @@ pub const Pardes = struct {
fn submitSearch(p: *Pardes, id: usize) void {
const pane = p.panes[id] orelse return;
if (selRegexArmed(pane)) |a| return p.applySelRegex(pane, a.pat, a.split);
- const tail = pane.tagSlice();
- const armed = tail[@min(pane.promptAt() orelse return, tail.len)..];
+ const armed = pane.promptText() orelse return;
const slash = std.mem.indexOfScalar(u8, armed, '/') orelse return;
if (std.mem.startsWith(u8, armed, config.rename_marker)) return p.lspRequest(id, .rename, armed[slash + 1 ..]);
if (std.mem.startsWith(u8, armed, config.symbol_marker)) return p.lspRequest(id, .workspace_symbols, armed[slash + 1 ..]);
@@ -13997,15 +14007,7 @@ pub const Pardes = struct {
// draws, and a band it cannot see is a message it never gets.
// The GUI skips grid cells a tag layer covers, so nothing is
// drawn twice there.
- const text = switch (kind) {
- .message => pane.msg[0..pane.msg_len],
- .older => pane.msg_older[pane.notices.index[i]].slice(),
- .leader => p.leaderText(&leader_buf),
- .prompt => blk: {
- const at = pane.promptAt() orelse continue;
- break :blk pane.tagSlice()[@min(at, pane.tag_tail_len)..];
- },
- };
+ const text = p.noticeText(pane, i, &leader_buf);
const chip = p.noticeCols(text, tw);
if (chip == 0) continue;
const cx = tx + tw - chip;
@@ -14525,6 +14527,8 @@ pub const Pardes = struct {
// just by wearing a tagline font. The geometry is the one the grid
// pass already chose and recorded, so the two cannot disagree and the
// GUI's "skip the cells a tag layer covers" leaves no stub behind.
+ // A prompt is one of them too: left on the grid, a pixel shell drew
+ // its tagline glyphs one to a BODY cell, spaced out like a banner.
for (p.panes, 0..) |slot, id| {
const pane = slot orelse continue;
const r = p.rects[id];
@@ -14532,14 +14536,7 @@ pub const Pardes = struct {
const first = p.bodyTop(r);
var leader_buf: [16]u8 = undefined;
for (pane.notices.slice(), 0..) |kind, i| {
- const text = switch (kind) {
- .message => pane.msg[0..pane.msg_len],
- .older => pane.msg_older[pane.notices.index[i]].slice(),
- .leader => p.leaderText(&leader_buf),
- // A prompt is an editing line with a cursor in it, so it
- // stays on the grid where the cursor can sit on a cell.
- .prompt => continue,
- };
+ const text = p.noticeText(pane, i, &leader_buf);
if (text.len == 0) continue;
const cx = pane.notices.left[i];
if (cx == std.math.maxInt(u16) or cx >= r.x + r.w) continue;
@@ -14572,6 +14569,16 @@ pub const Pardes = struct {
.h = BOX_H,
}, line, blendRgb(chrome.tag_bg, page, fade), blendRgb(chrome.tag_fg, page, fade));
p.surface.tag_layers[layer_index].slide = motion.slide;
+ // The caret rides the band, where the prompt's glyphs are,
+ // at the same place in the text the grid pass puts it.
+ if (kind != .prompt or id != p.active) continue;
+ const at = pane.promptAt() orelse continue;
+ const prompt0 = (p.tagPrefix(pane) catch continue).len + at;
+ if (pane.tag_col < prompt0) continue;
+ const typed = panes.File.displayWidth(text[0..@min(pane.tag_col - prompt0, text.len)]);
+ const dropped = shown - panes.File.displayWidth(kept);
+ if (typed < dropped) continue;
+ p.surface.tag_layers[layer_index].cursor = .{ .col = @intCast(pad + typed - dropped), .bar = pane.mode == .insert };
}
}
if (p.columnBarHeight() > 0) for (0..p.ncol) |column| {
@@ -14652,9 +14659,7 @@ pub const Pardes = struct {
for (pane.msg_older[0..pane.msg_older_len], 0..) |line, i| pane.notices.pushAt(.older, @intCast(i), line.slot);
if (pane.msg_len > 0) pane.notices.pushAt(.message, 0, pane.msg_slot);
if (id == p.active and p.leader_on) pane.notices.push(.leader);
- if (pane.promptAt()) |at| {
- if (pane.tagSlice()[@min(at, pane.tag_tail_len)..].len > 0) pane.notices.push(.prompt);
- }
+ if (pane.promptText()) |text| if (text.len > 0) pane.notices.push(.prompt);
// A pane only has so many rows to give. When they do not all fit the
// LAST ones survive, because the prompt is last and it is the one
// carrying the cursor: a prompt you cannot see is a prompt you type
@@ -15441,7 +15446,7 @@ test "notices stack over the body's top rows without moving them" {
try std.testing.expect(layer.cols == 0 or layer.kind != .notice);
}
-test "a prompt chip keeps its caret on the grid, and only the chip swallows a click" {
+test "a prompt chip is a band with its caret, still on the grid, and only the chip swallows a click" {
if (platform == .web) return;
const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true, .cols = 80, .rows = 24 });
defer p.deinit();
@@ -15464,6 +15469,15 @@ test "a prompt chip keeps its caret on the grid, and only the chip swallows a cl
try std.testing.expectEqual(row, cursor.y);
try std.testing.expect(cursor.x < r.x + r.w);
try std.testing.expect(cursor.x < p.surface.cols);
+ // A pixel shell draws the prompt from its band, in the tagline's pitch,
+ // with the caret right after the marker's last glyph.
+ const band = for (p.surface.tagLayers()) |*layer| {
+ if (layer.cols != 0 and layer.kind == .notice) break layer;
+ } else return error.NoPromptBand;
+ try std.testing.expectEqual(row, band.viewport.y);
+ const caret = band.cursor orelse return error.MissingBandCaret;
+ try std.testing.expect(caret.bar);
+ try std.testing.expectEqualStrings("/", band.cells[caret.col - 1].grapheme());
// The chip covers only its own columns. Left of it is ordinary body, and
// a sweep there still selects.