renderer: convert link to new state

This commit is contained in:
Mitchell Hashimoto
2024-03-15 20:58:59 -07:00
parent 5664c3e3c9
commit 7419794a7b

View File

@ -64,11 +64,13 @@ pub const Set = struct {
self: *const Set,
alloc: Allocator,
screen: *Screen,
mouse_vp_pt: point.Viewport,
mouse_vp_pt: point.Coordinate,
mouse_mods: inputpkg.Mods,
) !MatchSet {
// Convert the viewport point to a screen point.
const mouse_pt = mouse_vp_pt.toScreen(screen);
const mouse_pin = screen.pages.pin(.{
.viewport = mouse_vp_pt,
}) orelse return .{};
// This contains our list of matches. The matches are stored
// as selections which contain the start and end points of
@ -78,14 +80,25 @@ pub const Set = struct {
defer matches.deinit();
// Iterate over all the visible lines.
var lineIter = screen.lineIterator(.viewport);
while (lineIter.next()) |line| {
const strmap = line.stringMap(alloc) catch |err| {
log.warn(
"failed to build string map for link checking err={}",
.{err},
);
continue;
var lineIter = screen.lineIterator(screen.pages.pin(.{
.viewport = .{},
}) orelse return .{});
while (lineIter.next()) |line_sel| {
const strmap: terminal.StringMap = strmap: {
var strmap: terminal.StringMap = undefined;
const str = screen.selectionString(alloc, .{
.sel = line_sel,
.trim = false,
.map = &strmap,
}) catch |err| {
log.warn(
"failed to build string map for link checking err={}",
.{err},
);
continue;
};
alloc.free(str);
break :strmap strmap;
};
defer strmap.deinit(alloc);
@ -98,7 +111,7 @@ pub const Set = struct {
.always => {},
.always_mods => |v| if (!mouse_mods.equal(v)) continue,
inline .hover, .hover_mods => |v, tag| {
if (!line.selection().contains(mouse_pt)) continue;
if (!line_sel.contains(screen, mouse_pin)) continue;
if (comptime tag == .hover_mods) {
if (!mouse_mods.equal(v)) continue;
}
@ -121,7 +134,7 @@ pub const Set = struct {
.always, .always_mods => {},
.hover,
.hover_mods,
=> if (!sel.contains(mouse_pt)) continue,
=> if (!sel.contains(screen, mouse_pin)) continue,
}
try matches.append(sel);
@ -153,156 +166,228 @@ pub const MatchSet = struct {
/// results.
pub fn orderedContains(
self: *MatchSet,
pt: point.ScreenPoint,
screen: *const Screen,
pin: terminal.Pin,
) bool {
// If we're beyond the end of our possible matches, we're done.
if (self.i >= self.matches.len) return false;
// If our selection ends before the point, then no point will ever
// again match this selection so we move on to the next one.
while (self.matches[self.i].end.before(pt)) {
while (self.matches[self.i].end().before(pin)) {
self.i += 1;
if (self.i >= self.matches.len) return false;
}
return self.matches[self.i].contains(pt);
return self.matches[self.i].contains(screen, pin);
}
};
// TODO(paged-terminal)
// test "matchset" {
// const testing = std.testing;
// const alloc = testing.allocator;
//
// // Initialize our screen
// var s = try Screen.init(alloc, 5, 5, 0);
// defer s.deinit();
// const str = "1ABCD2EFGH\n3IJKL";
// try s.testWriteString(str);
//
// // Get a set
// var set = try Set.fromConfig(alloc, &.{
// .{
// .regex = "AB",
// .action = .{ .open = {} },
// .highlight = .{ .always = {} },
// },
//
// .{
// .regex = "EF",
// .action = .{ .open = {} },
// .highlight = .{ .always = {} },
// },
// });
// defer set.deinit(alloc);
//
// // Get our matches
// var match = try set.matchSet(alloc, &s, .{}, .{});
// defer match.deinit(alloc);
// try testing.expectEqual(@as(usize, 2), match.matches.len);
//
// // Test our matches
// try testing.expect(!match.orderedContains(.{ .x = 0, .y = 0 }));
// try testing.expect(match.orderedContains(.{ .x = 1, .y = 0 }));
// try testing.expect(match.orderedContains(.{ .x = 2, .y = 0 }));
// try testing.expect(!match.orderedContains(.{ .x = 3, .y = 0 }));
// try testing.expect(match.orderedContains(.{ .x = 1, .y = 1 }));
// try testing.expect(!match.orderedContains(.{ .x = 1, .y = 2 }));
// }
//
// test "matchset hover links" {
// const testing = std.testing;
// const alloc = testing.allocator;
//
// // Initialize our screen
// var s = try Screen.init(alloc, 5, 5, 0);
// defer s.deinit();
// const str = "1ABCD2EFGH\n3IJKL";
// try s.testWriteString(str);
//
// // Get a set
// var set = try Set.fromConfig(alloc, &.{
// .{
// .regex = "AB",
// .action = .{ .open = {} },
// .highlight = .{ .hover = {} },
// },
//
// .{
// .regex = "EF",
// .action = .{ .open = {} },
// .highlight = .{ .always = {} },
// },
// });
// defer set.deinit(alloc);
//
// // Not hovering over the first link
// {
// var match = try set.matchSet(alloc, &s, .{}, .{});
// defer match.deinit(alloc);
// try testing.expectEqual(@as(usize, 1), match.matches.len);
//
// // Test our matches
// try testing.expect(!match.orderedContains(.{ .x = 0, .y = 0 }));
// try testing.expect(!match.orderedContains(.{ .x = 1, .y = 0 }));
// try testing.expect(!match.orderedContains(.{ .x = 2, .y = 0 }));
// try testing.expect(!match.orderedContains(.{ .x = 3, .y = 0 }));
// try testing.expect(match.orderedContains(.{ .x = 1, .y = 1 }));
// try testing.expect(!match.orderedContains(.{ .x = 1, .y = 2 }));
// }
//
// // Hovering over the first link
// {
// var match = try set.matchSet(alloc, &s, .{ .x = 1, .y = 0 }, .{});
// defer match.deinit(alloc);
// try testing.expectEqual(@as(usize, 2), match.matches.len);
//
// // Test our matches
// try testing.expect(!match.orderedContains(.{ .x = 0, .y = 0 }));
// try testing.expect(match.orderedContains(.{ .x = 1, .y = 0 }));
// try testing.expect(match.orderedContains(.{ .x = 2, .y = 0 }));
// try testing.expect(!match.orderedContains(.{ .x = 3, .y = 0 }));
// try testing.expect(match.orderedContains(.{ .x = 1, .y = 1 }));
// try testing.expect(!match.orderedContains(.{ .x = 1, .y = 2 }));
// }
// }
//
// test "matchset mods no match" {
// const testing = std.testing;
// const alloc = testing.allocator;
//
// // Initialize our screen
// var s = try Screen.init(alloc, 5, 5, 0);
// defer s.deinit();
// const str = "1ABCD2EFGH\n3IJKL";
// try s.testWriteString(str);
//
// // Get a set
// var set = try Set.fromConfig(alloc, &.{
// .{
// .regex = "AB",
// .action = .{ .open = {} },
// .highlight = .{ .always = {} },
// },
//
// .{
// .regex = "EF",
// .action = .{ .open = {} },
// .highlight = .{ .always_mods = .{ .ctrl = true } },
// },
// });
// defer set.deinit(alloc);
//
// // Get our matches
// var match = try set.matchSet(alloc, &s, .{}, .{});
// defer match.deinit(alloc);
// try testing.expectEqual(@as(usize, 1), match.matches.len);
//
// // Test our matches
// try testing.expect(!match.orderedContains(.{ .x = 0, .y = 0 }));
// try testing.expect(match.orderedContains(.{ .x = 1, .y = 0 }));
// try testing.expect(match.orderedContains(.{ .x = 2, .y = 0 }));
// try testing.expect(!match.orderedContains(.{ .x = 3, .y = 0 }));
// try testing.expect(!match.orderedContains(.{ .x = 1, .y = 1 }));
// try testing.expect(!match.orderedContains(.{ .x = 1, .y = 2 }));
// }
test "matchset" {
const testing = std.testing;
const alloc = testing.allocator;
// Initialize our screen
var s = try Screen.init(alloc, 5, 5, 0);
defer s.deinit();
const str = "1ABCD2EFGH\n3IJKL";
try s.testWriteString(str);
// Get a set
var set = try Set.fromConfig(alloc, &.{
.{
.regex = "AB",
.action = .{ .open = {} },
.highlight = .{ .always = {} },
},
.{
.regex = "EF",
.action = .{ .open = {} },
.highlight = .{ .always = {} },
},
});
defer set.deinit(alloc);
// Get our matches
var match = try set.matchSet(alloc, &s, .{}, .{});
defer match.deinit(alloc);
try testing.expectEqual(@as(usize, 2), match.matches.len);
// Test our matches
try testing.expect(!match.orderedContains(&s, s.pages.pin(.{ .screen = .{
.x = 0,
.y = 0,
} }).?));
try testing.expect(match.orderedContains(&s, s.pages.pin(.{ .screen = .{
.x = 1,
.y = 0,
} }).?));
try testing.expect(match.orderedContains(&s, s.pages.pin(.{ .screen = .{
.x = 2,
.y = 0,
} }).?));
try testing.expect(!match.orderedContains(&s, s.pages.pin(.{ .screen = .{
.x = 3,
.y = 0,
} }).?));
try testing.expect(match.orderedContains(&s, s.pages.pin(.{ .screen = .{
.x = 1,
.y = 1,
} }).?));
try testing.expect(!match.orderedContains(&s, s.pages.pin(.{ .screen = .{
.x = 1,
.y = 2,
} }).?));
}
test "matchset hover links" {
const testing = std.testing;
const alloc = testing.allocator;
// Initialize our screen
var s = try Screen.init(alloc, 5, 5, 0);
defer s.deinit();
const str = "1ABCD2EFGH\n3IJKL";
try s.testWriteString(str);
// Get a set
var set = try Set.fromConfig(alloc, &.{
.{
.regex = "AB",
.action = .{ .open = {} },
.highlight = .{ .hover = {} },
},
.{
.regex = "EF",
.action = .{ .open = {} },
.highlight = .{ .always = {} },
},
});
defer set.deinit(alloc);
// Not hovering over the first link
{
var match = try set.matchSet(alloc, &s, .{}, .{});
defer match.deinit(alloc);
try testing.expectEqual(@as(usize, 1), match.matches.len);
// Test our matches
try testing.expect(!match.orderedContains(&s, s.pages.pin(.{ .screen = .{
.x = 0,
.y = 0,
} }).?));
try testing.expect(!match.orderedContains(&s, s.pages.pin(.{ .screen = .{
.x = 1,
.y = 0,
} }).?));
try testing.expect(!match.orderedContains(&s, s.pages.pin(.{ .screen = .{
.x = 2,
.y = 0,
} }).?));
try testing.expect(!match.orderedContains(&s, s.pages.pin(.{ .screen = .{
.x = 3,
.y = 0,
} }).?));
try testing.expect(match.orderedContains(&s, s.pages.pin(.{ .screen = .{
.x = 1,
.y = 1,
} }).?));
try testing.expect(!match.orderedContains(&s, s.pages.pin(.{ .screen = .{
.x = 1,
.y = 2,
} }).?));
}
// Hovering over the first link
{
var match = try set.matchSet(alloc, &s, .{ .x = 1, .y = 0 }, .{});
defer match.deinit(alloc);
try testing.expectEqual(@as(usize, 2), match.matches.len);
// Test our matches
try testing.expect(!match.orderedContains(&s, s.pages.pin(.{ .screen = .{
.x = 0,
.y = 0,
} }).?));
try testing.expect(match.orderedContains(&s, s.pages.pin(.{ .screen = .{
.x = 1,
.y = 0,
} }).?));
try testing.expect(match.orderedContains(&s, s.pages.pin(.{ .screen = .{
.x = 2,
.y = 0,
} }).?));
try testing.expect(!match.orderedContains(&s, s.pages.pin(.{ .screen = .{
.x = 3,
.y = 0,
} }).?));
try testing.expect(match.orderedContains(&s, s.pages.pin(.{ .screen = .{
.x = 1,
.y = 1,
} }).?));
try testing.expect(!match.orderedContains(&s, s.pages.pin(.{ .screen = .{
.x = 1,
.y = 2,
} }).?));
}
}
test "matchset mods no match" {
const testing = std.testing;
const alloc = testing.allocator;
// Initialize our screen
var s = try Screen.init(alloc, 5, 5, 0);
defer s.deinit();
const str = "1ABCD2EFGH\n3IJKL";
try s.testWriteString(str);
// Get a set
var set = try Set.fromConfig(alloc, &.{
.{
.regex = "AB",
.action = .{ .open = {} },
.highlight = .{ .always = {} },
},
.{
.regex = "EF",
.action = .{ .open = {} },
.highlight = .{ .always_mods = .{ .ctrl = true } },
},
});
defer set.deinit(alloc);
// Get our matches
var match = try set.matchSet(alloc, &s, .{}, .{});
defer match.deinit(alloc);
try testing.expectEqual(@as(usize, 1), match.matches.len);
// Test our matches
try testing.expect(!match.orderedContains(&s, s.pages.pin(.{ .screen = .{
.x = 0,
.y = 0,
} }).?));
try testing.expect(match.orderedContains(&s, s.pages.pin(.{ .screen = .{
.x = 1,
.y = 0,
} }).?));
try testing.expect(match.orderedContains(&s, s.pages.pin(.{ .screen = .{
.x = 2,
.y = 0,
} }).?));
try testing.expect(!match.orderedContains(&s, s.pages.pin(.{ .screen = .{
.x = 3,
.y = 0,
} }).?));
try testing.expect(!match.orderedContains(&s, s.pages.pin(.{ .screen = .{
.x = 1,
.y = 1,
} }).?));
try testing.expect(!match.orderedContains(&s, s.pages.pin(.{ .screen = .{
.x = 1,
.y = 2,
} }).?));
}