terminal: index hyperlink tests

This commit is contained in:
Mitchell Hashimoto
2024-07-04 10:23:21 -07:00
parent 96ff17a9b4
commit bac1307c4b
2 changed files with 165 additions and 6 deletions

View File

@ -5809,6 +5809,51 @@ test "Terminal: index from the bottom" {
}
}
test "Terminal: index scrolling with hyperlink" {
const alloc = testing.allocator;
var t = try init(alloc, .{ .cols = 2, .rows = 5 });
defer t.deinit(alloc);
t.setCursorPos(5, 1);
try t.screen.startHyperlink("http://example.com", null);
try t.print('A');
t.screen.endHyperlink();
t.cursorLeft(1); // undo moving right from 'A'
try t.index();
try t.print('B');
{
const str = try t.plainString(testing.allocator);
defer testing.allocator.free(str);
try testing.expectEqualStrings("\n\n\nA\nB", str);
}
{
const list_cell = t.screen.pages.getCell(.{ .viewport = .{
.x = 0,
.y = 3,
} }).?;
const row = list_cell.row;
try testing.expect(row.hyperlink);
const cell = list_cell.cell;
try testing.expect(cell.hyperlink);
const id = list_cell.page.data.lookupHyperlink(cell).?;
try testing.expectEqual(@as(hyperlink.Id, 1), id);
}
{
const list_cell = t.screen.pages.getCell(.{ .viewport = .{
.x = 0,
.y = 4,
} }).?;
const row = list_cell.row;
try testing.expect(!row.hyperlink);
const cell = list_cell.cell;
try testing.expect(!cell.hyperlink);
const id = list_cell.page.data.lookupHyperlink(cell);
try testing.expect(id == null);
}
}
test "Terminal: index outside of scrolling region" {
const alloc = testing.allocator;
var t = try init(alloc, .{ .cols = 2, .rows = 5 });
@ -5935,6 +5980,92 @@ test "Terminal: index inside scroll region" {
}
}
test "Terminal: index bottom of scroll region with hyperlinks" {
const alloc = testing.allocator;
var t = try init(alloc, .{ .rows = 5, .cols = 5 });
defer t.deinit(alloc);
t.setTopAndBottomMargin(1, 2);
try t.print('A');
try t.index();
t.carriageReturn();
try t.screen.startHyperlink("http://example.com", null);
try t.print('B');
t.screen.endHyperlink();
try t.index();
t.carriageReturn();
try t.print('C');
{
const str = try t.plainString(testing.allocator);
defer testing.allocator.free(str);
try testing.expectEqualStrings("B\nC", str);
}
{
const list_cell = t.screen.pages.getCell(.{ .viewport = .{
.x = 0,
.y = 0,
} }).?;
const row = list_cell.row;
try testing.expect(row.hyperlink);
const cell = list_cell.cell;
try testing.expect(cell.hyperlink);
const id = list_cell.page.data.lookupHyperlink(cell).?;
try testing.expectEqual(@as(hyperlink.Id, 1), id);
}
{
const list_cell = t.screen.pages.getCell(.{ .viewport = .{
.x = 0,
.y = 1,
} }).?;
const row = list_cell.row;
try testing.expect(!row.hyperlink);
const cell = list_cell.cell;
try testing.expect(!cell.hyperlink);
const id = list_cell.page.data.lookupHyperlink(cell);
try testing.expect(id == null);
}
}
test "Terminal: index bottom of scroll region clear hyperlinks" {
const alloc = testing.allocator;
var t = try init(alloc, .{ .rows = 5, .cols = 5 });
defer t.deinit(alloc);
t.setTopAndBottomMargin(1, 2);
try t.screen.startHyperlink("http://example.com", null);
try t.print('A');
t.screen.endHyperlink();
try t.index();
t.carriageReturn();
try t.print('B');
try t.index();
t.carriageReturn();
try t.print('C');
{
const str = try t.plainString(testing.allocator);
defer testing.allocator.free(str);
try testing.expectEqualStrings("B\nC", str);
}
for (0..2) |y| {
const list_cell = t.screen.pages.getCell(.{ .viewport = .{
.x = 0,
.y = @intCast(y),
} }).?;
const row = list_cell.row;
try testing.expect(!row.hyperlink);
const cell = list_cell.cell;
try testing.expect(!cell.hyperlink);
const id = list_cell.page.data.lookupHyperlink(cell);
try testing.expect(id == null);
const page = &list_cell.page.data;
try testing.expectEqual(0, page.hyperlink_set.count());
}
}
test "Terminal: index bottom of scroll region with background SGR" {
const alloc = testing.allocator;
var t = try init(alloc, .{ .rows = 5, .cols = 5 });

View File

@ -615,9 +615,7 @@ pub const Page = struct {
// If our destination has styles or graphemes then we need to
// clear some state.
if (dst_row.grapheme or dst_row.styled) {
self.clearCells(dst_row, x_start, x_end);
}
if (dst_row.managedMemory()) self.clearCells(dst_row, x_start, x_end);
// Copy all the row metadata but keep our cells offset
dst_row.* = copy: {
@ -640,7 +638,7 @@ pub const Page = struct {
// If we have no managed memory in the source, then we can just
// copy it directly.
if (!src_row.grapheme and !src_row.styled) {
if (!src_row.managedMemory()) {
fastmem.copy(Cell, cells, other_cells);
} else {
// We have managed memory, so we have to do a slower copy to
@ -655,6 +653,26 @@ pub const Page = struct {
const cps = other.lookupGrapheme(src_cell).?;
for (cps) |cp| try self.appendGrapheme(dst_row, dst_cell, cp);
}
if (src_cell.hyperlink) hyperlink: {
dst_row.hyperlink = true;
// Fast-path: same page we can move it directly
if (other == self) {
self.moveHyperlink(src_cell, dst_cell);
break :hyperlink;
}
// Slow-path: get the hyperlink from the other page,
// add it, and migrate.
const id = other.lookupHyperlink(src_cell).?;
const other_link = other.hyperlink_set.get(other.memory, id);
const dst_id = try self.hyperlink_set.addContext(
self.memory,
other_link.*,
.{ .page = self },
);
try self.setHyperlink(dst_row, dst_cell, dst_id);
}
if (src_cell.style_id != style.default_id) {
dst_row.styled = true;
@ -668,8 +686,12 @@ pub const Page = struct {
// Slow path: Get the style from the other
// page and add it to this page's style set.
const other_style = other.styles.get(other.memory, src_cell.style_id).*;
if (try self.styles.addWithId(self.memory, other_style, src_cell.style_id)) |id| {
const other_style = other.styles.get(other.memory, src_cell.style_id);
if (try self.styles.addWithId(
self.memory,
other_style.*,
src_cell.style_id,
)) |id| {
dst_cell.style_id = id;
}
}
@ -1365,6 +1387,12 @@ pub const Row = packed struct(u64) {
return self == .prompt or self == .prompt_continuation or self == .input;
}
};
/// Returns true if this row has any managed memory outside of the
/// row structure (graphemes, styles, etc.)
fn managedMemory(self: Row) bool {
return self.grapheme or self.styled or self.hyperlink;
}
};
/// A cell represents a single terminal grid cell.