From 85dc4b1842799d23db4abc5c2a671e4975c9d49d Mon Sep 17 00:00:00 2001 From: Vasyl Zuziak Date: Sat, 25 Apr 2026 15:46:25 +0200 Subject: [PATCH 1/2] surface: respect semantic prompt boundaries for links Link detection currently expands the clicked location to a full line before running the configured regexes. When semantic prompt markers are present, this can cause prompt text and neighboring content to be matched together even though they are distinct semantic regions. Use semantic prompt boundaries when selecting the text to inspect for link matching. This keeps prompt text separate from the content beside it and avoids folding prompt text into double-click link/path selection. Add a regression test that models a prompt and command on the same line and verifies the prompt region and input region remain separate. --- src/Surface.zig | 61 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/src/Surface.zig b/src/Surface.zig index dfc3a50ea..3a25757ea 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -4295,7 +4295,9 @@ fn linkAtPin( const line = screen.selectLine(.{ .pin = mouse_pin, .whitespace = null, - .semantic_prompt_boundary = false, + // Respect semantic prompt boundaries so link/path matching doesn't + // merge shell prompt content with the text beside it. + .semantic_prompt_boundary = true, }) orelse return null; var strmap: terminal.StringMap = undefined; @@ -6658,3 +6660,60 @@ test "Surface: rectangle selection logic" { true, //rectangle selection ); } + +test "Surface: link selection line respects prompt boundary" { + const testing = std.testing; + const alloc = testing.allocator; + + var screen = try terminal.Screen.init(alloc, .{ + .cols = 20, + .rows = 5, + .max_scrollback = 0, + }); + defer screen.deinit(); + + screen.cursorSetSemanticContent(.{ .prompt = .initial }); + try screen.testWriteString("/tmp $ "); + screen.cursorSetSemanticContent(.{ .input = .clear_explicit }); + try screen.testWriteString("locale"); + + { + var sel = screen.selectLine(.{ + .pin = screen.pages.pin(.{ .active = .{ + .x = 1, + .y = 0, + } }).?, + .whitespace = null, + .semantic_prompt_boundary = true, + }).?; + defer sel.deinit(&screen); + + const contents = try screen.selectionString(alloc, .{ + .sel = sel, + .trim = false, + }); + defer alloc.free(contents); + + try testing.expectEqualStrings("/tmp $ ", contents); + } + + { + var sel = screen.selectLine(.{ + .pin = screen.pages.pin(.{ .active = .{ + .x = 8, + .y = 0, + } }).?, + .whitespace = null, + .semantic_prompt_boundary = true, + }).?; + defer sel.deinit(&screen); + + const contents = try screen.selectionString(alloc, .{ + .sel = sel, + .trim = false, + }); + defer alloc.free(contents); + + try testing.expectEqualStrings("locale", contents); + } +} From c4a671ba5a5c3a896b90b066f2a22abe2eaad17b Mon Sep 17 00:00:00 2001 From: Vasyl Zuziak Date: Sat, 25 Apr 2026 19:26:22 +0200 Subject: [PATCH 2/2] fix: remove test as has been suggested in comment --- src/Surface.zig | 57 ------------------------------------------------- 1 file changed, 57 deletions(-) diff --git a/src/Surface.zig b/src/Surface.zig index 3a25757ea..c74142a0a 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -6660,60 +6660,3 @@ test "Surface: rectangle selection logic" { true, //rectangle selection ); } - -test "Surface: link selection line respects prompt boundary" { - const testing = std.testing; - const alloc = testing.allocator; - - var screen = try terminal.Screen.init(alloc, .{ - .cols = 20, - .rows = 5, - .max_scrollback = 0, - }); - defer screen.deinit(); - - screen.cursorSetSemanticContent(.{ .prompt = .initial }); - try screen.testWriteString("/tmp $ "); - screen.cursorSetSemanticContent(.{ .input = .clear_explicit }); - try screen.testWriteString("locale"); - - { - var sel = screen.selectLine(.{ - .pin = screen.pages.pin(.{ .active = .{ - .x = 1, - .y = 0, - } }).?, - .whitespace = null, - .semantic_prompt_boundary = true, - }).?; - defer sel.deinit(&screen); - - const contents = try screen.selectionString(alloc, .{ - .sel = sel, - .trim = false, - }); - defer alloc.free(contents); - - try testing.expectEqualStrings("/tmp $ ", contents); - } - - { - var sel = screen.selectLine(.{ - .pin = screen.pages.pin(.{ .active = .{ - .x = 8, - .y = 0, - } }).?, - .whitespace = null, - .semantic_prompt_boundary = true, - }).?; - defer sel.deinit(&screen); - - const contents = try screen.selectionString(alloc, .{ - .sel = sel, - .trim = false, - }); - defer alloc.free(contents); - - try testing.expectEqualStrings("locale", contents); - } -}