aboutsummaryrefslogtreecommitdiff
path: root/vendor/libghostty-vt/src/renderer/link.zig
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-07-16 23:28:05 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-07-16 23:29:28 +0300
commit4a3302d19a2baa3a33e910e0088c24e780905b90 (patch)
treedcaf76fae1c82102d933c9225b17ca7ac5d3a847 /vendor/libghostty-vt/src/renderer/link.zig
parent550e9f8cc085f428429f5cf2da0bb2dba897e8b8 (diff)
chore: update vendored libghostty-vt
refs #276
Diffstat (limited to 'vendor/libghostty-vt/src/renderer/link.zig')
-rw-r--r--vendor/libghostty-vt/src/renderer/link.zig96
1 files changed, 82 insertions, 14 deletions
diff --git a/vendor/libghostty-vt/src/renderer/link.zig b/vendor/libghostty-vt/src/renderer/link.zig
index c5de6157..11ecc87a 100644
--- a/vendor/libghostty-vt/src/renderer/link.zig
+++ b/vendor/libghostty-vt/src/renderer/link.zig
@@ -21,6 +21,20 @@ pub const Link = struct {
pub fn deinit(self: *Link) void {
self.regex.deinit();
}
+
+ /// Returns true if this link's highlight condition matches the given mouse state.
+ fn active(
+ self: *const Link,
+ mouse_viewport: ?point.Coordinate,
+ mouse_mods: inputpkg.Mods,
+ ) bool {
+ return switch (self.highlight) {
+ .always => true,
+ .always_mods => |v| mouse_mods.equal(v),
+ .hover => mouse_viewport != null,
+ .hover_mods => |v| mouse_viewport != null and mouse_mods.equal(v),
+ };
+ }
};
/// A set of links. This provides a higher level API for renderers
@@ -66,6 +80,14 @@ pub const Set = struct {
// Fast path, not very likely since we have default links.
if (self.links.len == 0) return;
+ // Determine if any links are active before building the string and
+ // byte-to-cell map. Those buffers scale with viewport size and this
+ // function runs during frame updates, so avoid allocating them when
+ // the current mouse/modifier state can't highlight any regex links.
+ for (self.links) |*link| {
+ if (link.active(mouse_viewport, mouse_mods)) break;
+ } else return;
+
// Convert our render state to a string + byte map.
var builder: std.Io.Writer.Allocating = .init(alloc);
defer builder.deinit();
@@ -80,20 +102,7 @@ pub const Set = struct {
// Go through each link and see if we have any matches.
for (self.links) |*link| {
- // Determine if our highlight conditions are met. We use a
- // switch here instead of an if so that we can get a compile
- // error if any other conditions are added.
- switch (link.highlight) {
- .always => {},
- .always_mods => |v| if (!mouse_mods.equal(v)) continue,
-
- // We check the hover points later.
- .hover => if (mouse_viewport == null) continue,
- .hover_mods => |v| {
- if (mouse_viewport == null) continue;
- if (!mouse_mods.equal(v)) continue;
- },
- }
+ if (!link.active(mouse_viewport, mouse_mods)) continue;
var offset: usize = 0;
while (offset < str.len) {
@@ -266,6 +275,65 @@ test "renderCellMap hover links" {
}
}
+test "renderCellMap inactive links don't allocate" {
+ const testing = std.testing;
+ const alloc = testing.allocator;
+
+ var t: terminal.Terminal = try .init(alloc, .{
+ .cols = 5,
+ .rows = 3,
+ });
+ defer t.deinit(alloc);
+
+ var s = t.vtStream();
+ defer s.deinit();
+ const str = "1ABCD2EFGH\r\n3IJKL";
+ s.nextSlice(str);
+
+ var state: terminal.RenderState = .empty;
+ defer state.deinit(alloc);
+ try state.update(alloc, &t);
+
+ var set = try Set.fromConfig(alloc, &.{
+ .{
+ .regex = "AB",
+ .action = .{ .open = {} },
+ .highlight = .{ .hover = {} },
+ },
+
+ .{
+ .regex = "EF",
+ .action = .{ .open = {} },
+ .highlight = .{ .always_mods = .{ .ctrl = true } },
+ },
+
+ .{
+ .regex = "IJ",
+ .action = .{ .open = {} },
+ .highlight = .{ .hover_mods = .{ .shift = true } },
+ },
+ });
+ defer set.deinit(alloc);
+
+ var failing = std.testing.FailingAllocator.init(
+ alloc,
+ .{ .fail_index = 0 },
+ );
+ const failing_alloc = failing.allocator();
+
+ var result: terminal.RenderState.CellSet = .empty;
+ defer result.deinit(failing_alloc);
+ try set.renderCellMap(
+ failing_alloc,
+ &result,
+ &state,
+ null,
+ .{},
+ );
+
+ try testing.expectEqual(@as(usize, 0), result.count());
+}
+
test "renderCellMap mods no match" {
const testing = std.testing;
const alloc = testing.allocator;