commit
7f950cc892
|
|
@ -825,6 +825,8 @@ pub const PageFormatter = struct {
|
||||||
/// byte written to the writer offset by the byte index. It is the
|
/// byte written to the writer offset by the byte index. It is the
|
||||||
/// caller's responsibility to free the map.
|
/// 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
|
/// Warning: there is a significant performance hit to track this
|
||||||
point_map: ?struct {
|
point_map: ?struct {
|
||||||
alloc: Allocator,
|
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" {
|
test "Page plain single wide char" {
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
const alloc = testing.allocator;
|
const alloc = testing.allocator;
|
||||||
|
|
|
||||||
|
|
@ -703,8 +703,16 @@ pub const RenderState = struct {
|
||||||
.{
|
.{
|
||||||
.tag = tag,
|
.tag = tag,
|
||||||
.range = .{
|
.range = .{
|
||||||
if (i == 0) hl.top_x else 0,
|
if (i == 0 and
|
||||||
if (i == nodes.len - 1) hl.bot_x else self.cols - 1,
|
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 Pin = PageList.Pin;
|
||||||
const Selection = terminal.Selection;
|
const Selection = terminal.Selection;
|
||||||
const Screen = terminal.Screen;
|
const Screen = terminal.Screen;
|
||||||
|
const Terminal = terminal.Terminal;
|
||||||
const PageFormatter = @import("../formatter.zig").PageFormatter;
|
const PageFormatter = @import("../formatter.zig").PageFormatter;
|
||||||
const FlattenedHighlight = terminal.highlight.Flattened;
|
const FlattenedHighlight = terminal.highlight.Flattened;
|
||||||
|
|
||||||
|
|
@ -462,12 +463,13 @@ pub const SlidingWindow = struct {
|
||||||
switch (self.direction) {
|
switch (self.direction) {
|
||||||
.forward => {},
|
.forward => {},
|
||||||
.reverse => {
|
.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) {
|
if (self.chunk_buf.len > 1) {
|
||||||
// Reverse all our chunks. This should be pretty obvious why.
|
// 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(*PageList.List.Node, nodes);
|
||||||
std.mem.reverse(size.CellCountInt, starts);
|
std.mem.reverse(size.CellCountInt, starts);
|
||||||
std.mem.reverse(size.CellCountInt, ends);
|
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
|
// We DON'T need to do this for any middle pages because
|
||||||
// they always use the full page.
|
// 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
|
// This is a fixup that makes our start/end match the
|
||||||
// same logic as the loops above if they were in forward
|
// same logic as the loops above if they were in forward
|
||||||
// order.
|
// order.
|
||||||
|
|
@ -496,6 +494,13 @@ pub const SlidingWindow = struct {
|
||||||
ends[0] = nodes[0].data.size.rows;
|
ends[0] = nodes[0].data.size.rows;
|
||||||
ends[nodes.len - 1] = starts[nodes.len - 1] + 1;
|
ends[nodes.len - 1] = starts[nodes.len - 1] + 1;
|
||||||
starts[nodes.len - 1] = 0;
|
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
|
// 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.
|
// Encode the page into the buffer.
|
||||||
const formatter: PageFormatter = formatter: {
|
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 = .{
|
formatter.point_map = .{
|
||||||
.alloc = self.alloc,
|
.alloc = self.alloc,
|
||||||
.map = &meta.cell_map,
|
.map = &meta.cell_map,
|
||||||
|
|
@ -1555,3 +1563,77 @@ test "SlidingWindow single append match on boundary reversed" {
|
||||||
}
|
}
|
||||||
try testing.expect(w.next() == null);
|
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