From d27cba9b2e2393b3eac882e84d748f4dfbd44af3 Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Fri, 20 Dec 2024 16:10:55 -0800 Subject: [PATCH] Add support for tabbing to embedded hyperlinks --- .../TerminalCore/TerminalSelection.cpp | 58 +++++++++++++++++-- 1 file changed, 52 insertions(+), 6 deletions(-) diff --git a/src/cascadia/TerminalCore/TerminalSelection.cpp b/src/cascadia/TerminalCore/TerminalSelection.cpp index 6a800f329dd..8155e35fb8e 100644 --- a/src/cascadia/TerminalCore/TerminalSelection.cpp +++ b/src/cascadia/TerminalCore/TerminalSelection.cpp @@ -404,7 +404,8 @@ void Terminal::SelectHyperlink(const SearchDirection dir) } // 0. Useful tools/vars - const auto bufferSize = _activeBuffer().GetSize(); + const auto& buffer = _activeBuffer(); + const auto bufferSize = buffer.GetSize(); const auto viewportHeight = _GetMutableViewport().Height(); // The patterns are stored relative to the "search area". Initially, this search area will be the viewport, @@ -514,15 +515,60 @@ void Terminal::SelectHyperlink(const SearchDirection dir) searchArea = Viewport::FromDimensions(searchStart, { searchEnd.x + 1, searchEnd.y + 1 }); } } + } - // 1.C) Nothing was found. Bail! - if (!result.has_value()) + // 2. We found a hyperlink from the pattern tree. Look for embedded hyperlinks too! + // Use the result (if one was found) to narrow down the search. + searchStart = dir == SearchDirection::Forward ? + _selection->start : + (result ? result->second : bufferSize.Origin()); + searchEnd = dir == SearchDirection::Forward ? + (result ? result->first : buffer.GetLastNonSpaceCharacter()) : + _selection->start; + auto iter = buffer.GetCellDataAt(dir == SearchDirection::Forward ? searchStart : searchEnd); + while (dir == SearchDirection::Forward ? iter.Pos() < searchEnd : iter.Pos() > searchStart) + { + // Don't let us select the same hyperlink again + if (iter.Pos() < _selection->start || iter.Pos() > _selection->end) { - return; + if (auto attr = iter->TextAttr(); attr.IsHyperlink()) + { + // Found an embedded hyperlink! + const auto hyperlinkId = attr.GetHyperlinkId(); + + // Expand the start to include the entire hyperlink + TextBufferCellIterator hyperlinkStartIter{ buffer, iter.Pos() }; + while (attr.IsHyperlink() && attr.GetHyperlinkId() == hyperlinkId) + { + hyperlinkStartIter--; + attr = hyperlinkStartIter->TextAttr(); + } + // undo a move to be inclusive + hyperlinkStartIter++; + + // Expand the end to include the entire hyperlink + // No need to undo a move! We'll decrement in the next step anyways. + TextBufferCellIterator hyperlinkEndIter{ buffer, iter.Pos() }; + attr = hyperlinkEndIter->TextAttr(); + while (attr.IsHyperlink() && attr.GetHyperlinkId() == hyperlinkId) + { + hyperlinkEndIter++; + attr = hyperlinkEndIter->TextAttr(); + } + + result = { hyperlinkStartIter.Pos(), hyperlinkEndIter.Pos() }; + break; + } } + iter += dir == SearchDirection::Forward ? 1 : -1; } - // 2. Select the hyperlink + // 3. Select the hyperlink, if one exists + if (!result.has_value()) + { + return; + } + else { auto selection{ _selection.write() }; wil::hide_name _selection; @@ -534,7 +580,7 @@ void Terminal::SelectHyperlink(const SearchDirection dir) _selectionEndpoint = SelectionEndpoint::End; } - // 3. Scroll to the selected area (if necessary) + // 4. Scroll to the selected area (if necessary) _ScrollToPoint(_selection->end); }