feat: Select/Copy Links On Right Click If Present

This is a solution for #2107.

When a user right-clicks, and there's no existing selection, the
existing behavior is to try to select the word under the cursor:

3548acfac6/src/Surface.zig (L3740-L3742)

This PR tweaks that behavior _slightly_: If there's a link under our
cursor, as determined by `linkAtPos`, select the link (to copy with the
right-click context menu). Otherwise, select the word as before.

As noted in #2107, this matches the behavior of iTerm and Gnome
Terminal.

It's worth noting that `linkAtPos` already does the right thing in terms
of checking the links from config and their highlight/hover states
(modified by Ctrl or Super depending on platform).

3548acfac6/src/Surface.zig (L3896-L3901)

It also therefore respects `link-url` from config.

3548acfac6/src/config/Config.zig (L3411-L3416)

By using `linkAtPos`, we get all that behavior for free. In practical
terms, that means:
 - If I'm holding Ctrl so a link is underlined and I right click on it,
   it selects the underlined link.
 - If I'm not holding Ctrl and I right click on a link that is not
   underlined, it selects the word as before.
 - This behavior respects per-platform key bindings and user config
   settings.

`linkAtPos` requires that the render state mutex is held. I believe it's
safe to call because we're inside a block holding the mutex:
3548acfac6/src/Surface.zig (L3702-L3704)

**AI Disclosure:** I used Gemini CLI to help me with this PR because
while I have many years of programming experience, this is my first time
writing Zig. I prototyped a couple different approaches with AI before
landing on this one, so AI generated various prototypes and I chose the
final imlementation. I've verified that my code compiles and works as
intended.
pull/9298/head
Mike Kasberg 2025-10-21 17:25:54 -06:00
parent 2cc7341b08
commit 9477d32be6
1 changed files with 9 additions and 3 deletions

View File

@ -4014,8 +4014,8 @@ pub fn mouseButtonCallback(
// Get our viewport pin
const screen: *terminal.Screen = self.renderer_state.terminal.screens.active;
const pos = try self.rt_surface.getCursorPos();
const pin = pin: {
const pos = try self.rt_surface.getCursorPos();
const pt_viewport = self.posToViewport(pos.x, pos.y);
const pin = screen.pages.pin(.{
.viewport = .{
@ -4046,8 +4046,14 @@ pub fn mouseButtonCallback(
// word selection where we clicked.
}
const sel = screen.selectWord(pin) orelse break :sel;
try self.setSelection(sel);
// If there is a link at this position, we want to
// select the link. Otherwise, select the word.
if (try self.linkAtPos(pos)) |link| {
try self.setSelection(link[1]);
} else {
const sel = screen.selectWord(pin) orelse break :sel;
try self.setSelection(sel);
}
try self.queueRender();
// Don't consume so that we show the context menu in apprt.