diff options
Diffstat (limited to 'src/pardes.zig')
| -rw-r--r-- | src/pardes.zig | 60 |
1 files changed, 37 insertions, 23 deletions
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. |
