[PR #13405] Add keyboard navigation to hyperlinks #29534

Open
opened 2026-01-31 09:35:28 +00:00 by claunia · 0 comments
Owner

Original Pull Request: https://github.com/microsoft/terminal/pull/13405

State: closed
Merged: Yes


Summary of the Pull Request

Adds support to navigate to clickable hyperlinks using only the keyboard. When in mark mode, the user can press [shift+]tab to go the previous/next hyperlink in the text buffer. Once a hyperlink is selected, the user can press Ctrl+Enter to open the hyperlink.

References

#4993

PR Checklist

Detailed Description of the Pull Request / Additional comments

  • Main change
    • The OpenHyperlink event needs to be piped down to ControlCore now so that we can open a hyperlink at that layer.
    • SelectHyperlink(dir) searches the buffer in a given direction and finds the first hyperlink, then selects it.
    • "finding the hyperlink" is the tough part because the pattern tree takes in buffer coordinates, searches through the buffer in that given space, then stores everything relative to the defined space. Normally, the given buffer coordinates would align with the viewport's start and end. However, we're now trying to search outside of the viewport (sometimes), so we need to manage two coordinate systems at the same time.
    • convertToSearchArea() lambda was used to convert a given coordinate into the search area coordinate system. So if the search area is the visible viewport, we spit out a viewport position. If the search area is the next viewport, we spit out a position relative to that.
    • extractResultFromList() lambda takes the list of patterns from the pattern tree and spits out the hyperlink we want. If we're searching forwards, we get the next one. Otherwise, we get the previous one. We explicitly ignore the one we're already on. If we don't find any, we return nullopt.
    • Now that we have all these cool tools, we use them to progressively search through the buffer to find the next/previous hyperlink. Start by searching the visible viewport after (or before) the current selection. If we can't find anything, go to the next "page" (viewport scrolled up/down). Repeat this process until something comes up.
    • If we can't find anything, nothing happens. We don't wrap around.
  • Other relevant changes
    • the copy action is no longer bound to Enter. Instead, we manually handle it in ControlCore.cpp. This also lets us handle Shift+Enter appropriately without having to take another key binding.
    • _ScrollToPoint was added. It's a simple function that just scrolls the viewport such that the provided buffer position is in view. This was used to de-duplicate code.
    • _ScrollToPoint was added into the ToggleMarkMode() function. Turns out, we don't scroll to the new selection when we enter mark mode (whoops!). We should do that and we should backport this part of the change too. I'll handle that.
    • add some clarity when some functions are using the viewport position vs the buffer position. This is important because the pattern interval tree works in the viewport space.

Validation Steps Performed

  • case: all hyperlinks are in the view
    • get next link
    • get prev link
  • case: need to scroll down for next link
  • case: need to scroll up for next link
**Original Pull Request:** https://github.com/microsoft/terminal/pull/13405 **State:** closed **Merged:** Yes --- ## Summary of the Pull Request Adds support to navigate to clickable hyperlinks using only the keyboard. When in mark mode, the user can press [shift+]tab to go the previous/next hyperlink in the text buffer. Once a hyperlink is selected, the user can press <kbd>Ctrl+Enter</kbd> to open the hyperlink. ## References #4993 ## PR Checklist * [x] Closes #6649 * [x] Documentation updated at https://github.com/MicrosoftDocs/terminal/pull/558 ## Detailed Description of the Pull Request / Additional comments - Main change - The `OpenHyperlink` event needs to be piped down to `ControlCore` now so that we can open a hyperlink at that layer. - `SelectHyperlink(dir)` searches the buffer in a given direction and finds the first hyperlink, then selects it. - "finding the hyperlink" is the tough part because the pattern tree takes in buffer coordinates, searches through the buffer in that given space, then stores everything relative to the defined space. Normally, the given buffer coordinates would align with the viewport's start and end. However, we're now trying to search outside of the viewport (sometimes), so we need to manage two coordinate systems at the same time. - `convertToSearchArea()` lambda was used to convert a given coordinate into the search area coordinate system. So if the search area is the visible viewport, we spit out a viewport position. If the search area is the _next_ viewport, we spit out a position relative to that. - `extractResultFromList()` lambda takes the list of patterns from the pattern tree and spits out the hyperlink we want. If we're searching forwards, we get the next one. Otherwise, we get the previous one. We explicitly ignore the one we're already on. If we don't find any, we return `nullopt`. - Now that we have all these cool tools, we use them to progressively search through the buffer to find the next/previous hyperlink. Start by searching the visible viewport _after_ (or _before_) the current selection. If we can't find anything, go to the next "page" (viewport scrolled up/down). Repeat this process until something comes up. - If we can't find anything, nothing happens. We don't wrap around. - Other relevant changes - the `copy` action is no longer bound to `Enter`. Instead, we manually handle it in `ControlCore.cpp`. This also lets us handle <kbd>Shift+Enter</kbd> appropriately without having to take another key binding. - `_ScrollToPoint` was added. It's a simple function that just scrolls the viewport such that the provided buffer position is in view. This was used to de-duplicate code. - `_ScrollToPoint` was added into the `ToggleMarkMode()` function. Turns out, we don't scroll to the new selection when we enter mark mode (whoops!). We _should_ do that and we should backport this part of the change too. I'll handle that. - add some clarity when some functions are using the viewport position vs the buffer position. This is important because the pattern interval tree works in the viewport space. ## Validation Steps Performed - case: all hyperlinks are in the view - ✅ get next link - ✅ get prev link - ✅ case: need to scroll down for next link - ✅ case: need to scroll up for next link
claunia added the pull-request label 2026-01-31 09:35:28 +00:00
Sign in to join this conversation.
No Label pull-request
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/terminal#29534