parent
2fd48b433d
commit
10f19ebdc3
|
|
@ -825,6 +825,8 @@ pub const PageFormatter = struct {
|
|||
/// byte written to the writer offset by the byte index. It is the
|
||||
/// caller's responsibility to free the map.
|
||||
///
|
||||
/// The x/y coordinate will be the coordinates within the page.
|
||||
///
|
||||
/// Warning: there is a significant performance hit to track this
|
||||
point_map: ?struct {
|
||||
alloc: Allocator,
|
||||
|
|
@ -1450,6 +1452,76 @@ test "Page plain single line" {
|
|||
);
|
||||
}
|
||||
|
||||
test "Page plain single line soft-wrapped unwrapped" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var builder: std.Io.Writer.Allocating = .init(alloc);
|
||||
defer builder.deinit();
|
||||
|
||||
var t = try Terminal.init(alloc, .{
|
||||
.cols = 3,
|
||||
.rows = 5,
|
||||
});
|
||||
defer t.deinit(alloc);
|
||||
|
||||
var s = t.vtStream();
|
||||
defer s.deinit();
|
||||
|
||||
try s.nextSlice("hello!");
|
||||
|
||||
// Verify we have only a single page
|
||||
const pages = &t.screens.active.pages;
|
||||
try testing.expect(pages.pages.first != null);
|
||||
try testing.expect(pages.pages.first == pages.pages.last);
|
||||
|
||||
// Create the formatter
|
||||
const page = &pages.pages.last.?.data;
|
||||
var formatter: PageFormatter = .init(page, .{
|
||||
.emit = .plain,
|
||||
.unwrap = true,
|
||||
});
|
||||
|
||||
// Test our point map.
|
||||
var point_map: std.ArrayList(Coordinate) = .empty;
|
||||
defer point_map.deinit(alloc);
|
||||
formatter.point_map = .{ .alloc = alloc, .map = &point_map };
|
||||
|
||||
// Verify output
|
||||
// Note: we don't test the trailing state, which may have bugs
|
||||
// with unwrap...
|
||||
_ = try formatter.formatWithState(&builder.writer);
|
||||
const output = builder.writer.buffered();
|
||||
try testing.expectEqualStrings("hello!", output);
|
||||
|
||||
// Verify our point map
|
||||
try testing.expectEqual(output.len, point_map.items.len);
|
||||
try testing.expectEqual(
|
||||
Coordinate{ .x = 0, .y = 0 },
|
||||
point_map.items[0],
|
||||
);
|
||||
try testing.expectEqual(
|
||||
Coordinate{ .x = 1, .y = 0 },
|
||||
point_map.items[1],
|
||||
);
|
||||
try testing.expectEqual(
|
||||
Coordinate{ .x = 2, .y = 0 },
|
||||
point_map.items[2],
|
||||
);
|
||||
try testing.expectEqual(
|
||||
Coordinate{ .x = 0, .y = 1 },
|
||||
point_map.items[3],
|
||||
);
|
||||
try testing.expectEqual(
|
||||
Coordinate{ .x = 1, .y = 1 },
|
||||
point_map.items[4],
|
||||
);
|
||||
try testing.expectEqual(
|
||||
Coordinate{ .x = 2, .y = 1 },
|
||||
point_map.items[5],
|
||||
);
|
||||
}
|
||||
|
||||
test "Page plain single wide char" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
|
|
|||
|
|
@ -703,8 +703,16 @@ pub const RenderState = struct {
|
|||
.{
|
||||
.tag = tag,
|
||||
.range = .{
|
||||
if (i == 0) hl.top_x else 0,
|
||||
if (i == nodes.len - 1) hl.bot_x else self.cols - 1,
|
||||
if (i == 0 and
|
||||
row_pin.y == starts[0])
|
||||
hl.top_x
|
||||
else
|
||||
0,
|
||||
if (i == nodes.len - 1 and
|
||||
row_pin.y == ends[nodes.len - 1] - 1)
|
||||
hl.bot_x
|
||||
else
|
||||
self.cols - 1,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ const PageList = terminal.PageList;
|
|||
const Pin = PageList.Pin;
|
||||
const Selection = terminal.Selection;
|
||||
const Screen = terminal.Screen;
|
||||
const Terminal = terminal.Terminal;
|
||||
const PageFormatter = @import("../formatter.zig").PageFormatter;
|
||||
const FlattenedHighlight = terminal.highlight.Flattened;
|
||||
|
||||
|
|
@ -462,12 +463,13 @@ pub const SlidingWindow = struct {
|
|||
switch (self.direction) {
|
||||
.forward => {},
|
||||
.reverse => {
|
||||
const slice = self.chunk_buf.slice();
|
||||
const nodes = slice.items(.node);
|
||||
const starts = slice.items(.start);
|
||||
const ends = slice.items(.end);
|
||||
|
||||
if (self.chunk_buf.len > 1) {
|
||||
// Reverse all our chunks. This should be pretty obvious why.
|
||||
const slice = self.chunk_buf.slice();
|
||||
const nodes = slice.items(.node);
|
||||
const starts = slice.items(.start);
|
||||
const ends = slice.items(.end);
|
||||
std.mem.reverse(*PageList.List.Node, nodes);
|
||||
std.mem.reverse(size.CellCountInt, starts);
|
||||
std.mem.reverse(size.CellCountInt, ends);
|
||||
|
|
@ -484,10 +486,6 @@ pub const SlidingWindow = struct {
|
|||
// We DON'T need to do this for any middle pages because
|
||||
// they always use the full page.
|
||||
//
|
||||
// We DON'T need to do this for chunks.len == 1 because
|
||||
// the pages themselves aren't reversed and we don't have
|
||||
// any prefix/suffix problems.
|
||||
//
|
||||
// This is a fixup that makes our start/end match the
|
||||
// same logic as the loops above if they were in forward
|
||||
// order.
|
||||
|
|
@ -496,6 +494,13 @@ pub const SlidingWindow = struct {
|
|||
ends[0] = nodes[0].data.size.rows;
|
||||
ends[nodes.len - 1] = starts[nodes.len - 1] + 1;
|
||||
starts[nodes.len - 1] = 0;
|
||||
} else {
|
||||
// For a single chunk, the y values are in reverse order
|
||||
// (start is the screen-end, end is the screen-start).
|
||||
// Swap them to get proper top-to-bottom order.
|
||||
const start_y = starts[0];
|
||||
starts[0] = ends[0] - 1;
|
||||
ends[0] = start_y + 1;
|
||||
}
|
||||
|
||||
// X values also need to be reversed since the top/bottom
|
||||
|
|
@ -539,7 +544,10 @@ pub const SlidingWindow = struct {
|
|||
|
||||
// Encode the page into the buffer.
|
||||
const formatter: PageFormatter = formatter: {
|
||||
var formatter: PageFormatter = .init(&meta.node.data, .plain);
|
||||
var formatter: PageFormatter = .init(&meta.node.data, .{
|
||||
.emit = .plain,
|
||||
.unwrap = true,
|
||||
});
|
||||
formatter.point_map = .{
|
||||
.alloc = self.alloc,
|
||||
.map = &meta.cell_map,
|
||||
|
|
@ -1555,3 +1563,77 @@ test "SlidingWindow single append match on boundary reversed" {
|
|||
}
|
||||
try testing.expect(w.next() == null);
|
||||
}
|
||||
|
||||
test "SlidingWindow single append soft wrapped" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var w: SlidingWindow = try .init(alloc, .forward, "boo!");
|
||||
defer w.deinit();
|
||||
|
||||
var t: Terminal = try .init(alloc, .{ .cols = 4, .rows = 5 });
|
||||
defer t.deinit(alloc);
|
||||
|
||||
var s = t.vtStream();
|
||||
defer s.deinit();
|
||||
try s.nextSlice("A\r\nxxboo!\r\nC");
|
||||
|
||||
// We want to test single-page cases.
|
||||
const screen = t.screens.active;
|
||||
try testing.expect(screen.pages.pages.first == screen.pages.pages.last);
|
||||
const node: *PageList.List.Node = screen.pages.pages.first.?;
|
||||
_ = try w.append(node);
|
||||
|
||||
// We should be able to find two matches.
|
||||
{
|
||||
const h = w.next().?;
|
||||
const sel = h.untracked();
|
||||
try testing.expectEqual(point.Point{ .active = .{
|
||||
.x = 2,
|
||||
.y = 1,
|
||||
} }, screen.pages.pointFromPin(.active, sel.start));
|
||||
try testing.expectEqual(point.Point{ .active = .{
|
||||
.x = 1,
|
||||
.y = 2,
|
||||
} }, screen.pages.pointFromPin(.active, sel.end));
|
||||
}
|
||||
try testing.expect(w.next() == null);
|
||||
try testing.expect(w.next() == null);
|
||||
}
|
||||
|
||||
test "SlidingWindow single append reversed soft wrapped" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var w: SlidingWindow = try .init(alloc, .reverse, "boo!");
|
||||
defer w.deinit();
|
||||
|
||||
var t: Terminal = try .init(alloc, .{ .cols = 4, .rows = 5 });
|
||||
defer t.deinit(alloc);
|
||||
|
||||
var s = t.vtStream();
|
||||
defer s.deinit();
|
||||
try s.nextSlice("A\r\nxxboo!\r\nC");
|
||||
|
||||
// We want to test single-page cases.
|
||||
const screen = t.screens.active;
|
||||
try testing.expect(screen.pages.pages.first == screen.pages.pages.last);
|
||||
const node: *PageList.List.Node = screen.pages.pages.first.?;
|
||||
_ = try w.append(node);
|
||||
|
||||
// We should be able to find two matches.
|
||||
{
|
||||
const h = w.next().?;
|
||||
const sel = h.untracked();
|
||||
try testing.expectEqual(point.Point{ .active = .{
|
||||
.x = 2,
|
||||
.y = 1,
|
||||
} }, screen.pages.pointFromPin(.active, sel.start));
|
||||
try testing.expectEqual(point.Point{ .active = .{
|
||||
.x = 1,
|
||||
.y = 2,
|
||||
} }, screen.pages.pointFromPin(.active, sel.end));
|
||||
}
|
||||
try testing.expect(w.next() == null);
|
||||
try testing.expect(w.next() == null);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue