From 481c16ea1ea1e413e6b3af8841983a375c86baad Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Mon, 21 Sep 2020 12:56:40 -0400 Subject: [PATCH 01/48] rough implementation --- src/buffer/out/OutputCellIterator.cpp | 5 +++ src/buffer/out/OutputCellIterator.hpp | 2 + src/buffer/out/textBuffer.cpp | 65 +++++++++++++++++++++++++-- src/buffer/out/textBuffer.hpp | 2 + 4 files changed, 71 insertions(+), 3 deletions(-) diff --git a/src/buffer/out/OutputCellIterator.cpp b/src/buffer/out/OutputCellIterator.cpp index d32087fe9e6..d74471d336c 100644 --- a/src/buffer/out/OutputCellIterator.cpp +++ b/src/buffer/out/OutputCellIterator.cpp @@ -197,6 +197,11 @@ OutputCellIterator::operator bool() const noexcept CATCH_FAIL_FAST(); } +std::wstring_view OutputCellIterator::returnRun() +{ + return std::get(_run); +} + // Routine Description: // - Advances the iterator one position over the underlying data source. // Return Value: diff --git a/src/buffer/out/OutputCellIterator.hpp b/src/buffer/out/OutputCellIterator.hpp index 59c6007aa3a..b9a1c57a649 100644 --- a/src/buffer/out/OutputCellIterator.hpp +++ b/src/buffer/out/OutputCellIterator.hpp @@ -48,6 +48,8 @@ class OutputCellIterator final operator bool() const noexcept; + std::wstring_view returnRun(); + ptrdiff_t GetCellDistance(OutputCellIterator other) const noexcept; ptrdiff_t GetInputDistance(OutputCellIterator other) const noexcept; friend ptrdiff_t operator-(OutputCellIterator one, OutputCellIterator two) = delete; diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index 294813a3f55..07763c4d81f 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -307,14 +307,73 @@ bool TextBuffer::_PrepareForDoubleByteSequence(const DbcsAttribute dbcsAttribute // - givenIt - Iterator representing output cell data to write // Return Value: // - The final position of the iterator -OutputCellIterator TextBuffer::Write(const OutputCellIterator givenIt) +OutputCellIterator TextBuffer::Write(OutputCellIterator givenIt) { const auto& cursor = GetCursor(); const auto target = cursor.GetPosition(); - const auto finalIt = Write(givenIt, target); + // the regex object we will use to match URIs + std::regex uri(R"([a-z]+[:.].*?(?=\s))"); - return finalIt; + // get the text from the iterator we are about to write + auto run = til::u16u8(givenIt.returnRun()); + + // search through the run with our regex object + auto words_begin = std::sregex_iterator(run.begin(), run.end(), uri); + auto words_end = std::sregex_iterator(); + if (words_begin != words_end) + { + // we found some matches + std::wstring sufStr; + + // get a mutable target coord + auto target2 = target; + for (auto i = words_begin; i != words_end; ++i) + { + // for each match, we write the prefix (which is everything between the last match and + // this match) with the current attributes, then set the hyperlink attribute to write the URI, + // then we set the attributes back + + // get the URI and prefix strings + const auto uriStr = til::u8u16(i->str()); + const auto preStr = til::u8u16(i->prefix().str()); + + // store the suffix string in case this is the last match we've found - once the loop + // is done we need to write the suffix of the last match + // we only write the suffix of the last match because the suffix of the nth match + // is simply the prefix of the (n+1)th match, which we write anyway + sufStr = til::u8u16(i->suffix().str()); + + // write the prefix string and update the target + OutputCellIterator preIter{ preStr, _currentAttributes }; + Write(preIter, target2); + target2.X += (SHORT) preStr.size(); + + // update metadata for hyperlinks + const auto id = GetHyperlinkId(L""); + AddHyperlinkToMap(uriStr, id); + const auto oldId = _currentAttributes.GetHyperlinkId(); + _currentAttributes.SetHyperlinkId(id); + + // write the uri and update the target + OutputCellIterator uriIter{ uriStr, _currentAttributes }; + Write(uriIter, target2); + target2.X += (SHORT) uriStr.size(); + + // set the attributes back + _currentAttributes.SetHyperlinkId(oldId); + } + + // write the suffix string of the last match + const OutputCellIterator sufIter{ sufStr, _currentAttributes }; + const auto finalIt = Write(sufIter, target2); + return finalIt; + } + else + { + const auto finalIt = Write(givenIt, target); + return finalIt; + } } // Routine Description: diff --git a/src/buffer/out/textBuffer.hpp b/src/buffer/out/textBuffer.hpp index 56a3c59f3b0..014757ddba0 100644 --- a/src/buffer/out/textBuffer.hpp +++ b/src/buffer/out/textBuffer.hpp @@ -60,6 +60,8 @@ filling in the last row, and updating the screen. #include "../renderer/inc/IRenderTarget.hpp" +#include + class TextBuffer final { public: From 062e7800593399ea41efdb33f4b212cacc5d6603 Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Mon, 21 Sep 2020 13:07:50 -0400 Subject: [PATCH 02/48] spell/format --- .github/actions/spell-check/dictionary/apis.txt | 1 + src/buffer/out/textBuffer.cpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/actions/spell-check/dictionary/apis.txt b/.github/actions/spell-check/dictionary/apis.txt index 6e450ee12bb..1ced8c34a83 100644 --- a/.github/actions/spell-check/dictionary/apis.txt +++ b/.github/actions/spell-check/dictionary/apis.txt @@ -45,6 +45,7 @@ rx serializer SIZENS spsc +sregex STDCPP syscall tmp diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index 07763c4d81f..b72906bd82b 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -347,7 +347,7 @@ OutputCellIterator TextBuffer::Write(OutputCellIterator givenIt) // write the prefix string and update the target OutputCellIterator preIter{ preStr, _currentAttributes }; Write(preIter, target2); - target2.X += (SHORT) preStr.size(); + target2.X += (SHORT)preStr.size(); // update metadata for hyperlinks const auto id = GetHyperlinkId(L""); @@ -358,7 +358,7 @@ OutputCellIterator TextBuffer::Write(OutputCellIterator givenIt) // write the uri and update the target OutputCellIterator uriIter{ uriStr, _currentAttributes }; Write(uriIter, target2); - target2.X += (SHORT) uriStr.size(); + target2.X += (SHORT)uriStr.size(); // set the attributes back _currentAttributes.SetHyperlinkId(oldId); From 973ed974e865bd5323d184df3888ac1a8255f51a Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Mon, 21 Sep 2020 14:10:20 -0400 Subject: [PATCH 03/48] const/c cast fix --- src/buffer/out/textBuffer.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index b72906bd82b..73e5a8a12f7 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -325,6 +325,7 @@ OutputCellIterator TextBuffer::Write(OutputCellIterator givenIt) { // we found some matches std::wstring sufStr; + SHORT inc; // get a mutable target coord auto target2 = target; @@ -345,9 +346,10 @@ OutputCellIterator TextBuffer::Write(OutputCellIterator givenIt) sufStr = til::u8u16(i->suffix().str()); // write the prefix string and update the target - OutputCellIterator preIter{ preStr, _currentAttributes }; + const OutputCellIterator preIter{ preStr, _currentAttributes }; Write(preIter, target2); - target2.X += (SHORT)preStr.size(); + UInt64ToShort(preStr.size(), &inc); + target2.X += inc; // update metadata for hyperlinks const auto id = GetHyperlinkId(L""); @@ -356,9 +358,10 @@ OutputCellIterator TextBuffer::Write(OutputCellIterator givenIt) _currentAttributes.SetHyperlinkId(id); // write the uri and update the target - OutputCellIterator uriIter{ uriStr, _currentAttributes }; + const OutputCellIterator uriIter{ uriStr, _currentAttributes }; Write(uriIter, target2); - target2.X += (SHORT)uriStr.size(); + UInt64ToShort(uriStr.size(), &inc); + target2.X += inc; // set the attributes back _currentAttributes.SetHyperlinkId(oldId); From dbb9a9d1ea248d2b0df08f8b00264fa5dd7afe5e Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Mon, 21 Sep 2020 14:20:42 -0400 Subject: [PATCH 04/48] initialize inc --- src/buffer/out/textBuffer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index 73e5a8a12f7..c3dbf2d6dac 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -325,7 +325,7 @@ OutputCellIterator TextBuffer::Write(OutputCellIterator givenIt) { // we found some matches std::wstring sufStr; - SHORT inc; + SHORT inc = 0; // get a mutable target coord auto target2 = target; From e4e58580e213b4ebc8638be0420b5ccc6e9069a6 Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Fri, 25 Sep 2020 11:37:51 -0400 Subject: [PATCH 05/48] prototype --- src/buffer/out/OutputCellIterator.cpp | 5 - src/buffer/out/OutputCellIterator.hpp | 2 - src/buffer/out/textBuffer.cpp | 154 ++++++++++-------- src/buffer/out/textBuffer.hpp | 6 + src/cascadia/TerminalControl/TermControl.cpp | 23 +++ src/cascadia/TerminalControl/TermControl.h | 4 + src/cascadia/TerminalCore/Terminal.cpp | 51 ++++++ src/cascadia/TerminalCore/Terminal.hpp | 8 + .../TerminalCore/terminalrenderdata.cpp | 18 ++ src/host/renderData.cpp | 6 + src/host/renderData.hpp | 2 + src/renderer/base/renderer.cpp | 21 ++- src/renderer/inc/IRenderData.hpp | 2 + 13 files changed, 226 insertions(+), 76 deletions(-) diff --git a/src/buffer/out/OutputCellIterator.cpp b/src/buffer/out/OutputCellIterator.cpp index d74471d336c..d32087fe9e6 100644 --- a/src/buffer/out/OutputCellIterator.cpp +++ b/src/buffer/out/OutputCellIterator.cpp @@ -197,11 +197,6 @@ OutputCellIterator::operator bool() const noexcept CATCH_FAIL_FAST(); } -std::wstring_view OutputCellIterator::returnRun() -{ - return std::get(_run); -} - // Routine Description: // - Advances the iterator one position over the underlying data source. // Return Value: diff --git a/src/buffer/out/OutputCellIterator.hpp b/src/buffer/out/OutputCellIterator.hpp index b9a1c57a649..59c6007aa3a 100644 --- a/src/buffer/out/OutputCellIterator.hpp +++ b/src/buffer/out/OutputCellIterator.hpp @@ -48,8 +48,6 @@ class OutputCellIterator final operator bool() const noexcept; - std::wstring_view returnRun(); - ptrdiff_t GetCellDistance(OutputCellIterator other) const noexcept; ptrdiff_t GetInputDistance(OutputCellIterator other) const noexcept; friend ptrdiff_t operator-(OutputCellIterator one, OutputCellIterator two) = delete; diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index c3dbf2d6dac..c03a4509ed1 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -35,7 +35,8 @@ TextBuffer::TextBuffer(const COORD screenBufferSize, _unicodeStorage{}, _renderTarget{ renderTarget }, _size{}, - _currentHyperlinkId{ 1 } + _currentHyperlinkId{ 1 }, + _currentPatternId{ 0 } { // initialize ROWs for (size_t i = 0; i < static_cast(screenBufferSize.Y); ++i) @@ -307,76 +308,14 @@ bool TextBuffer::_PrepareForDoubleByteSequence(const DbcsAttribute dbcsAttribute // - givenIt - Iterator representing output cell data to write // Return Value: // - The final position of the iterator -OutputCellIterator TextBuffer::Write(OutputCellIterator givenIt) +OutputCellIterator TextBuffer::Write(const OutputCellIterator givenIt) { const auto& cursor = GetCursor(); const auto target = cursor.GetPosition(); - // the regex object we will use to match URIs - std::regex uri(R"([a-z]+[:.].*?(?=\s))"); + const auto finalIt = Write(givenIt, target); - // get the text from the iterator we are about to write - auto run = til::u16u8(givenIt.returnRun()); - - // search through the run with our regex object - auto words_begin = std::sregex_iterator(run.begin(), run.end(), uri); - auto words_end = std::sregex_iterator(); - if (words_begin != words_end) - { - // we found some matches - std::wstring sufStr; - SHORT inc = 0; - - // get a mutable target coord - auto target2 = target; - for (auto i = words_begin; i != words_end; ++i) - { - // for each match, we write the prefix (which is everything between the last match and - // this match) with the current attributes, then set the hyperlink attribute to write the URI, - // then we set the attributes back - - // get the URI and prefix strings - const auto uriStr = til::u8u16(i->str()); - const auto preStr = til::u8u16(i->prefix().str()); - - // store the suffix string in case this is the last match we've found - once the loop - // is done we need to write the suffix of the last match - // we only write the suffix of the last match because the suffix of the nth match - // is simply the prefix of the (n+1)th match, which we write anyway - sufStr = til::u8u16(i->suffix().str()); - - // write the prefix string and update the target - const OutputCellIterator preIter{ preStr, _currentAttributes }; - Write(preIter, target2); - UInt64ToShort(preStr.size(), &inc); - target2.X += inc; - - // update metadata for hyperlinks - const auto id = GetHyperlinkId(L""); - AddHyperlinkToMap(uriStr, id); - const auto oldId = _currentAttributes.GetHyperlinkId(); - _currentAttributes.SetHyperlinkId(id); - - // write the uri and update the target - const OutputCellIterator uriIter{ uriStr, _currentAttributes }; - Write(uriIter, target2); - UInt64ToShort(uriStr.size(), &inc); - target2.X += inc; - - // set the attributes back - _currentAttributes.SetHyperlinkId(oldId); - } - - // write the suffix string of the last match - const OutputCellIterator sufIter{ sufStr, _currentAttributes }; - const auto finalIt = Write(sufIter, target2); - return finalIt; - } - else - { - const auto finalIt = Write(givenIt, target); - return finalIt; - } + return finalIt; } // Routine Description: @@ -2415,3 +2354,86 @@ void TextBuffer::CopyHyperlinkMaps(const TextBuffer& other) _hyperlinkMap = other._hyperlinkMap; _hyperlinkCustomIdMap = other._hyperlinkCustomIdMap; } + +// Method Description: +// - Adds a regex pattern we should search for +// - The searching does not happen here, we only search when asked to by TerminalCore +// Arguments: +// - The regex pattern +// Return value: +// - An ID that the caller should associate with the given pattern +const size_t TextBuffer::AddPatternRecognizer(const std::string regexString) +{ + ++_currentPatternId; + _IdsAndPatterns.emplace(std::make_pair(_currentPatternId, regexString)); + return _currentPatternId; +} + +// Method Description: +// - Finds patterns within the requested region of the text buffer +// Arguments: +// - The firstRow to start searching from +// - The lastRow to search +// Return value: +// - A vector containing 3-element tuples where +// tuple<0>: ID of the pattern +// tuple<1>: start coordinate of the pattern +// tuple<2>: end coordinate of the pattern +const std::vector> TextBuffer::UpdatePatterns(const size_t firstRow, const size_t lastRow) const +{ + std::vector> result; + + std::wstring concatAll; + const auto rowSize = GetRowByOffset(0).size(); + concatAll.reserve(rowSize * (lastRow - firstRow + 1)); + + // to deal with text that spans multiple lines, we will first concatenate + // all the text into one string and find the pattern in that string + // later, we convert the locations of the patterns back into (row, col) coords + for (auto i = firstRow; i <= lastRow; ++i) + { + auto row = GetRowByOffset(i); + concatAll += row.GetCharRow().GetText(); + } + + // convert the string into something the regex iterator can work with + const auto constAll = til::u16u8(concatAll); + + // for each pattern we know of, iterate through the string + for (auto idAndPattern : _IdsAndPatterns) + { + std::regex regexObj{ idAndPattern.second }; + + // search through the run with our regex object + auto words_begin = std::sregex_iterator(constAll.begin(), constAll.end(), regexObj); + auto words_end = std::sregex_iterator(); + + size_t lenUpToThis = 0; + for (auto i = words_begin; i != words_end; ++i) + { + // record the locations and convert them back to coords + // when we find a match, the prefix is text that is between this + // match and the previous match, and the suffix is the text that + // it between this match and the next match + // we will use that, along with the size of the match, to determine + // the locations + const auto prefixSize = i->prefix().str().size(); + const auto start = lenUpToThis + prefixSize; + const auto end = start + i->str().size(); + lenUpToThis = end; + + // store the locations as (col, row) coordinates + // NOTE: these are VIEWPORT coordinates, not buffer coordinates + // Keeping these as viewport coordinates for now because its the renderer + // that actually uses these coordinates and the renderer works in viewport coords + SHORT startRow = gsl::narrow(start / rowSize); + SHORT startCol = gsl::narrow(start % rowSize); + SHORT endRow = gsl::narrow(end / rowSize); + SHORT endCol = gsl::narrow(end % rowSize); + COORD startCoord{ startCol, startRow }; + COORD endCoord{ endCol, endRow }; + result.push_back(std::make_tuple(idAndPattern.first, startCoord, endCoord)); + } + } + return result; +} diff --git a/src/buffer/out/textBuffer.hpp b/src/buffer/out/textBuffer.hpp index 014757ddba0..67ebfea17d7 100644 --- a/src/buffer/out/textBuffer.hpp +++ b/src/buffer/out/textBuffer.hpp @@ -184,6 +184,9 @@ class TextBuffer final const std::optional lastCharacterViewport, std::optional> positionInfo); + const size_t AddPatternRecognizer(const std::string regexString); + const std::vector> UpdatePatterns(const size_t firstRow, const size_t lastRow) const; + private: void _UpdateSize(); Microsoft::Console::Types::Viewport _size; @@ -231,6 +234,9 @@ class TextBuffer final void _PruneHyperlinks(); + std::unordered_map _IdsAndPatterns; + size_t _currentPatternId; + #ifdef UNIT_TESTING friend class TextBufferTests; friend class UiaTextRangeTests; diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index a44ee4c0586..8c21513762f 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -35,6 +35,9 @@ constexpr const auto ScrollBarUpdateInterval = std::chrono::milliseconds(8); // The minimum delay between updating the TSF input control. constexpr const auto TsfRedrawInterval = std::chrono::milliseconds(100); +// The minimum delay between updating the locations of regex patterns +constexpr const auto UpdatePatternLocationsInterval = std::chrono::milliseconds(50); + DEFINE_ENUM_FLAG_OPERATORS(winrt::Microsoft::Terminal::TerminalControl::CopyFormat); namespace winrt::Microsoft::Terminal::TerminalControl::implementation @@ -103,6 +106,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation // This event is explicitly revoked in the destructor: does not need weak_ref auto onReceiveOutputFn = [this](const hstring str) { _terminal->Write(str); + _updatePatternLocations->Run(); }; _connectionOutputEventToken = _connection.TerminalOutput(onReceiveOutputFn); @@ -142,6 +146,16 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation TsfRedrawInterval, Dispatcher()); + _updatePatternLocations = std::make_shared>( + [weakThis = get_weak()]() { + if (auto control{ weakThis.get() }) + { + control->UpdatePatternLocations(); + } + }, + TsfRedrawInterval, + Dispatcher()); + _updateScrollBar = std::make_shared>( [weakThis = get_weak()](const auto& update) { if (auto control{ weakThis.get() }) @@ -1477,6 +1491,15 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation return _DoMouseWheel(location, modifiers, delta, state); } + // Method Description: + // - Tell TerminalCore to update its knowledge about the locations of visible regex patterns + // - We should call this (through the throttled function) when something causes the visible + // region to change, such as when new text enters the buffer or the viewport is scrolled + void TermControl::UpdatePatternLocations() + { + _terminal->UpdatePatterns(); + } + // Method Description: // - Adjust the opacity of the acrylic background in response to a mouse // scrolling event. diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index c2c46d01d66..71d3674b8d3 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -118,6 +118,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation bool OnMouseWheel(const Windows::Foundation::Point location, const int32_t delta, const bool leftButtonDown, const bool midButtonDown, const bool rightButtonDown); + void UpdatePatternLocations(); + ~TermControl(); Windows::UI::Xaml::Automation::Peers::AutomationPeer OnCreateAutomationPeer(); @@ -179,6 +181,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation std::shared_ptr> _tsfTryRedrawCanvas; + std::shared_ptr> _updatePatternLocations; + struct ScrollBarUpdate { std::optional newValue; diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index f7a9acab759..05b7e13ddf4 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -77,6 +77,9 @@ void Terminal::Create(COORD viewportSize, SHORT scrollbackLines, IRenderTarget& const TextAttribute attr{}; const UINT cursorSize = 12; _buffer = std::make_unique(bufferSize, attr, cursorSize, renderTarget); + // Add regex pattern recognizers to the buffer + // For now, we only add the URI regex pattern + _hyperlinkPatternId = _buffer->AddPatternRecognizer(R"([a-z]+[:.].*?(?=\s))"); } // Method Description: @@ -960,6 +963,45 @@ void Terminal::_NotifyTerminalCursorPositionChanged() noexcept } } +// Method Description: +// - Implements the logic to determine if a location is in a given region +// Arguments: +// - The location +// - The start and end coordinates of the region +// Return value: +// - True if the location is within those coordinates, false otherwise +bool Microsoft::Terminal::Core::Terminal::_IsLocationWithinCoordinates(const COORD location, const COORD first, const COORD second) const noexcept +{ + if (first.Y == second.Y) + { + const auto sameRow = location.Y == first.Y; + const auto inRange = (first.X <= location.X && location.X < second.X); + return (sameRow && inRange); + } + else + { + // check first row + if (location.Y == first.Y && (first.X <= location.X)) + { + return true; + } + // check rows in between first row and last row + for (auto curRow = first.Y + 1; curRow < second.Y; ++curRow) + { + if (location.Y == curRow) + { + return true; + } + } + // check last row + if (location.Y == second.Y && location.X < second.X) + { + return true; + } + } + return false; +} + void Terminal::SetWriteInputCallback(std::function pfn) noexcept { _pfnWriteInput.swap(pfn); @@ -1032,6 +1074,15 @@ bool Terminal::IsCursorBlinkingAllowed() const noexcept return cursor.IsBlinkingAllowed(); } +// Method Description: +// - Update our internal knowledge about where regex patterns are on the screen +// - This is called by TerminalControl (through a throttled function) when the visible +// region changes (for example by text entering the buffer or scrolling) +void Microsoft::Terminal::Core::Terminal::UpdatePatterns() noexcept +{ + _patternsAndLocations = _buffer->UpdatePatterns(_VisibleStartIndex(), _VisibleEndIndex()); +} + const std::optional Terminal::GetTabColor() const noexcept { return _tabColor; diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index accc7a2797d..2646ad59eae 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -160,6 +160,7 @@ class Microsoft::Terminal::Core::Terminal final : const bool IsGridLineDrawingAllowed() noexcept override; const std::wstring GetHyperlinkUri(uint16_t id) const noexcept override; const std::wstring GetHyperlinkCustomId(uint16_t id) const noexcept override; + const size_t GetPatternId(const COORD location) const noexcept override; #pragma endregion #pragma region IUiaData @@ -185,6 +186,8 @@ class Microsoft::Terminal::Core::Terminal final : void SetCursorOn(const bool isOn); bool IsCursorBlinkingAllowed() const noexcept; + void UpdatePatterns() noexcept; + const std::optional GetTabColor() const noexcept; #pragma region TextSelection @@ -229,6 +232,9 @@ class Microsoft::Terminal::Core::Terminal final : bool _altGrAliasing; bool _suppressApplicationTitle; + size_t _hyperlinkPatternId; + std::vector> _patternsAndLocations; + #pragma region Text Selection // a selection is represented as a range between two COORDs (start and end) // the pivot is the COORD that remains selected when you extend a selection in any direction @@ -302,6 +308,8 @@ class Microsoft::Terminal::Core::Terminal final : void _NotifyTerminalCursorPositionChanged() noexcept; + bool _IsLocationWithinCoordinates(const COORD location, const COORD first, const COORD second) const noexcept; + #pragma region TextSelection // These methods are defined in TerminalSelection.cpp std::vector _GetSelectionRects() const noexcept; diff --git a/src/cascadia/TerminalCore/terminalrenderdata.cpp b/src/cascadia/TerminalCore/terminalrenderdata.cpp index a2caab80074..3053bf2a3de 100644 --- a/src/cascadia/TerminalCore/terminalrenderdata.cpp +++ b/src/cascadia/TerminalCore/terminalrenderdata.cpp @@ -131,6 +131,24 @@ const std::wstring Microsoft::Terminal::Core::Terminal::GetHyperlinkCustomId(uin return _buffer->GetCustomIdFromId(id); } +// Method Description: +// - Gets the regex pattern id of a location +// Arguments: +// - The location +// Return value: +// - The pattern ID of the location +const size_t Microsoft::Terminal::Core::Terminal::GetPatternId(const COORD location) const noexcept +{ + for (auto found : _patternsAndLocations) + { + if (_IsLocationWithinCoordinates(location, std::get<1>(found), std::get<2>(found))) + { + return std::get<0>(found); + } + } + return 0; +} + std::vector Terminal::GetSelectionRects() noexcept try { diff --git a/src/host/renderData.cpp b/src/host/renderData.cpp index 33ae6fa3485..9b50f11194c 100644 --- a/src/host/renderData.cpp +++ b/src/host/renderData.cpp @@ -348,6 +348,12 @@ const std::wstring RenderData::GetHyperlinkCustomId(uint16_t id) const noexcept return gci.GetActiveOutputBuffer().GetTextBuffer().GetCustomIdFromId(id); } +// For now, we ignore regex patterns in conhost +const size_t RenderData::GetPatternId(const COORD /*location*/) const noexcept +{ + return 0; +} + // Routine Description: // - Converts a text attribute into the RGB values that should be presented, applying // relevant table translation information and preferences. diff --git a/src/host/renderData.hpp b/src/host/renderData.hpp index 2e8fa68790c..368b5e2c962 100644 --- a/src/host/renderData.hpp +++ b/src/host/renderData.hpp @@ -58,6 +58,8 @@ class RenderData final : const std::wstring GetHyperlinkUri(uint16_t id) const noexcept override; const std::wstring GetHyperlinkCustomId(uint16_t id) const noexcept override; + + const size_t GetPatternId(const COORD location) const noexcept override; #pragma endregion #pragma region IUiaData diff --git a/src/renderer/base/renderer.cpp b/src/renderer/base/renderer.cpp index 0bff7c42d5a..41207293f31 100644 --- a/src/renderer/base/renderer.cpp +++ b/src/renderer/base/renderer.cpp @@ -693,6 +693,8 @@ void Renderer::_PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine, // Retrieve the first color. auto color = it->TextAttr(); + // Retrieve the first pattern id + auto patternId = _pData->GetPatternId(target); // And hold the point where we should start drawing. auto screenPoint = target; @@ -704,7 +706,16 @@ void Renderer::_PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine, // We'll be changing the persistent one as we run through the inner loops to detect // when a run changes, but we will still need to know this color at the bottom // when we go to draw gridlines for the length of the run. - const auto currentRunColor = color; + auto currentRunColor = color; + + // Hold onto the current pattern id as well + const auto currentPatternId = patternId; + + // For now, we underline all patterns so adjust the currentRunColor if this run is a pattern + if (currentPatternId != 0) + { + currentRunColor.SetUnderlined(true); + } // Update the drawing brushes with our color. THROW_IF_FAILED(_UpdateDrawingBrushes(pEngine, currentRunColor, false)); @@ -731,16 +742,20 @@ void Renderer::_PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine, // This inner loop will accumulate clusters until the color changes. // When the color changes, it will save the new color off and break. + // We also accumulate clusters according to regex patterns do { - if (color != it->TextAttr()) + COORD thisPoint{ screenPoint.X + gsl::narrow(cols), screenPoint.Y }; + const auto thisPointPattern = _pData->GetPatternId(thisPoint); + if (color != it->TextAttr() || patternId != thisPointPattern) { auto newAttr{ it->TextAttr() }; // foreground doesn't matter for runs of spaces (!) // if we trick it . . . we call Paint far fewer times for cmatrix - if (!_IsAllSpaces(it->Chars()) || !newAttr.HasIdenticalVisualRepresentationForBlankSpace(color, globalInvert)) + if (!_IsAllSpaces(it->Chars()) || !newAttr.HasIdenticalVisualRepresentationForBlankSpace(color, globalInvert) || patternId != thisPointPattern) { color = newAttr; + patternId = thisPointPattern; break; // vend this run } } diff --git a/src/renderer/inc/IRenderData.hpp b/src/renderer/inc/IRenderData.hpp index 11b347e657e..0bf95e47ba4 100644 --- a/src/renderer/inc/IRenderData.hpp +++ b/src/renderer/inc/IRenderData.hpp @@ -69,6 +69,8 @@ namespace Microsoft::Console::Render virtual const std::wstring GetHyperlinkUri(uint16_t id) const noexcept = 0; virtual const std::wstring GetHyperlinkCustomId(uint16_t id) const noexcept = 0; + virtual const size_t GetPatternId(const COORD location) const noexcept = 0; + protected: IRenderData() = default; }; From 4db85a4d8c1d2dfd5e11bdbea9f6d71c37f4a1c1 Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Fri, 25 Sep 2020 11:57:19 -0400 Subject: [PATCH 06/48] const --- src/buffer/out/textBuffer.cpp | 12 ++++++------ src/host/ut_host/VtIoTests.cpp | 5 +++++ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index c03a4509ed1..db5683e9705 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -2426,12 +2426,12 @@ const std::vector> TextBuffer::UpdatePatterns(c // NOTE: these are VIEWPORT coordinates, not buffer coordinates // Keeping these as viewport coordinates for now because its the renderer // that actually uses these coordinates and the renderer works in viewport coords - SHORT startRow = gsl::narrow(start / rowSize); - SHORT startCol = gsl::narrow(start % rowSize); - SHORT endRow = gsl::narrow(end / rowSize); - SHORT endCol = gsl::narrow(end % rowSize); - COORD startCoord{ startCol, startRow }; - COORD endCoord{ endCol, endRow }; + const auto startRow = gsl::narrow(start / rowSize); + const auto startCol = gsl::narrow(start % rowSize); + const auto endRow = gsl::narrow(end / rowSize); + const auto endCol = gsl::narrow(end % rowSize); + const COORD startCoord{ startCol, startRow }; + const COORD endCoord{ endCol, endRow }; result.push_back(std::make_tuple(idAndPattern.first, startCoord, endCoord)); } } diff --git a/src/host/ut_host/VtIoTests.cpp b/src/host/ut_host/VtIoTests.cpp index 0e20bbcb3f5..21def6f814c 100644 --- a/src/host/ut_host/VtIoTests.cpp +++ b/src/host/ut_host/VtIoTests.cpp @@ -401,6 +401,11 @@ class MockRenderData : public IRenderData, IUiaData { return {}; } + + const size_t GetPatternId(const COORD /*location*/) const noexcept + { + return 0; + } }; void VtIoTests::RendererDtorAndThread() From 137fcb9631010c83e3fb4f338a3ecd123f766b7f Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Fri, 25 Sep 2020 14:24:06 -0400 Subject: [PATCH 07/48] detected links now clickable --- src/buffer/out/textBuffer.cpp | 2 +- src/buffer/out/textBuffer.hpp | 4 +--- src/cascadia/TerminalCore/Terminal.cpp | 24 ++++++++++++++++++++++-- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index db5683e9705..5b22cfe15fe 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -2362,7 +2362,7 @@ void TextBuffer::CopyHyperlinkMaps(const TextBuffer& other) // - The regex pattern // Return value: // - An ID that the caller should associate with the given pattern -const size_t TextBuffer::AddPatternRecognizer(const std::string regexString) +const size_t TextBuffer::AddPatternRecognizer(const std::string_view regexString) { ++_currentPatternId; _IdsAndPatterns.emplace(std::make_pair(_currentPatternId, regexString)); diff --git a/src/buffer/out/textBuffer.hpp b/src/buffer/out/textBuffer.hpp index 67ebfea17d7..452dfb264ff 100644 --- a/src/buffer/out/textBuffer.hpp +++ b/src/buffer/out/textBuffer.hpp @@ -60,8 +60,6 @@ filling in the last row, and updating the screen. #include "../renderer/inc/IRenderTarget.hpp" -#include - class TextBuffer final { public: @@ -184,7 +182,7 @@ class TextBuffer final const std::optional lastCharacterViewport, std::optional> positionInfo); - const size_t AddPatternRecognizer(const std::string regexString); + const size_t AddPatternRecognizer(const std::string_view regexString); const std::vector> UpdatePatterns(const size_t firstRow, const size_t lastRow) const; private: diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 05b7e13ddf4..463243ca2e0 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -412,9 +412,9 @@ bool Terminal::IsTrackingMouseInput() const noexcept } // Method Description: -// - If the clicked text is a hyperlink, open it +// - Given a coord, get the URI at that location // Arguments: -// - The position of the clicked text +// - The position std::wstring Terminal::GetHyperlinkAtPosition(const COORD position) { auto attr = _buffer->GetCellDataAt(_ConvertToBufferCell(position))->TextAttr(); @@ -423,6 +423,26 @@ std::wstring Terminal::GetHyperlinkAtPosition(const COORD position) auto uri = _buffer->GetHyperlinkUriFromId(attr.GetHyperlinkId()); return uri; } + // also look through our known pattern locations + for (auto pattern : _patternsAndLocations) + { + if (std::get<0>(pattern) == _hyperlinkPatternId) + { + const auto start = std::get<1>(pattern); + const auto end = std::get<2>(pattern); + if (_IsLocationWithinCoordinates(position, start, end)) + { + std::wstring uri; + const auto startIter = _buffer->GetCellDataAt(_ConvertToBufferCell(start)); + const auto endIter = _buffer->GetCellDataAt(_ConvertToBufferCell(end)); + for (auto iter = startIter; iter != endIter; ++iter) + { + uri += iter->Chars(); + } + return uri; + } + } + } return {}; } From 8b95bd9719f5ee335555723cc5ac7fe0a218a53f Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Mon, 28 Sep 2020 15:05:39 -0400 Subject: [PATCH 08/48] interval trees --- src/buffer/out/textBuffer.cpp | 17 +- src/buffer/out/textBuffer.hpp | 2 +- src/cascadia/TerminalCore/Terminal.cpp | 65 ++------ src/cascadia/TerminalCore/Terminal.hpp | 5 +- .../TerminalCore/terminalrenderdata.cpp | 8 +- src/inc/til.h | 1 + src/inc/til/interval_tree.h | 147 ++++++++++++++++++ 7 files changed, 173 insertions(+), 72 deletions(-) create mode 100644 src/inc/til/interval_tree.h diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index 5b22cfe15fe..0d8b66f8613 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -2379,9 +2379,11 @@ const size_t TextBuffer::AddPatternRecognizer(const std::string_view regexString // tuple<0>: ID of the pattern // tuple<1>: start coordinate of the pattern // tuple<2>: end coordinate of the pattern -const std::vector> TextBuffer::UpdatePatterns(const size_t firstRow, const size_t lastRow) const + +til::IntervalTree::ITNode* TextBuffer::UpdatePatterns(const size_t firstRow, const size_t lastRow) const { - std::vector> result; + til::IntervalTree tree; + til::IntervalTree::ITNode* result = NULL; std::wstring concatAll; const auto rowSize = GetRowByOffset(0).size(); @@ -2396,17 +2398,14 @@ const std::vector> TextBuffer::UpdatePatterns(c concatAll += row.GetCharRow().GetText(); } - // convert the string into something the regex iterator can work with - const auto constAll = til::u16u8(concatAll); - // for each pattern we know of, iterate through the string for (auto idAndPattern : _IdsAndPatterns) { - std::regex regexObj{ idAndPattern.second }; + std::wregex regexObj{ til::u8u16(idAndPattern.second) }; // search through the run with our regex object - auto words_begin = std::sregex_iterator(constAll.begin(), constAll.end(), regexObj); - auto words_end = std::sregex_iterator(); + auto words_begin = std::wsregex_iterator(concatAll.begin(), concatAll.end(), regexObj); + auto words_end = std::wsregex_iterator(); size_t lenUpToThis = 0; for (auto i = words_begin; i != words_end; ++i) @@ -2432,7 +2431,7 @@ const std::vector> TextBuffer::UpdatePatterns(c const auto endCol = gsl::narrow(end % rowSize); const COORD startCoord{ startCol, startRow }; const COORD endCoord{ endCol, endRow }; - result.push_back(std::make_tuple(idAndPattern.first, startCoord, endCoord)); + result = tree.insert(result, til::IntervalTree::Interval{ startCoord, endCoord }, idAndPattern.first); } } return result; diff --git a/src/buffer/out/textBuffer.hpp b/src/buffer/out/textBuffer.hpp index 452dfb264ff..4079d81bd0b 100644 --- a/src/buffer/out/textBuffer.hpp +++ b/src/buffer/out/textBuffer.hpp @@ -183,7 +183,7 @@ class TextBuffer final std::optional> positionInfo); const size_t AddPatternRecognizer(const std::string_view regexString); - const std::vector> UpdatePatterns(const size_t firstRow, const size_t lastRow) const; + til::IntervalTree::ITNode* UpdatePatterns(const size_t firstRow, const size_t lastRow) const; private: void _UpdateSize(); diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 463243ca2e0..e1d47959395 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -424,24 +424,20 @@ std::wstring Terminal::GetHyperlinkAtPosition(const COORD position) return uri; } // also look through our known pattern locations - for (auto pattern : _patternsAndLocations) + const auto found = _tree.overlapSearch(_patternsAndLocations, til::IntervalTree::Interval{ position, position }); + if (found != NULL && found->patternId == _hyperlinkPatternId) { - if (std::get<0>(pattern) == _hyperlinkPatternId) + const auto start = found->i->low; + const auto end = found->i->high; + std::wstring uri; + + const auto startIter = _buffer->GetCellDataAt(_ConvertToBufferCell(start)); + const auto endIter = _buffer->GetCellDataAt(_ConvertToBufferCell(end)); + for (auto iter = startIter; iter != endIter; ++iter) { - const auto start = std::get<1>(pattern); - const auto end = std::get<2>(pattern); - if (_IsLocationWithinCoordinates(position, start, end)) - { - std::wstring uri; - const auto startIter = _buffer->GetCellDataAt(_ConvertToBufferCell(start)); - const auto endIter = _buffer->GetCellDataAt(_ConvertToBufferCell(end)); - for (auto iter = startIter; iter != endIter; ++iter) - { - uri += iter->Chars(); - } - return uri; - } + uri += iter->Chars(); } + return uri; } return {}; } @@ -983,45 +979,6 @@ void Terminal::_NotifyTerminalCursorPositionChanged() noexcept } } -// Method Description: -// - Implements the logic to determine if a location is in a given region -// Arguments: -// - The location -// - The start and end coordinates of the region -// Return value: -// - True if the location is within those coordinates, false otherwise -bool Microsoft::Terminal::Core::Terminal::_IsLocationWithinCoordinates(const COORD location, const COORD first, const COORD second) const noexcept -{ - if (first.Y == second.Y) - { - const auto sameRow = location.Y == first.Y; - const auto inRange = (first.X <= location.X && location.X < second.X); - return (sameRow && inRange); - } - else - { - // check first row - if (location.Y == first.Y && (first.X <= location.X)) - { - return true; - } - // check rows in between first row and last row - for (auto curRow = first.Y + 1; curRow < second.Y; ++curRow) - { - if (location.Y == curRow) - { - return true; - } - } - // check last row - if (location.Y == second.Y && location.X < second.X) - { - return true; - } - } - return false; -} - void Terminal::SetWriteInputCallback(std::function pfn) noexcept { _pfnWriteInput.swap(pfn); diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index 2646ad59eae..973cad418f3 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -233,7 +233,8 @@ class Microsoft::Terminal::Core::Terminal final : bool _suppressApplicationTitle; size_t _hyperlinkPatternId; - std::vector> _patternsAndLocations; + til::IntervalTree _tree; + til::IntervalTree::ITNode* _patternsAndLocations; #pragma region Text Selection // a selection is represented as a range between two COORDs (start and end) @@ -308,8 +309,6 @@ class Microsoft::Terminal::Core::Terminal final : void _NotifyTerminalCursorPositionChanged() noexcept; - bool _IsLocationWithinCoordinates(const COORD location, const COORD first, const COORD second) const noexcept; - #pragma region TextSelection // These methods are defined in TerminalSelection.cpp std::vector _GetSelectionRects() const noexcept; diff --git a/src/cascadia/TerminalCore/terminalrenderdata.cpp b/src/cascadia/TerminalCore/terminalrenderdata.cpp index 3053bf2a3de..ed7bebc939c 100644 --- a/src/cascadia/TerminalCore/terminalrenderdata.cpp +++ b/src/cascadia/TerminalCore/terminalrenderdata.cpp @@ -139,12 +139,10 @@ const std::wstring Microsoft::Terminal::Core::Terminal::GetHyperlinkCustomId(uin // - The pattern ID of the location const size_t Microsoft::Terminal::Core::Terminal::GetPatternId(const COORD location) const noexcept { - for (auto found : _patternsAndLocations) + const auto found = _tree.overlapSearch(_patternsAndLocations, til::IntervalTree::Interval{ location, location }); + if (found != NULL) { - if (_IsLocationWithinCoordinates(location, std::get<1>(found), std::get<2>(found))) - { - return std::get<0>(found); - } + return found->patternId; } return 0; } diff --git a/src/inc/til.h b/src/inc/til.h index 1816d2e0736..252935a152e 100644 --- a/src/inc/til.h +++ b/src/inc/til.h @@ -18,6 +18,7 @@ #include "til/spsc.h" #include "til/coalesce.h" #include "til/replace.h" +#include "til/interval_tree.h" namespace til // Terminal Implementation Library. Also: "Today I Learned" { diff --git a/src/inc/til/interval_tree.h b/src/inc/til/interval_tree.h new file mode 100644 index 00000000000..ec9e60fe48e --- /dev/null +++ b/src/inc/til/interval_tree.h @@ -0,0 +1,147 @@ +// Code here is taken from https://www.geeksforgeeks.org/interval-tree/ and edited + +#pragma once + +namespace til +{ + class IntervalTree + { + public: + // Structure to represent an interval + struct Interval + { + COORD low, high; + }; + + // Structure to represent a node in Interval Search Tree + struct ITNode + { + Interval* i; + COORD max; + ITNode *left, *right; + size_t patternId; + }; + + // A utility function to create a new Interval Search Tree Node + ITNode* newNode(Interval i, size_t id) + { + ITNode* temp = new ITNode; + temp->i = new Interval(i); + temp->max = i.high; + temp->left = temp->right = NULL; + temp->patternId = id; + return temp; + }; + + // A utility function to insert a new Interval Search Tree Node + // This is similar to BST Insert. Here the low value of interval + // is used to maintain BST property + ITNode* insert(ITNode* root, Interval i, size_t id) + { + // Base case: Tree is empty, new node becomes root + if (root == NULL) + return newNode(i, id); + + // Get low value of interval at root + COORD l = root->i->low; + + // If root's low value is smaller, then new interval goes to + // left subtree + if (lessThan(i.low, l)) + root->left = insert(root->left, i, id); + + // Else, new node goes to right subtree. + else + root->right = insert(root->right, i, id); + + // Update the max value of this ancestor if needed + if (lessThan(root->max, i.high)) + root->max = i.high; + + return root; + } + + // A utility function to check if given two intervals overlap + bool doOverlap(Interval i1, Interval i2) const + { + if (lessThanOrEqual(i1.low, i2.low) && lessThan(i2.high, i1.high)) + return true; + return false; + } + + // The main function that searches a given interval i in a given + // Interval Tree. + ITNode* overlapSearch(ITNode* root, Interval i) const + { + // Base Case, tree is empty + if (root == NULL) + return NULL; + + // If given interval overlaps with root + if (doOverlap(*(root->i), i)) + return root; + + // If left child of root is present and max of left child is + // greater than or equal to given interval, then i may + // overlap with an interval is left subtree + if (root->left != NULL && greaterThanOrEqual(root->left->max, i.high)) + return overlapSearch(root->left, i); + + // Else interval can only overlap with right subtree + return overlapSearch(root->right, i); + } + + private: + bool lessThan(const COORD x, const COORD y) const + { + if (x.Y < y.Y) + { + return true; + } + else if (x.Y == y.Y) + { + return x.X < y.X; + } + return false; + } + + bool lessThanOrEqual(const COORD x, const COORD y) const + { + if (x.Y < y.Y) + { + return true; + } + else if (x.Y == y.Y) + { + return x.X <= y.X; + } + return false; + } + + bool greaterThan(const COORD x, const COORD y) const + { + if (x.Y > y.Y) + { + return true; + } + else if (x.Y == y.Y) + { + return x.X > y.X; + } + return false; + } + + bool greaterThanOrEqual(const COORD x, const COORD y) const + { + if (x.Y > y.Y) + { + return true; + } + else if (x.Y == y.Y) + { + return x.X >= y.X; + } + return false; + } + }; +} From d1b2db37564ef43a6ee6b87924d70869c620ee21 Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Mon, 28 Sep 2020 15:15:21 -0400 Subject: [PATCH 09/48] spell --- .github/actions/spell-check/dictionary/apis.txt | 1 + src/inc/til/interval_tree.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/actions/spell-check/dictionary/apis.txt b/.github/actions/spell-check/dictionary/apis.txt index 1ced8c34a83..37e40202f15 100644 --- a/.github/actions/spell-check/dictionary/apis.txt +++ b/.github/actions/spell-check/dictionary/apis.txt @@ -52,5 +52,6 @@ tmp tx userenv wcstoui +wsregex XDocument XElement diff --git a/src/inc/til/interval_tree.h b/src/inc/til/interval_tree.h index ec9e60fe48e..993c1e80a60 100644 --- a/src/inc/til/interval_tree.h +++ b/src/inc/til/interval_tree.h @@ -1,4 +1,4 @@ -// Code here is taken from https://www.geeksforgeeks.org/interval-tree/ and edited +// Code here is taken from the Geeks for Geeks website and edited #pragma once From 89b3c5a23818bf0c108e5d2632a08d95f024e235 Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Tue, 29 Sep 2020 17:06:26 -0400 Subject: [PATCH 10/48] using legal interval tree implementation --- oss/interval_tree/IntervalTree.h | 418 ++++++++++++++++++ oss/interval_tree/LICENSE | 19 + oss/interval_tree/MAINTAINER_README.md | 17 + oss/interval_tree/cgmanifest.json | 13 + src/buffer/out/textBuffer.cpp | 25 +- src/buffer/out/textBuffer.hpp | 2 +- src/cascadia/TerminalCore/Terminal.cpp | 34 +- src/cascadia/TerminalCore/Terminal.hpp | 4 +- .../TerminalCore/terminalrenderdata.cpp | 14 +- src/common.build.pre.props | 2 +- src/inc/LibraryIncludes.h | 3 + src/inc/til.h | 1 - src/inc/til/interval_tree.h | 147 ------ 13 files changed, 514 insertions(+), 185 deletions(-) create mode 100644 oss/interval_tree/IntervalTree.h create mode 100644 oss/interval_tree/LICENSE create mode 100644 oss/interval_tree/MAINTAINER_README.md create mode 100644 oss/interval_tree/cgmanifest.json delete mode 100644 src/inc/til/interval_tree.h diff --git a/oss/interval_tree/IntervalTree.h b/oss/interval_tree/IntervalTree.h new file mode 100644 index 00000000000..d0f2ee44b12 --- /dev/null +++ b/oss/interval_tree/IntervalTree.h @@ -0,0 +1,418 @@ +#ifndef __INTERVAL_TREE_H +#define __INTERVAL_TREE_H + +#include +#include +#include +#include +#include + +#ifdef USE_INTERVAL_TREE_NAMESPACE +namespace interval_tree +{ +#endif + template + class Interval + { + public: + Scalar start; + Scalar stop; + Value value; + Interval(const Scalar& s, const Scalar& e, const Value& v) : + start(std::min(s, e)), stop(std::max(s, e)), value(v) + { + } + }; + + template + Value intervalStart(const Interval& i) + { + return i.start; + } + + template + Value intervalStop(const Interval& i) + { + return i.stop; + } + + template + std::ostream& operator<<(std::ostream& out, const Interval& i) + { + out << "Interval(" << i.start << ", " << i.stop << "): " << i.value; + return out; + } + + template + class IntervalTree + { + public: + typedef Interval interval; + typedef std::vector interval_vector; + + struct IntervalStartCmp + { + bool operator()(const interval& a, const interval& b) + { + return a.start < b.start; + } + }; + + struct IntervalStopCmp + { + bool operator()(const interval& a, const interval& b) + { + return a.stop < b.stop; + } + }; + + IntervalTree() : + left(nullptr), right(nullptr), center(0) + { + } + + ~IntervalTree() = default; + + std::unique_ptr clone() const + { + return std::unique_ptr(new IntervalTree(*this)); + } + + IntervalTree(const IntervalTree& other) : + intervals(other.intervals), + left(other.left ? other.left->clone() : nullptr), + right(other.right ? other.right->clone() : nullptr), + center(other.center) + { + } + + IntervalTree& operator=(IntervalTree&&) = default; + IntervalTree(IntervalTree&&) = default; + + IntervalTree& operator=(const IntervalTree& other) + { + center = other.center; + intervals = other.intervals; + left = other.left ? other.left->clone() : nullptr; + right = other.right ? other.right->clone() : nullptr; + return *this; + } + + IntervalTree( + interval_vector&& ivals, + std::size_t depth = 16, + std::size_t minbucket = 64, + std::size_t maxbucket = 512, + Scalar leftextent = 0, + Scalar rightextent = 0) : + left(nullptr), right(nullptr) + { + --depth; + const auto minmaxStop = std::minmax_element(ivals.begin(), ivals.end(), IntervalStopCmp()); + const auto minmaxStart = std::minmax_element(ivals.begin(), ivals.end(), IntervalStartCmp()); + if (!ivals.empty()) + { + center = (minmaxStart.first->start + minmaxStop.second->stop) / 2; + } + if (leftextent == 0 && rightextent == 0) + { + // sort intervals by start + std::sort(ivals.begin(), ivals.end(), IntervalStartCmp()); + } + else + { + assert(std::is_sorted(ivals.begin(), ivals.end(), IntervalStartCmp())); + } + if (depth == 0 || (ivals.size() < minbucket && ivals.size() < maxbucket)) + { + std::sort(ivals.begin(), ivals.end(), IntervalStartCmp()); + intervals = std::move(ivals); + assert(is_valid().first); + return; + } + else + { + Scalar leftp = 0; + Scalar rightp = 0; + + if (leftextent || rightextent) + { + leftp = leftextent; + rightp = rightextent; + } + else + { + leftp = ivals.front().start; + rightp = std::max_element(ivals.begin(), ivals.end(), IntervalStopCmp())->stop; + } + + interval_vector lefts; + interval_vector rights; + + for (typename interval_vector::const_iterator i = ivals.begin(); + i != ivals.end(); + ++i) + { + const interval& interval = *i; + if (interval.stop < center) + { + lefts.push_back(interval); + } + else if (interval.start > center) + { + rights.push_back(interval); + } + else + { + assert(interval.start <= center); + assert(center <= interval.stop); + intervals.push_back(interval); + } + } + + if (!lefts.empty()) + { + left.reset(new IntervalTree(std::move(lefts), + depth, + minbucket, + maxbucket, + leftp, + center)); + } + if (!rights.empty()) + { + right.reset(new IntervalTree(std::move(rights), + depth, + minbucket, + maxbucket, + center, + rightp)); + } + } + assert(is_valid().first); + } + + // Call f on all intervals near the range [start, stop]: + template + void visit_near(const Scalar& start, const Scalar& stop, UnaryFunction f) const + { + if (!intervals.empty() && !(stop < intervals.front().start)) + { + for (auto& i : intervals) + { + f(i); + } + } + if (left && start <= center) + { + left->visit_near(start, stop, f); + } + if (right && stop >= center) + { + right->visit_near(start, stop, f); + } + } + + // Call f on all intervals crossing pos + template + void visit_overlapping(const Scalar& pos, UnaryFunction f) const + { + visit_overlapping(pos, pos, f); + } + + // Call f on all intervals overlapping [start, stop] + template + void visit_overlapping(const Scalar& start, const Scalar& stop, UnaryFunction f) const + { + auto filterF = [&](const interval& interval) { + if (interval.stop >= start && interval.start <= stop) + { + // Only apply f if overlapping + f(interval); + } + }; + visit_near(start, stop, filterF); + } + + // Call f on all intervals contained within [start, stop] + template + void visit_contained(const Scalar& start, const Scalar& stop, UnaryFunction f) const + { + auto filterF = [&](const interval& interval) { + if (start <= interval.start && interval.stop <= stop) + { + f(interval); + } + }; + visit_near(start, stop, filterF); + } + + interval_vector findOverlapping(const Scalar& start, const Scalar& stop) const + { + interval_vector result; + visit_overlapping(start, stop, [&](const interval& interval) { + result.emplace_back(interval); + }); + return result; + } + + interval_vector findContained(const Scalar& start, const Scalar& stop) const + { + interval_vector result; + visit_contained(start, stop, [&](const interval& interval) { + result.push_back(interval); + }); + return result; + } + bool empty() const + { + if (left && !left->empty()) + { + return false; + } + if (!intervals.empty()) + { + return false; + } + if (right && !right->empty()) + { + return false; + } + return true; + } + + template + void visit_all(UnaryFunction f) const + { + if (left) + { + left->visit_all(f); + } + std::for_each(intervals.begin(), intervals.end(), f); + if (right) + { + right->visit_all(f); + } + } + + std::pair extentBruitForce() const + { + struct Extent + { + std::pair x = { std::numeric_limits::max(), + std::numeric_limits::min() }; + void operator()(const interval& interval) + { + x.first = std::min(x.first, interval.start); + x.second = std::max(x.second, interval.stop); + } + }; + Extent extent; + + visit_all([&](const interval& interval) { extent(interval); }); + return extent.x; + } + + // Check all constraints. + // If first is false, second is invalid. + std::pair> is_valid() const + { + const auto minmaxStop = std::minmax_element(intervals.begin(), intervals.end(), IntervalStopCmp()); + const auto minmaxStart = std::minmax_element(intervals.begin(), intervals.end(), IntervalStartCmp()); + + std::pair> result = { true, { std::numeric_limits::max(), std::numeric_limits::min() } }; + if (!intervals.empty()) + { + result.second.first = std::min(result.second.first, minmaxStart.first->start); + result.second.second = std::min(result.second.second, minmaxStop.second->stop); + } + if (left) + { + auto valid = left->is_valid(); + result.first &= valid.first; + result.second.first = std::min(result.second.first, valid.second.first); + result.second.second = std::min(result.second.second, valid.second.second); + if (!result.first) + { + return result; + } + if (valid.second.second >= center) + { + result.first = false; + return result; + } + } + if (right) + { + auto valid = right->is_valid(); + result.first &= valid.first; + result.second.first = std::min(result.second.first, valid.second.first); + result.second.second = std::min(result.second.second, valid.second.second); + if (!result.first) + { + return result; + } + if (valid.second.first <= center) + { + result.first = false; + return result; + } + } + if (!std::is_sorted(intervals.begin(), intervals.end(), IntervalStartCmp())) + { + result.first = false; + } + return result; + } + + friend std::ostream& operator<<(std::ostream& os, const IntervalTree& itree) + { + return writeOut(os, itree); + } + + friend std::ostream& writeOut(std::ostream& os, const IntervalTree& itree, std::size_t depth = 0) + { + auto pad = [&]() { for (std::size_t i = 0; i != depth; ++i) { os << ' '; } }; + pad(); + os << "center: " << itree.center << '\n'; + for (const interval& inter : itree.intervals) + { + pad(); + os << inter << '\n'; + } + if (itree.left) + { + pad(); + os << "left:\n"; + writeOut(os, *itree.left, depth + 1); + } + else + { + pad(); + os << "left: nullptr\n"; + } + if (itree.right) + { + pad(); + os << "right:\n"; + writeOut(os, *itree.right, depth + 1); + } + else + { + pad(); + os << "right: nullptr\n"; + } + return os; + } + + private: + interval_vector intervals; + std::unique_ptr left; + std::unique_ptr right; + Scalar center; + }; +#ifdef USE_INTERVAL_TREE_NAMESPACE +} +#endif + +#endif diff --git a/oss/interval_tree/LICENSE b/oss/interval_tree/LICENSE new file mode 100644 index 00000000000..8d33acbf125 --- /dev/null +++ b/oss/interval_tree/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2011 Erik Garrison + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/oss/interval_tree/MAINTAINER_README.md b/oss/interval_tree/MAINTAINER_README.md new file mode 100644 index 00000000000..997f8a6a0d8 --- /dev/null +++ b/oss/interval_tree/MAINTAINER_README.md @@ -0,0 +1,17 @@ +### Notes for Future Maintainers + +This was originally imported by @miniksa in January 2020. + +The provenance information (where it came from and which commit) is stored in the file `cgmanifest.json` in the same directory as this readme. +Please update the provenance information in that file when ingesting an updated version of the dependent library. +That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropiate governance standards. + +## What should be done to update this in the future? + +1. Go to ekg/intervaltreerepository on GitHub. +2. Take the file IntervalTree.h wholesale and drop it into the directory here. +3. Don't change anything about it. +4. Validate that the license in the root of the repository didn't change and update it if so. It is sitting in the same directory as this readme. + If it changed dramatically, ensure that it is still compatible with our license scheme. Also update the NOTICE file in the root of our repository to declare the third-party usage. +5. Submit the pull. + diff --git a/oss/interval_tree/cgmanifest.json b/oss/interval_tree/cgmanifest.json new file mode 100644 index 00000000000..b6c5b21999e --- /dev/null +++ b/oss/interval_tree/cgmanifest.json @@ -0,0 +1,13 @@ +{"Registrations":[ + { + "component": { + "type": "git", + "git": { + "repositoryUrl": "https://github.com/ekg/intervaltree", + "commitHash": "b90527f9e6d51cd36ecbb50429e4524d3a418ea5" + } + } + } + ], + "Version": 1 +} diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index 0d8b66f8613..a4b44f10df6 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -2375,15 +2375,11 @@ const size_t TextBuffer::AddPatternRecognizer(const std::string_view regexString // - The firstRow to start searching from // - The lastRow to search // Return value: -// - A vector containing 3-element tuples where -// tuple<0>: ID of the pattern -// tuple<1>: start coordinate of the pattern -// tuple<2>: end coordinate of the pattern - -til::IntervalTree::ITNode* TextBuffer::UpdatePatterns(const size_t firstRow, const size_t lastRow) const +// - An interval tree containing the patterns found +interval_tree::IntervalTree TextBuffer::UpdatePatterns(const size_t firstRow, const size_t lastRow) const { - til::IntervalTree tree; - til::IntervalTree::ITNode* result = NULL; + typedef interval_tree::IntervalTree ITree; + ITree::interval_vector intervals; std::wstring concatAll; const auto rowSize = GetRowByOffset(0).size(); @@ -2421,18 +2417,13 @@ til::IntervalTree::ITNode* TextBuffer::UpdatePatterns(const size_t firstRow, con const auto end = start + i->str().size(); lenUpToThis = end; - // store the locations as (col, row) coordinates - // NOTE: these are VIEWPORT coordinates, not buffer coordinates + // store the intervals + // NOTE: these intervals are relative to the VIEWPORT not the buffer // Keeping these as viewport coordinates for now because its the renderer // that actually uses these coordinates and the renderer works in viewport coords - const auto startRow = gsl::narrow(start / rowSize); - const auto startCol = gsl::narrow(start % rowSize); - const auto endRow = gsl::narrow(end / rowSize); - const auto endCol = gsl::narrow(end % rowSize); - const COORD startCoord{ startCol, startRow }; - const COORD endCoord{ endCol, endRow }; - result = tree.insert(result, til::IntervalTree::Interval{ startCoord, endCoord }, idAndPattern.first); + intervals.push_back(ITree::interval(start, end, idAndPattern.first)); } } + ITree result(std::move(intervals)); return result; } diff --git a/src/buffer/out/textBuffer.hpp b/src/buffer/out/textBuffer.hpp index 4079d81bd0b..e03b83fd2fb 100644 --- a/src/buffer/out/textBuffer.hpp +++ b/src/buffer/out/textBuffer.hpp @@ -183,7 +183,7 @@ class TextBuffer final std::optional> positionInfo); const size_t AddPatternRecognizer(const std::string_view regexString); - til::IntervalTree::ITNode* UpdatePatterns(const size_t firstRow, const size_t lastRow) const; + interval_tree::IntervalTree UpdatePatterns(const size_t firstRow, const size_t lastRow) const; private: void _UpdateSize(); diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index e1d47959395..97f707a0418 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -424,20 +424,30 @@ std::wstring Terminal::GetHyperlinkAtPosition(const COORD position) return uri; } // also look through our known pattern locations - const auto found = _tree.overlapSearch(_patternsAndLocations, til::IntervalTree::Interval{ position, position }); - if (found != NULL && found->patternId == _hyperlinkPatternId) + const auto absLoc = (_buffer->GetRowByOffset(0).size() * position.Y) + position.X; + const auto results = _patternIntervalTree.findOverlapping(absLoc + 1, absLoc); + if (results.size() > 0) { - const auto start = found->i->low; - const auto end = found->i->high; - std::wstring uri; - - const auto startIter = _buffer->GetCellDataAt(_ConvertToBufferCell(start)); - const auto endIter = _buffer->GetCellDataAt(_ConvertToBufferCell(end)); - for (auto iter = startIter; iter != endIter; ++iter) + const auto rowSize = _buffer->GetRowByOffset(0).size(); + for (auto result : results) { - uri += iter->Chars(); + if (result.value == _hyperlinkPatternId) + { + const auto start = results.at(0).start; + const auto end = results.at(0).stop; + COORD startCoord{ gsl::narrow(start % rowSize), gsl::narrow(start / rowSize) }; + COORD endCoord{ gsl::narrow(end % rowSize), gsl::narrow(end / rowSize) }; + std::wstring uri; + + const auto startIter = _buffer->GetCellDataAt(_ConvertToBufferCell(startCoord)); + const auto endIter = _buffer->GetCellDataAt(_ConvertToBufferCell(endCoord)); + for (auto iter = startIter; iter != endIter; ++iter) + { + uri += iter->Chars(); + } + return uri; + } } - return uri; } return {}; } @@ -1057,7 +1067,7 @@ bool Terminal::IsCursorBlinkingAllowed() const noexcept // region changes (for example by text entering the buffer or scrolling) void Microsoft::Terminal::Core::Terminal::UpdatePatterns() noexcept { - _patternsAndLocations = _buffer->UpdatePatterns(_VisibleStartIndex(), _VisibleEndIndex()); + _patternIntervalTree = _buffer->UpdatePatterns(_VisibleStartIndex(), _VisibleEndIndex()); } const std::optional Terminal::GetTabColor() const noexcept diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index 973cad418f3..71e6f12e992 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -233,8 +233,6 @@ class Microsoft::Terminal::Core::Terminal final : bool _suppressApplicationTitle; size_t _hyperlinkPatternId; - til::IntervalTree _tree; - til::IntervalTree::ITNode* _patternsAndLocations; #pragma region Text Selection // a selection is represented as a range between two COORDs (start and end) @@ -277,6 +275,8 @@ class Microsoft::Terminal::Core::Terminal final : // underneath them, while others would prefer to anchor it in place. // Either way, we should make this behavior controlled by a setting. + interval_tree::IntervalTree _patternIntervalTree; + // Since virtual keys are non-zero, you assume that this field is empty/invalid if it is. struct KeyEventCodes { diff --git a/src/cascadia/TerminalCore/terminalrenderdata.cpp b/src/cascadia/TerminalCore/terminalrenderdata.cpp index ed7bebc939c..8401a63776e 100644 --- a/src/cascadia/TerminalCore/terminalrenderdata.cpp +++ b/src/cascadia/TerminalCore/terminalrenderdata.cpp @@ -139,12 +139,18 @@ const std::wstring Microsoft::Terminal::Core::Terminal::GetHyperlinkCustomId(uin // - The pattern ID of the location const size_t Microsoft::Terminal::Core::Terminal::GetPatternId(const COORD location) const noexcept { - const auto found = _tree.overlapSearch(_patternsAndLocations, til::IntervalTree::Interval{ location, location }); - if (found != NULL) + // Convert the location into its 1-d location because the interval tree stores locations that way + const auto absLoc = (_buffer->GetRowByOffset(0).size() * location.Y) + location.X; + const auto results = _patternIntervalTree.findOverlapping(absLoc + 1, absLoc); + if (results.size() == 0) { - return found->patternId; + return 0; + } + else + { + // At some point, this should be updated to return a vector of IDs + return results.at(0).value; } - return 0; } std::vector Terminal::GetSelectionRects() noexcept diff --git a/src/common.build.pre.props b/src/common.build.pre.props index fc209613749..19a9f43cd59 100644 --- a/src/common.build.pre.props +++ b/src/common.build.pre.props @@ -83,7 +83,7 @@ precomp.h $(IntDir)$(TargetName).pdb ProgramDatabase - $(SolutionDir)\src\inc;$(SolutionDir)\dep;$(SolutionDir)\dep\Console;$(SolutionDir)\dep\Win32K;$(SolutionDir)\dep\gsl\include;$(SolutionDir)\dep\wil\include;$(SolutionDir)\oss\chromium;$(SolutionDir)\oss\fmt\include;$(SolutionDir)\oss\dynamic_bitset;$(SolutionDir)\oss\libpopcnt;%(AdditionalIncludeDirectories); + $(SolutionDir)\src\inc;$(SolutionDir)\dep;$(SolutionDir)\dep\Console;$(SolutionDir)\dep\Win32K;$(SolutionDir)\dep\gsl\include;$(SolutionDir)\dep\wil\include;$(SolutionDir)\oss\chromium;$(SolutionDir)\oss\fmt\include;$(SolutionDir)\oss\dynamic_bitset;$(SolutionDir)\oss\libpopcnt;$(SolutionDir)\oss\interval_tree;%(AdditionalIncludeDirectories); true false false diff --git a/src/inc/LibraryIncludes.h b/src/inc/LibraryIncludes.h index 4f7721b89d3..398d690fed0 100644 --- a/src/inc/LibraryIncludes.h +++ b/src/inc/LibraryIncludes.h @@ -93,6 +93,9 @@ // {fmt}, a C++20-compatible formatting library #include +#define USE_INTERVAL_TREE_NAMESPACE +#include + // SAL #include diff --git a/src/inc/til.h b/src/inc/til.h index 252935a152e..1816d2e0736 100644 --- a/src/inc/til.h +++ b/src/inc/til.h @@ -18,7 +18,6 @@ #include "til/spsc.h" #include "til/coalesce.h" #include "til/replace.h" -#include "til/interval_tree.h" namespace til // Terminal Implementation Library. Also: "Today I Learned" { diff --git a/src/inc/til/interval_tree.h b/src/inc/til/interval_tree.h deleted file mode 100644 index 993c1e80a60..00000000000 --- a/src/inc/til/interval_tree.h +++ /dev/null @@ -1,147 +0,0 @@ -// Code here is taken from the Geeks for Geeks website and edited - -#pragma once - -namespace til -{ - class IntervalTree - { - public: - // Structure to represent an interval - struct Interval - { - COORD low, high; - }; - - // Structure to represent a node in Interval Search Tree - struct ITNode - { - Interval* i; - COORD max; - ITNode *left, *right; - size_t patternId; - }; - - // A utility function to create a new Interval Search Tree Node - ITNode* newNode(Interval i, size_t id) - { - ITNode* temp = new ITNode; - temp->i = new Interval(i); - temp->max = i.high; - temp->left = temp->right = NULL; - temp->patternId = id; - return temp; - }; - - // A utility function to insert a new Interval Search Tree Node - // This is similar to BST Insert. Here the low value of interval - // is used to maintain BST property - ITNode* insert(ITNode* root, Interval i, size_t id) - { - // Base case: Tree is empty, new node becomes root - if (root == NULL) - return newNode(i, id); - - // Get low value of interval at root - COORD l = root->i->low; - - // If root's low value is smaller, then new interval goes to - // left subtree - if (lessThan(i.low, l)) - root->left = insert(root->left, i, id); - - // Else, new node goes to right subtree. - else - root->right = insert(root->right, i, id); - - // Update the max value of this ancestor if needed - if (lessThan(root->max, i.high)) - root->max = i.high; - - return root; - } - - // A utility function to check if given two intervals overlap - bool doOverlap(Interval i1, Interval i2) const - { - if (lessThanOrEqual(i1.low, i2.low) && lessThan(i2.high, i1.high)) - return true; - return false; - } - - // The main function that searches a given interval i in a given - // Interval Tree. - ITNode* overlapSearch(ITNode* root, Interval i) const - { - // Base Case, tree is empty - if (root == NULL) - return NULL; - - // If given interval overlaps with root - if (doOverlap(*(root->i), i)) - return root; - - // If left child of root is present and max of left child is - // greater than or equal to given interval, then i may - // overlap with an interval is left subtree - if (root->left != NULL && greaterThanOrEqual(root->left->max, i.high)) - return overlapSearch(root->left, i); - - // Else interval can only overlap with right subtree - return overlapSearch(root->right, i); - } - - private: - bool lessThan(const COORD x, const COORD y) const - { - if (x.Y < y.Y) - { - return true; - } - else if (x.Y == y.Y) - { - return x.X < y.X; - } - return false; - } - - bool lessThanOrEqual(const COORD x, const COORD y) const - { - if (x.Y < y.Y) - { - return true; - } - else if (x.Y == y.Y) - { - return x.X <= y.X; - } - return false; - } - - bool greaterThan(const COORD x, const COORD y) const - { - if (x.Y > y.Y) - { - return true; - } - else if (x.Y == y.Y) - { - return x.X > y.X; - } - return false; - } - - bool greaterThanOrEqual(const COORD x, const COORD y) const - { - if (x.Y > y.Y) - { - return true; - } - else if (x.Y == y.Y) - { - return x.X >= y.X; - } - return false; - } - }; -} From 1297c152431ad15969922da8a4904d99edaef365 Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Tue, 29 Sep 2020 17:12:29 -0400 Subject: [PATCH 11/48] spelling and date --- oss/interval_tree/MAINTAINER_README.md | 2 +- src/buffer/out/textBuffer.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/oss/interval_tree/MAINTAINER_README.md b/oss/interval_tree/MAINTAINER_README.md index 997f8a6a0d8..7b6422d5227 100644 --- a/oss/interval_tree/MAINTAINER_README.md +++ b/oss/interval_tree/MAINTAINER_README.md @@ -1,6 +1,6 @@ ### Notes for Future Maintainers -This was originally imported by @miniksa in January 2020. +This was originally imported by @PankajBhojwani in September 2020. The provenance information (where it came from and which commit) is stored in the file `cgmanifest.json` in the same directory as this readme. Please update the provenance information in that file when ingesting an updated version of the dependent library. diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index a4b44f10df6..495a685f193 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -2378,8 +2378,8 @@ const size_t TextBuffer::AddPatternRecognizer(const std::string_view regexString // - An interval tree containing the patterns found interval_tree::IntervalTree TextBuffer::UpdatePatterns(const size_t firstRow, const size_t lastRow) const { - typedef interval_tree::IntervalTree ITree; - ITree::interval_vector intervals; + typedef interval_tree::IntervalTree ThisTree; + ThisTree::interval_vector intervals; std::wstring concatAll; const auto rowSize = GetRowByOffset(0).size(); @@ -2421,9 +2421,9 @@ interval_tree::IntervalTree TextBuffer::UpdatePatterns(const siz // NOTE: these intervals are relative to the VIEWPORT not the buffer // Keeping these as viewport coordinates for now because its the renderer // that actually uses these coordinates and the renderer works in viewport coords - intervals.push_back(ITree::interval(start, end, idAndPattern.first)); + intervals.push_back(ThisTree::interval(start, end, idAndPattern.first)); } } - ITree result(std::move(intervals)); + ThisTree result(std::move(intervals)); return result; } From 68d2c4c9fe56bba61782ad554a8e552cd5d325e3 Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Tue, 29 Sep 2020 17:18:18 -0400 Subject: [PATCH 12/48] license things --- NOTICE.md | 29 +++++++++++++++++++++++++++++ oss/interval_tree/LICENSE | 19 ------------------- 2 files changed, 29 insertions(+), 19 deletions(-) delete mode 100644 oss/interval_tree/LICENSE diff --git a/NOTICE.md b/NOTICE.md index f5b54b42688..f7dd815c0c8 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -218,3 +218,32 @@ of this Software are embedded into a machine-executable object form of such source code, you may redistribute such embedded portions in such object form without including the above copyright and permission notices. ``` + +## interval_tree + +**Source**: https://github.com/ekg/intervaltree + +### License + +``` +Copyright (c) 2011 Erik Garrison + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` diff --git a/oss/interval_tree/LICENSE b/oss/interval_tree/LICENSE deleted file mode 100644 index 8d33acbf125..00000000000 --- a/oss/interval_tree/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2011 Erik Garrison - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file From b3f45d994d59dc5735142c5dbe16abaee5b23b85 Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Tue, 29 Sep 2020 17:24:21 -0400 Subject: [PATCH 13/48] spellllll --- .github/actions/spell-check/dictionary/names.txt | 1 + NOTICE.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/actions/spell-check/dictionary/names.txt b/.github/actions/spell-check/dictionary/names.txt index c7f4c1f2b15..442be459672 100644 --- a/.github/actions/spell-check/dictionary/names.txt +++ b/.github/actions/spell-check/dictionary/names.txt @@ -8,6 +8,7 @@ dhowett Diviness dsafa duhowett +ekg ethanschoonover Firefox Gatta diff --git a/NOTICE.md b/NOTICE.md index f7dd815c0c8..3dec78750c0 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -221,7 +221,7 @@ without including the above copyright and permission notices. ## interval_tree -**Source**: https://github.com/ekg/intervaltree +**Source**: https://github.com/ekg/IntervalTree ### License From c88ab2d87dcddff4a70ada20001421b8e8631d87 Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Tue, 29 Sep 2020 18:08:06 -0400 Subject: [PATCH 14/48] terminal universal --- .../WindowsTerminalUniversal/WindowsTerminalUniversal.vcxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cascadia/WindowsTerminalUniversal/WindowsTerminalUniversal.vcxproj b/src/cascadia/WindowsTerminalUniversal/WindowsTerminalUniversal.vcxproj index 0a749357a56..4d30d0763b3 100644 --- a/src/cascadia/WindowsTerminalUniversal/WindowsTerminalUniversal.vcxproj +++ b/src/cascadia/WindowsTerminalUniversal/WindowsTerminalUniversal.vcxproj @@ -87,7 +87,7 @@ - $(OpenConsoleDir)\src\inc;$(OpenConsoleDir)\dep;$(OpenConsoleDir)\dep\Console;$(OpenConsoleDir)\dep\Win32K;$(OpenConsoleDir)\dep\gsl\include;$(OpenConsoleDir)\dep\wil\include;$(SolutionDir)\oss\chromium;$(SolutionDir)\oss\fmt\include;$(SolutionDir)\oss\dynamic_bitset;$(SolutionDir)\oss\libpopcnt;%(AdditionalIncludeDirectories); + $(OpenConsoleDir)\src\inc;$(OpenConsoleDir)\dep;$(OpenConsoleDir)\dep\Console;$(OpenConsoleDir)\dep\Win32K;$(OpenConsoleDir)\dep\gsl\include;$(OpenConsoleDir)\dep\wil\include;$(SolutionDir)\oss\chromium;$(SolutionDir)\oss\fmt\include;$(SolutionDir)\oss\dynamic_bitset;$(SolutionDir)\oss\libpopcnt;$(SolutionDir)\oss\interval_tree;%(AdditionalIncludeDirectories); gdi32.lib;dwmapi.lib;Shcore.lib;%(AdditionalDependencies) From 4692b9b63c1e00461363e50d9554c42ca3b4a454 Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Wed, 30 Sep 2020 11:01:13 -0400 Subject: [PATCH 15/48] fix comments/some optimizations --- src/buffer/out/textBuffer.cpp | 18 ++++++++---------- src/cascadia/TerminalControl/TermControl.cpp | 2 +- src/cascadia/TerminalCore/Terminal.cpp | 13 +++++++------ .../TerminalCore/terminalrenderdata.cpp | 1 + 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index 495a685f193..b1cfc92b255 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -2386,8 +2386,7 @@ interval_tree::IntervalTree TextBuffer::UpdatePatterns(const siz concatAll.reserve(rowSize * (lastRow - firstRow + 1)); // to deal with text that spans multiple lines, we will first concatenate - // all the text into one string and find the pattern in that string - // later, we convert the locations of the patterns back into (row, col) coords + // all the text into one string and find the patterns in that string for (auto i = firstRow; i <= lastRow; ++i) { auto row = GetRowByOffset(i); @@ -2395,7 +2394,7 @@ interval_tree::IntervalTree TextBuffer::UpdatePatterns(const siz } // for each pattern we know of, iterate through the string - for (auto idAndPattern : _IdsAndPatterns) + for (const auto& idAndPattern : _IdsAndPatterns) { std::wregex regexObj{ til::u8u16(idAndPattern.second) }; @@ -2406,12 +2405,10 @@ interval_tree::IntervalTree TextBuffer::UpdatePatterns(const siz size_t lenUpToThis = 0; for (auto i = words_begin; i != words_end; ++i) { - // record the locations and convert them back to coords + // record the locations - // when we find a match, the prefix is text that is between this - // match and the previous match, and the suffix is the text that - // it between this match and the next match - // we will use that, along with the size of the match, to determine - // the locations + // match and the previous match, so we use the size of the prefix + // along with the size of the match to determine the locations const auto prefixSize = i->prefix().str().size(); const auto start = lenUpToThis + prefixSize; const auto end = start + i->str().size(); @@ -2419,8 +2416,9 @@ interval_tree::IntervalTree TextBuffer::UpdatePatterns(const siz // store the intervals // NOTE: these intervals are relative to the VIEWPORT not the buffer - // Keeping these as viewport coordinates for now because its the renderer - // that actually uses these coordinates and the renderer works in viewport coords + // Keeping these relative to the viewport for now because its the renderer + // that actually uses these locations and the renderer works relative to + // the viewport intervals.push_back(ThisTree::interval(start, end, idAndPattern.first)); } } diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 8c21513762f..3c41b3016ad 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -153,7 +153,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation control->UpdatePatternLocations(); } }, - TsfRedrawInterval, + UpdatePatternLocationsInterval, Dispatcher()); _updateScrollBar = std::make_shared>( diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 97f707a0418..4073fd6ef4e 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -423,18 +423,19 @@ std::wstring Terminal::GetHyperlinkAtPosition(const COORD position) auto uri = _buffer->GetHyperlinkUriFromId(attr.GetHyperlinkId()); return uri; } - // also look through our known pattern locations - const auto absLoc = (_buffer->GetRowByOffset(0).size() * position.Y) + position.X; + // also look through our known pattern locations in our pattern interval tree + // we need to convert to 1-d coordinates because that is how the tree stores them + const auto rowSize = _buffer->GetRowByOffset(0).size(); + const auto absLoc = (rowSize * position.Y) + position.X; const auto results = _patternIntervalTree.findOverlapping(absLoc + 1, absLoc); if (results.size() > 0) { - const auto rowSize = _buffer->GetRowByOffset(0).size(); - for (auto result : results) + for (const auto& result : results) { if (result.value == _hyperlinkPatternId) { - const auto start = results.at(0).start; - const auto end = results.at(0).stop; + const auto start = result.start; + const auto end = result.stop; COORD startCoord{ gsl::narrow(start % rowSize), gsl::narrow(start / rowSize) }; COORD endCoord{ gsl::narrow(end % rowSize), gsl::narrow(end / rowSize) }; std::wstring uri; diff --git a/src/cascadia/TerminalCore/terminalrenderdata.cpp b/src/cascadia/TerminalCore/terminalrenderdata.cpp index 8401a63776e..e4165133b08 100644 --- a/src/cascadia/TerminalCore/terminalrenderdata.cpp +++ b/src/cascadia/TerminalCore/terminalrenderdata.cpp @@ -149,6 +149,7 @@ const size_t Microsoft::Terminal::Core::Terminal::GetPatternId(const COORD locat else { // At some point, this should be updated to return a vector of IDs + // which will represent all the patterns this location is a part of return results.at(0).value; } } From 9f1a5bce8c0e8665ea58ef292a853d87c8287291 Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Wed, 30 Sep 2020 17:26:46 -0400 Subject: [PATCH 16/48] invalidate regions --- src/cascadia/TerminalControl/TermControl.cpp | 4 +- src/cascadia/TerminalCore/Terminal.cpp | 38 +++++++++++++++++++ src/cascadia/TerminalCore/Terminal.hpp | 2 + .../TerminalCore/terminalrenderdata.cpp | 1 + 4 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 3c41b3016ad..31eece83035 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -36,7 +36,7 @@ constexpr const auto ScrollBarUpdateInterval = std::chrono::milliseconds(8); constexpr const auto TsfRedrawInterval = std::chrono::milliseconds(100); // The minimum delay between updating the locations of regex patterns -constexpr const auto UpdatePatternLocationsInterval = std::chrono::milliseconds(50); +constexpr const auto UpdatePatternLocationsInterval = std::chrono::milliseconds(10); DEFINE_ENUM_FLAG_OPERATORS(winrt::Microsoft::Terminal::TerminalControl::CopyFormat); @@ -1613,6 +1613,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation // Make sure selection reflects that immediately. _SetEndSelectionPointAtCursor(point); } + + _updatePatternLocations->Run(); } void TermControl::_ScrollbarChangeHandler(Windows::Foundation::IInspectable const& /*sender*/, diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 4073fd6ef4e..9e98cff5180 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -622,6 +622,41 @@ bool Terminal::SendCharEvent(const wchar_t ch, const WORD scanCode, const Contro return handledDown || handledUp; } +void Microsoft::Terminal::Core::Terminal::_InvalidatePatternTree(interval_tree::IntervalTree& tree) +{ + typedef interval_tree::IntervalTree ThisTree; + const auto rowSize = _buffer->GetRowByOffset(0).size(); + const auto vis = _VisibleStartIndex(); + auto invalidate = [=](const ThisTree::interval& interval) { + COORD startCoord{ gsl::narrow(interval.start % rowSize), gsl::narrow(interval.start / rowSize + vis) }; + COORD endCoord{ gsl::narrow(interval.stop % rowSize), gsl::narrow(interval.stop / rowSize + vis) }; + _InvalidateRegion(startCoord, endCoord); + }; + tree.visit_all(invalidate); +} + +void Microsoft::Terminal::Core::Terminal::_InvalidateRegion(const COORD start, const COORD end) +{ + if (start.Y == end.Y) + { + SMALL_RECT region{ start.X, start.Y, end.X, end.Y }; + _buffer->GetRenderTarget().TriggerRedraw(Viewport::FromInclusive(region)); + } + else + { + const auto rowSize = gsl::narrow(_buffer->GetRowByOffset(0).size()); + SMALL_RECT region{ start.X, start.Y, rowSize - 1, start.Y }; + _buffer->GetRenderTarget().TriggerRedraw(Viewport::FromInclusive(region)); + for (SHORT row = start.Y + 1; row < end.Y; ++row) + { + region = til::rectangle(0, row, rowSize - 1, row); + _buffer->GetRenderTarget().TriggerRedraw(Viewport::FromInclusive(region)); + } + region = til::rectangle(0, end.Y, end.X, end.Y); + _buffer->GetRenderTarget().TriggerRedraw(Viewport::FromInclusive(region)); + } +} + // Method Description: // - Returns the keyboard's scan code for the given virtual key code. // Arguments: @@ -1068,7 +1103,10 @@ bool Terminal::IsCursorBlinkingAllowed() const noexcept // region changes (for example by text entering the buffer or scrolling) void Microsoft::Terminal::Core::Terminal::UpdatePatterns() noexcept { + auto old = _patternIntervalTree; _patternIntervalTree = _buffer->UpdatePatterns(_VisibleStartIndex(), _VisibleEndIndex()); + _InvalidatePatternTree(old); + _InvalidatePatternTree(_patternIntervalTree); } const std::optional Terminal::GetTabColor() const noexcept diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index 71e6f12e992..9f4f4205e4b 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -276,6 +276,8 @@ class Microsoft::Terminal::Core::Terminal final : // Either way, we should make this behavior controlled by a setting. interval_tree::IntervalTree _patternIntervalTree; + void _InvalidatePatternTree(interval_tree::IntervalTree& tree); + void _InvalidateRegion(const COORD start, const COORD end); // Since virtual keys are non-zero, you assume that this field is empty/invalid if it is. struct KeyEventCodes diff --git a/src/cascadia/TerminalCore/terminalrenderdata.cpp b/src/cascadia/TerminalCore/terminalrenderdata.cpp index e4165133b08..771ac9b0bbd 100644 --- a/src/cascadia/TerminalCore/terminalrenderdata.cpp +++ b/src/cascadia/TerminalCore/terminalrenderdata.cpp @@ -152,6 +152,7 @@ const size_t Microsoft::Terminal::Core::Terminal::GetPatternId(const COORD locat // which will represent all the patterns this location is a part of return results.at(0).value; } + return 0; } std::vector Terminal::GetSelectionRects() noexcept From 9d88aa503f9a5a3ab7f8f5ee6b23d2a07047d18f Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Thu, 1 Oct 2020 10:46:46 -0400 Subject: [PATCH 17/48] renaming --- src/buffer/out/textBuffer.cpp | 2 +- src/buffer/out/textBuffer.hpp | 2 +- src/cascadia/TerminalCore/Terminal.cpp | 6 +++--- src/cascadia/TerminalCore/Terminal.hpp | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index b1cfc92b255..6958971a171 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -2376,7 +2376,7 @@ const size_t TextBuffer::AddPatternRecognizer(const std::string_view regexString // - The lastRow to search // Return value: // - An interval tree containing the patterns found -interval_tree::IntervalTree TextBuffer::UpdatePatterns(const size_t firstRow, const size_t lastRow) const +interval_tree::IntervalTree TextBuffer::GetPatterns(const size_t firstRow, const size_t lastRow) const { typedef interval_tree::IntervalTree ThisTree; ThisTree::interval_vector intervals; diff --git a/src/buffer/out/textBuffer.hpp b/src/buffer/out/textBuffer.hpp index e03b83fd2fb..bf850fb64fb 100644 --- a/src/buffer/out/textBuffer.hpp +++ b/src/buffer/out/textBuffer.hpp @@ -183,7 +183,7 @@ class TextBuffer final std::optional> positionInfo); const size_t AddPatternRecognizer(const std::string_view regexString); - interval_tree::IntervalTree UpdatePatterns(const size_t firstRow, const size_t lastRow) const; + interval_tree::IntervalTree GetPatterns(const size_t firstRow, const size_t lastRow) const; private: void _UpdateSize(); diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 9e98cff5180..59adb4e93f8 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -630,12 +630,12 @@ void Microsoft::Terminal::Core::Terminal::_InvalidatePatternTree(interval_tree:: auto invalidate = [=](const ThisTree::interval& interval) { COORD startCoord{ gsl::narrow(interval.start % rowSize), gsl::narrow(interval.start / rowSize + vis) }; COORD endCoord{ gsl::narrow(interval.stop % rowSize), gsl::narrow(interval.stop / rowSize + vis) }; - _InvalidateRegion(startCoord, endCoord); + _InvalidateFromCoords(startCoord, endCoord); }; tree.visit_all(invalidate); } -void Microsoft::Terminal::Core::Terminal::_InvalidateRegion(const COORD start, const COORD end) +void Microsoft::Terminal::Core::Terminal::_InvalidateFromCoords(const COORD start, const COORD end) { if (start.Y == end.Y) { @@ -1104,7 +1104,7 @@ bool Terminal::IsCursorBlinkingAllowed() const noexcept void Microsoft::Terminal::Core::Terminal::UpdatePatterns() noexcept { auto old = _patternIntervalTree; - _patternIntervalTree = _buffer->UpdatePatterns(_VisibleStartIndex(), _VisibleEndIndex()); + _patternIntervalTree = _buffer->GetPatterns(_VisibleStartIndex(), _VisibleEndIndex()); _InvalidatePatternTree(old); _InvalidatePatternTree(_patternIntervalTree); } diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index 9f4f4205e4b..7b71a21d4ed 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -277,7 +277,7 @@ class Microsoft::Terminal::Core::Terminal final : interval_tree::IntervalTree _patternIntervalTree; void _InvalidatePatternTree(interval_tree::IntervalTree& tree); - void _InvalidateRegion(const COORD start, const COORD end); + void _InvalidateFromCoords(const COORD start, const COORD end); // Since virtual keys are non-zero, you assume that this field is empty/invalid if it is. struct KeyEventCodes From 9211952f09eb22c75ffd0009421c5705873f0bc1 Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Thu, 1 Oct 2020 14:37:46 -0400 Subject: [PATCH 18/48] addressing comments --- src/buffer/out/textBuffer.cpp | 3 ++- src/cascadia/TerminalCore/Terminal.cpp | 17 +++++++++++++---- .../TerminalCore/terminalrenderdata.cpp | 2 +- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index 6958971a171..587fa46db00 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -14,6 +14,8 @@ using namespace Microsoft::Console; using namespace Microsoft::Console::Types; +typedef interval_tree::IntervalTree ThisTree; + // Routine Description: // - Creates a new instance of TextBuffer // Arguments: @@ -2378,7 +2380,6 @@ const size_t TextBuffer::AddPatternRecognizer(const std::string_view regexString // - An interval tree containing the patterns found interval_tree::IntervalTree TextBuffer::GetPatterns(const size_t firstRow, const size_t lastRow) const { - typedef interval_tree::IntervalTree ThisTree; ThisTree::interval_vector intervals; std::wstring concatAll; diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 59adb4e93f8..0ede9337352 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -19,6 +19,8 @@ using namespace Microsoft::Console::Render; using namespace Microsoft::Console::Types; using namespace Microsoft::Console::VirtualTerminal; +typedef interval_tree::IntervalTree ThisTree; + static std::wstring _KeyEventsToText(std::deque>& inEventsToWrite) { std::wstring wstr = L""; @@ -622,9 +624,12 @@ bool Terminal::SendCharEvent(const wchar_t ch, const WORD scanCode, const Contro return handledDown || handledUp; } -void Microsoft::Terminal::Core::Terminal::_InvalidatePatternTree(interval_tree::IntervalTree& tree) +// Method Description: +// - Invalidates the regions described in the given pattern tree for the rendering purposes +// Arguments: +// - The interval tree containing regions that need to be invalidated +void Terminal::_InvalidatePatternTree(interval_tree::IntervalTree& tree) { - typedef interval_tree::IntervalTree ThisTree; const auto rowSize = _buffer->GetRowByOffset(0).size(); const auto vis = _VisibleStartIndex(); auto invalidate = [=](const ThisTree::interval& interval) { @@ -635,7 +640,11 @@ void Microsoft::Terminal::Core::Terminal::_InvalidatePatternTree(interval_tree:: tree.visit_all(invalidate); } -void Microsoft::Terminal::Core::Terminal::_InvalidateFromCoords(const COORD start, const COORD end) +// Method Description: +// - Given start and end coords, invalidates all the regions between them +// Arguments: +// - The start and end coords +void Terminal::_InvalidateFromCoords(const COORD start, const COORD end) { if (start.Y == end.Y) { @@ -1101,7 +1110,7 @@ bool Terminal::IsCursorBlinkingAllowed() const noexcept // - Update our internal knowledge about where regex patterns are on the screen // - This is called by TerminalControl (through a throttled function) when the visible // region changes (for example by text entering the buffer or scrolling) -void Microsoft::Terminal::Core::Terminal::UpdatePatterns() noexcept +void Terminal::UpdatePatterns() noexcept { auto old = _patternIntervalTree; _patternIntervalTree = _buffer->GetPatterns(_VisibleStartIndex(), _VisibleEndIndex()); diff --git a/src/cascadia/TerminalCore/terminalrenderdata.cpp b/src/cascadia/TerminalCore/terminalrenderdata.cpp index 771ac9b0bbd..0a2e7d9d2a0 100644 --- a/src/cascadia/TerminalCore/terminalrenderdata.cpp +++ b/src/cascadia/TerminalCore/terminalrenderdata.cpp @@ -137,7 +137,7 @@ const std::wstring Microsoft::Terminal::Core::Terminal::GetHyperlinkCustomId(uin // - The location // Return value: // - The pattern ID of the location -const size_t Microsoft::Terminal::Core::Terminal::GetPatternId(const COORD location) const noexcept +const size_t Terminal::GetPatternId(const COORD location) const noexcept { // Convert the location into its 1-d location because the interval tree stores locations that way const auto absLoc = (_buffer->GetRowByOffset(0).size() * location.Y) + location.X; From 9ff827f7ed5418af00d6e012815d0c7e1d020e9b Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Thu, 1 Oct 2020 16:25:10 -0400 Subject: [PATCH 19/48] interval tree of points --- oss/interval_tree/IntervalTree.h | 14 ++++---- src/buffer/out/textBuffer.cpp | 9 ++++-- src/buffer/out/textBuffer.hpp | 2 +- src/cascadia/TerminalCore/Terminal.cpp | 19 ++++------- src/cascadia/TerminalCore/Terminal.hpp | 4 +-- .../TerminalCore/terminalrenderdata.cpp | 5 ++- src/inc/til/point.h | 32 +++++++++++++++++++ 7 files changed, 57 insertions(+), 28 deletions(-) diff --git a/oss/interval_tree/IntervalTree.h b/oss/interval_tree/IntervalTree.h index d0f2ee44b12..dc4671551f0 100644 --- a/oss/interval_tree/IntervalTree.h +++ b/oss/interval_tree/IntervalTree.h @@ -67,7 +67,7 @@ namespace interval_tree }; IntervalTree() : - left(nullptr), right(nullptr), center(0) + left(nullptr), right(nullptr), center(Scalar{}) { } @@ -103,8 +103,8 @@ namespace interval_tree std::size_t depth = 16, std::size_t minbucket = 64, std::size_t maxbucket = 512, - Scalar leftextent = 0, - Scalar rightextent = 0) : + Scalar leftextent = {}, + Scalar rightextent = {}) : left(nullptr), right(nullptr) { --depth; @@ -114,7 +114,7 @@ namespace interval_tree { center = (minmaxStart.first->start + minmaxStop.second->stop) / 2; } - if (leftextent == 0 && rightextent == 0) + if (leftextent == Scalar{} && rightextent == Scalar{}) { // sort intervals by start std::sort(ivals.begin(), ivals.end(), IntervalStartCmp()); @@ -132,10 +132,10 @@ namespace interval_tree } else { - Scalar leftp = 0; - Scalar rightp = 0; + Scalar leftp = Scalar{}; + Scalar rightp = Scalar{}; - if (leftextent || rightextent) + if (leftextent != Scalar{} || rightextent != Scalar{}) { leftp = leftextent; rightp = rightextent; diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index 587fa46db00..60370ec6d1f 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -14,7 +14,7 @@ using namespace Microsoft::Console; using namespace Microsoft::Console::Types; -typedef interval_tree::IntervalTree ThisTree; +typedef interval_tree::IntervalTree ThisTree; // Routine Description: // - Creates a new instance of TextBuffer @@ -2378,7 +2378,7 @@ const size_t TextBuffer::AddPatternRecognizer(const std::string_view regexString // - The lastRow to search // Return value: // - An interval tree containing the patterns found -interval_tree::IntervalTree TextBuffer::GetPatterns(const size_t firstRow, const size_t lastRow) const +interval_tree::IntervalTree TextBuffer::GetPatterns(const size_t firstRow, const size_t lastRow) const { ThisTree::interval_vector intervals; @@ -2415,12 +2415,15 @@ interval_tree::IntervalTree TextBuffer::GetPatterns(const size_t const auto end = start + i->str().size(); lenUpToThis = end; + til::point startCoord{ gsl::narrow(start % rowSize), gsl::narrow(start / rowSize) }; + til::point endCoord{ gsl::narrow(end % rowSize), gsl::narrow(end / rowSize) }; + // store the intervals // NOTE: these intervals are relative to the VIEWPORT not the buffer // Keeping these relative to the viewport for now because its the renderer // that actually uses these locations and the renderer works relative to // the viewport - intervals.push_back(ThisTree::interval(start, end, idAndPattern.first)); + intervals.push_back(ThisTree::interval(startCoord, endCoord, idAndPattern.first)); } } ThisTree result(std::move(intervals)); diff --git a/src/buffer/out/textBuffer.hpp b/src/buffer/out/textBuffer.hpp index bf850fb64fb..6be541198a8 100644 --- a/src/buffer/out/textBuffer.hpp +++ b/src/buffer/out/textBuffer.hpp @@ -183,7 +183,7 @@ class TextBuffer final std::optional> positionInfo); const size_t AddPatternRecognizer(const std::string_view regexString); - interval_tree::IntervalTree GetPatterns(const size_t firstRow, const size_t lastRow) const; + interval_tree::IntervalTree GetPatterns(const size_t firstRow, const size_t lastRow) const; private: void _UpdateSize(); diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 0ede9337352..6c3165a21b6 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -19,7 +19,7 @@ using namespace Microsoft::Console::Render; using namespace Microsoft::Console::Types; using namespace Microsoft::Console::VirtualTerminal; -typedef interval_tree::IntervalTree ThisTree; +typedef interval_tree::IntervalTree ThisTree; static std::wstring _KeyEventsToText(std::deque>& inEventsToWrite) { @@ -427,9 +427,7 @@ std::wstring Terminal::GetHyperlinkAtPosition(const COORD position) } // also look through our known pattern locations in our pattern interval tree // we need to convert to 1-d coordinates because that is how the tree stores them - const auto rowSize = _buffer->GetRowByOffset(0).size(); - const auto absLoc = (rowSize * position.Y) + position.X; - const auto results = _patternIntervalTree.findOverlapping(absLoc + 1, absLoc); + const auto results = _patternIntervalTree.findOverlapping(COORD{ position.X + 1, position.Y }, position); if (results.size() > 0) { for (const auto& result : results) @@ -438,12 +436,10 @@ std::wstring Terminal::GetHyperlinkAtPosition(const COORD position) { const auto start = result.start; const auto end = result.stop; - COORD startCoord{ gsl::narrow(start % rowSize), gsl::narrow(start / rowSize) }; - COORD endCoord{ gsl::narrow(end % rowSize), gsl::narrow(end / rowSize) }; std::wstring uri; - const auto startIter = _buffer->GetCellDataAt(_ConvertToBufferCell(startCoord)); - const auto endIter = _buffer->GetCellDataAt(_ConvertToBufferCell(endCoord)); + const auto startIter = _buffer->GetCellDataAt(_ConvertToBufferCell(start)); + const auto endIter = _buffer->GetCellDataAt(_ConvertToBufferCell(end)); for (auto iter = startIter; iter != endIter; ++iter) { uri += iter->Chars(); @@ -628,13 +624,12 @@ bool Terminal::SendCharEvent(const wchar_t ch, const WORD scanCode, const Contro // - Invalidates the regions described in the given pattern tree for the rendering purposes // Arguments: // - The interval tree containing regions that need to be invalidated -void Terminal::_InvalidatePatternTree(interval_tree::IntervalTree& tree) +void Terminal::_InvalidatePatternTree(interval_tree::IntervalTree& tree) { - const auto rowSize = _buffer->GetRowByOffset(0).size(); const auto vis = _VisibleStartIndex(); auto invalidate = [=](const ThisTree::interval& interval) { - COORD startCoord{ gsl::narrow(interval.start % rowSize), gsl::narrow(interval.start / rowSize + vis) }; - COORD endCoord{ gsl::narrow(interval.stop % rowSize), gsl::narrow(interval.stop / rowSize + vis) }; + COORD startCoord{ gsl::narrow(interval.start.x()), gsl::narrow(interval.start.y() + vis) }; + COORD endCoord{ gsl::narrow(interval.stop.x()), gsl::narrow(interval.stop.y() + vis) }; _InvalidateFromCoords(startCoord, endCoord); }; tree.visit_all(invalidate); diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index 7b71a21d4ed..5e9fb69bb86 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -275,8 +275,8 @@ class Microsoft::Terminal::Core::Terminal final : // underneath them, while others would prefer to anchor it in place. // Either way, we should make this behavior controlled by a setting. - interval_tree::IntervalTree _patternIntervalTree; - void _InvalidatePatternTree(interval_tree::IntervalTree& tree); + interval_tree::IntervalTree _patternIntervalTree; + void _InvalidatePatternTree(interval_tree::IntervalTree& tree); void _InvalidateFromCoords(const COORD start, const COORD end); // Since virtual keys are non-zero, you assume that this field is empty/invalid if it is. diff --git a/src/cascadia/TerminalCore/terminalrenderdata.cpp b/src/cascadia/TerminalCore/terminalrenderdata.cpp index 0a2e7d9d2a0..f4270c388b4 100644 --- a/src/cascadia/TerminalCore/terminalrenderdata.cpp +++ b/src/cascadia/TerminalCore/terminalrenderdata.cpp @@ -139,9 +139,8 @@ const std::wstring Microsoft::Terminal::Core::Terminal::GetHyperlinkCustomId(uin // - The pattern ID of the location const size_t Terminal::GetPatternId(const COORD location) const noexcept { - // Convert the location into its 1-d location because the interval tree stores locations that way - const auto absLoc = (_buffer->GetRowByOffset(0).size() * location.Y) + location.X; - const auto results = _patternIntervalTree.findOverlapping(absLoc + 1, absLoc); + // Look through our interval tree for this location + const auto results = _patternIntervalTree.findOverlapping(COORD{ location.X + 1, location.Y }, location); if (results.size() == 0) { return 0; diff --git a/src/inc/til/point.h b/src/inc/til/point.h index 6d7f40a36d6..87c802ebb27 100644 --- a/src/inc/til/point.h +++ b/src/inc/til/point.h @@ -123,6 +123,38 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" } } + constexpr bool operator<=(const point& other) const noexcept + { + if (_y < other._y) + { + return true; + } + else if (_y > other._y) + { + return false; + } + else + { + return _x <= other._x; + } + } + + constexpr bool operator>=(const point& other) const noexcept + { + if (_y > other._y) + { + return true; + } + else if (_y < other._y) + { + return false; + } + else + { + return _x >= other._x; + } + } + point operator+(const point& other) const { ptrdiff_t x; From 2256cacf48927721839be56f32ee8b3bbc82af15 Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Thu, 1 Oct 2020 16:59:37 -0400 Subject: [PATCH 20/48] small fix --- oss/interval_tree/IntervalTree.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oss/interval_tree/IntervalTree.h b/oss/interval_tree/IntervalTree.h index dc4671551f0..a6edb7d31c6 100644 --- a/oss/interval_tree/IntervalTree.h +++ b/oss/interval_tree/IntervalTree.h @@ -67,7 +67,7 @@ namespace interval_tree }; IntervalTree() : - left(nullptr), right(nullptr), center(Scalar{}) + left(nullptr), right(nullptr), center() { } From 27e2b3f52232389e308e5ca57c1dea41f711ea1e Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Thu, 1 Oct 2020 17:11:29 -0400 Subject: [PATCH 21/48] const --- src/buffer/out/textBuffer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index 60370ec6d1f..caa806c56cc 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -2415,8 +2415,8 @@ interval_tree::IntervalTree TextBuffer::GetPatterns(const si const auto end = start + i->str().size(); lenUpToThis = end; - til::point startCoord{ gsl::narrow(start % rowSize), gsl::narrow(start / rowSize) }; - til::point endCoord{ gsl::narrow(end % rowSize), gsl::narrow(end / rowSize) }; + const til::point startCoord{ gsl::narrow(start % rowSize), gsl::narrow(start / rowSize) }; + const til::point endCoord{ gsl::narrow(end % rowSize), gsl::narrow(end / rowSize) }; // store the intervals // NOTE: these intervals are relative to the VIEWPORT not the buffer From 13df0dc2012cb2fffdac4f496bbd391d2d5450cb Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Fri, 2 Oct 2020 14:48:29 -0400 Subject: [PATCH 22/48] double underline on hover --- src/cascadia/TerminalControl/TermControl.cpp | 10 +++++--- src/cascadia/TerminalControl/TermControl.h | 4 +++ src/cascadia/TerminalCore/Terminal.cpp | 16 ++++++++++++ src/cascadia/TerminalCore/Terminal.hpp | 1 + src/renderer/base/renderer.cpp | 27 +++++++++++++++----- src/renderer/base/renderer.hpp | 6 +++++ 6 files changed, 54 insertions(+), 10 deletions(-) diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 31eece83035..d99666b50dd 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -78,7 +78,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation _lastMouseClickTimestamp{}, _lastMouseClickPos{}, _selectionNeedsToBeCopied{ false }, - _searchBox{ nullptr } + _searchBox{ nullptr }, + _lastHoveredInterval{ til::point{}, til::point{}, size_t{} } { _EnsureStaticInitialization(); InitializeComponent(); @@ -1299,13 +1300,16 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation _lastHoveredCell = terminalPos; const auto newId = _terminal->GetHyperlinkIdAtPosition(terminalPos); + const auto newInterval = _terminal->GetHyperlinkIntervalFromPosition(terminalPos); // If the hyperlink ID changed, trigger a redraw all (so this will happen both when we move // onto a link and when we move off a link) - if (newId != _lastHoveredId) + if (newId != _lastHoveredId || (newInterval.start != _lastHoveredInterval.start)) { + _lastHoveredId = newId; + _lastHoveredInterval = newInterval; _renderEngine->UpdateHyperlinkHoveredId(newId); + _renderer->UpdateLastHoveredInterval(newInterval); _renderer->TriggerRedrawAll(); - _lastHoveredId = newId; } } } diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 71d3674b8d3..fbb190e0a60 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -17,6 +17,8 @@ #include "SearchBoxControl.h" #include "ThrottledFunc.h" +typedef interval_tree::IntervalTree ThisTree; + namespace Microsoft::Console::VirtualTerminal { struct MouseButtonState; @@ -215,6 +217,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation // Track the last hyperlink ID we hovered over uint16_t _lastHoveredId; + ThisTree::interval _lastHoveredInterval; + using Timestamp = uint64_t; // imported from WinUser diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 6c3165a21b6..659ebf105be 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -462,6 +462,22 @@ uint16_t Terminal::GetHyperlinkIdAtPosition(const COORD position) return _buffer->GetCellDataAt(_ConvertToBufferCell(position))->TextAttr().GetHyperlinkId(); } +interval_tree::Interval Microsoft::Terminal::Core::Terminal::GetHyperlinkIntervalFromPosition(const COORD position) +{ + const auto results = _patternIntervalTree.findOverlapping(COORD{ position.X + 1, position.Y }, position); + if (results.size() > 0) + { + for (const auto& result : results) + { + if (result.value == _hyperlinkPatternId) + { + return result; + } + } + } + return ThisTree::interval(til::point{}, til::point{}, size_t{}); +} + // Method Description: // - Send this particular (non-character) key event to the terminal. // - The terminal will translate the key and the modifiers pressed into the diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index 5e9fb69bb86..d318501b7a6 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -131,6 +131,7 @@ class Microsoft::Terminal::Core::Terminal final : std::wstring GetHyperlinkAtPosition(const COORD position); uint16_t GetHyperlinkIdAtPosition(const COORD position); + interval_tree::Interval GetHyperlinkIntervalFromPosition(const COORD position); #pragma endregion #pragma region IBaseData(base to IRenderData and IUiaData) diff --git a/src/renderer/base/renderer.cpp b/src/renderer/base/renderer.cpp index 41207293f31..47e06813f59 100644 --- a/src/renderer/base/renderer.cpp +++ b/src/renderer/base/renderer.cpp @@ -30,7 +30,8 @@ Renderer::Renderer(IRenderData* pData, _pThread{ std::move(thread) }, _destructing{ false }, _clusterBuffer{}, - _viewport{ pData->GetViewport() } + _viewport{ pData->GetViewport() }, + _hoveredInterval{ til::point{}, til::point{}, size_t{} } { for (size_t i = 0; i < cEngines; i++) { @@ -711,12 +712,6 @@ void Renderer::_PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine, // Hold onto the current pattern id as well const auto currentPatternId = patternId; - // For now, we underline all patterns so adjust the currentRunColor if this run is a pattern - if (currentPatternId != 0) - { - currentRunColor.SetUnderlined(true); - } - // Update the drawing brushes with our color. THROW_IF_FAILED(_UpdateDrawingBrushes(pEngine, currentRunColor, false)); @@ -724,6 +719,19 @@ void Renderer::_PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine, screenPoint.X += gsl::narrow(cols); cols = 0; + // For now, we underline all patterns so adjust the currentRunColor if this run is a pattern + if (currentPatternId != 0) + { + if (_hoveredInterval.start <= til::point{ screenPoint } && til::point{ screenPoint } <= _hoveredInterval.stop) + { + currentRunColor.SetDoublyUnderlined(true); + } + else + { + currentRunColor.SetUnderlined(true); + } + } + // Hold onto the start of this run iterator and the target location where we started // in case we need to do some special work to paint the line drawing characters. const auto currentRunItStart = it; @@ -1231,6 +1239,11 @@ void Renderer::ResetErrorStateAndResume() EnablePainting(); } +void Microsoft::Console::Render::Renderer::UpdateLastHoveredInterval(const ThisTree::interval newInterval) +{ + _hoveredInterval = newInterval; +} + // Method Description: // - Blocks until the engines are able to render without blocking. void Renderer::WaitUntilCanRender() diff --git a/src/renderer/base/renderer.hpp b/src/renderer/base/renderer.hpp index db6dc6d3875..bf549f171e7 100644 --- a/src/renderer/base/renderer.hpp +++ b/src/renderer/base/renderer.hpp @@ -25,6 +25,8 @@ Author(s): #include "../../buffer/out/textBuffer.hpp" #include "../../buffer/out/CharRow.hpp" +typedef interval_tree::IntervalTree ThisTree; + namespace Microsoft::Console::Render { class Renderer sealed : public IRenderer @@ -80,6 +82,8 @@ namespace Microsoft::Console::Render void SetRendererEnteredErrorStateCallback(std::function pfn); void ResetErrorStateAndResume(); + void UpdateLastHoveredInterval(const ThisTree::interval newInterval); + private: std::deque _rgpEngines; @@ -88,6 +92,8 @@ namespace Microsoft::Console::Render std::unique_ptr _pThread; bool _destructing = false; + ThisTree::interval _hoveredInterval; + void _NotifyPaintFrame(); [[nodiscard]] HRESULT _PaintFrameForEngine(_In_ IRenderEngine* const pEngine) noexcept; From d45a4d96f4970cbccb67cdfb367d387c794f803a Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Mon, 5 Oct 2020 10:22:59 -0400 Subject: [PATCH 23/48] dashed underline for patterns, solid underline on hover --- src/cascadia/TerminalControl/TermControl.cpp | 2 +- src/renderer/base/renderer.cpp | 29 ++++++++++---------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index d99666b50dd..a73e97f5abb 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -36,7 +36,7 @@ constexpr const auto ScrollBarUpdateInterval = std::chrono::milliseconds(8); constexpr const auto TsfRedrawInterval = std::chrono::milliseconds(100); // The minimum delay between updating the locations of regex patterns -constexpr const auto UpdatePatternLocationsInterval = std::chrono::milliseconds(10); +constexpr const auto UpdatePatternLocationsInterval = std::chrono::milliseconds(50); DEFINE_ENUM_FLAG_OPERATORS(winrt::Microsoft::Terminal::TerminalControl::CopyFormat); diff --git a/src/renderer/base/renderer.cpp b/src/renderer/base/renderer.cpp index 47e06813f59..cff0142cd5f 100644 --- a/src/renderer/base/renderer.cpp +++ b/src/renderer/base/renderer.cpp @@ -707,7 +707,7 @@ void Renderer::_PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine, // We'll be changing the persistent one as we run through the inner loops to detect // when a run changes, but we will still need to know this color at the bottom // when we go to draw gridlines for the length of the run. - auto currentRunColor = color; + const auto currentRunColor = color; // Hold onto the current pattern id as well const auto currentPatternId = patternId; @@ -719,19 +719,6 @@ void Renderer::_PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine, screenPoint.X += gsl::narrow(cols); cols = 0; - // For now, we underline all patterns so adjust the currentRunColor if this run is a pattern - if (currentPatternId != 0) - { - if (_hoveredInterval.start <= til::point{ screenPoint } && til::point{ screenPoint } <= _hoveredInterval.stop) - { - currentRunColor.SetDoublyUnderlined(true); - } - else - { - currentRunColor.SetUnderlined(true); - } - } - // Hold onto the start of this run iterator and the target location where we started // in case we need to do some special work to paint the line drawing characters. const auto currentRunItStart = it; @@ -923,6 +910,20 @@ void Renderer::_PaintBufferOutputGridLineHelper(_In_ IRenderEngine* const pEngin { // Convert console grid line representations into rendering engine enum representations. IRenderEngine::GridLines lines = Renderer::s_GetGridlines(textAttribute); + + // For now, we dash underline patterns and switch to regular underline on hover + if (_pData->GetPatternId(coordTarget) != 0) + { + if (_hoveredInterval.start <= til::point{ coordTarget } && til::point{ coordTarget } <= _hoveredInterval.stop) + { + lines |= IRenderEngine::GridLines::Underline; + } + else + { + lines |= IRenderEngine::GridLines::HyperlinkUnderline; + } + } + // Return early if there are no lines to paint. if (lines != IRenderEngine::GridLines::None) { From 3f79842995443db2ef28665514b49fe910c6f870 Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Mon, 5 Oct 2020 10:58:26 -0400 Subject: [PATCH 24/48] some cleaning up --- src/cascadia/TerminalControl/TermControl.cpp | 4 +-- src/cascadia/TerminalCore/Terminal.cpp | 37 ++++++++++---------- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index a73e97f5abb..11921056fe4 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -1301,8 +1301,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation const auto newId = _terminal->GetHyperlinkIdAtPosition(terminalPos); const auto newInterval = _terminal->GetHyperlinkIntervalFromPosition(terminalPos); - // If the hyperlink ID changed, trigger a redraw all (so this will happen both when we move - // onto a link and when we move off a link) + // If the hyperlink ID changed or the interval changed, trigger a redraw all + // (so this will happen both when we move onto a link and when we move off a link) if (newId != _lastHoveredId || (newInterval.start != _lastHoveredInterval.start)) { _lastHoveredId = newId; diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 659ebf105be..063a6d93223 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -426,27 +426,20 @@ std::wstring Terminal::GetHyperlinkAtPosition(const COORD position) return uri; } // also look through our known pattern locations in our pattern interval tree - // we need to convert to 1-d coordinates because that is how the tree stores them - const auto results = _patternIntervalTree.findOverlapping(COORD{ position.X + 1, position.Y }, position); - if (results.size() > 0) + const auto result = GetHyperlinkIntervalFromPosition(position); + if (result.value == _hyperlinkPatternId) { - for (const auto& result : results) - { - if (result.value == _hyperlinkPatternId) - { - const auto start = result.start; - const auto end = result.stop; - std::wstring uri; + const auto start = result.start; + const auto end = result.stop; + std::wstring uri; - const auto startIter = _buffer->GetCellDataAt(_ConvertToBufferCell(start)); - const auto endIter = _buffer->GetCellDataAt(_ConvertToBufferCell(end)); - for (auto iter = startIter; iter != endIter; ++iter) - { - uri += iter->Chars(); - } - return uri; - } + const auto startIter = _buffer->GetCellDataAt(_ConvertToBufferCell(start)); + const auto endIter = _buffer->GetCellDataAt(_ConvertToBufferCell(end)); + for (auto iter = startIter; iter != endIter; ++iter) + { + uri += iter->Chars(); } + return uri; } return {}; } @@ -462,7 +455,13 @@ uint16_t Terminal::GetHyperlinkIdAtPosition(const COORD position) return _buffer->GetCellDataAt(_ConvertToBufferCell(position))->TextAttr().GetHyperlinkId(); } -interval_tree::Interval Microsoft::Terminal::Core::Terminal::GetHyperlinkIntervalFromPosition(const COORD position) +// Method description: +// - Given a position in a URI pattern, gets the start and end coordinates of the URI +// Arguments: +// - The position +// Return value: +// - The interval representing the start and end coordinates +ThisTree::interval Terminal::GetHyperlinkIntervalFromPosition(const COORD position) { const auto results = _patternIntervalTree.findOverlapping(COORD{ position.X + 1, position.Y }, position); if (results.size() > 0) From 0c324b0d6f6e27a7ca0963675880a4ae8faedc72 Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Mon, 5 Oct 2020 11:32:22 -0400 Subject: [PATCH 25/48] more cleanup --- oss/interval_tree/IntervalTree.h | 18 ++++++++++++++++++ src/cascadia/TerminalControl/TermControl.cpp | 5 ++--- src/cascadia/TerminalCore/Terminal.cpp | 4 +--- src/cascadia/TerminalCore/Terminal.hpp | 4 +++- src/renderer/base/renderer.cpp | 3 +-- 5 files changed, 25 insertions(+), 9 deletions(-) diff --git a/oss/interval_tree/IntervalTree.h b/oss/interval_tree/IntervalTree.h index a6edb7d31c6..8480614c4bc 100644 --- a/oss/interval_tree/IntervalTree.h +++ b/oss/interval_tree/IntervalTree.h @@ -22,6 +22,24 @@ namespace interval_tree start(std::min(s, e)), stop(std::max(s, e)), value(v) { } + + Interval() + { + } + + constexpr bool operator==(const Interval& other) const noexcept + { + return start == other.start && + stop == other.stop && + value == other.value; + } + + constexpr bool operator!=(const Interval& other) const noexcept + { + return start != other.start || + stop != other.stop || + value != other.value; + } }; template diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 11921056fe4..a5c53206346 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -78,8 +78,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation _lastMouseClickTimestamp{}, _lastMouseClickPos{}, _selectionNeedsToBeCopied{ false }, - _searchBox{ nullptr }, - _lastHoveredInterval{ til::point{}, til::point{}, size_t{} } + _searchBox{ nullptr } { _EnsureStaticInitialization(); InitializeComponent(); @@ -1303,7 +1302,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation const auto newInterval = _terminal->GetHyperlinkIntervalFromPosition(terminalPos); // If the hyperlink ID changed or the interval changed, trigger a redraw all // (so this will happen both when we move onto a link and when we move off a link) - if (newId != _lastHoveredId || (newInterval.start != _lastHoveredInterval.start)) + if (newId != _lastHoveredId || (newInterval != _lastHoveredInterval)) { _lastHoveredId = newId; _lastHoveredInterval = newInterval; diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 063a6d93223..a3788825747 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -19,8 +19,6 @@ using namespace Microsoft::Console::Render; using namespace Microsoft::Console::Types; using namespace Microsoft::Console::VirtualTerminal; -typedef interval_tree::IntervalTree ThisTree; - static std::wstring _KeyEventsToText(std::deque>& inEventsToWrite) { std::wstring wstr = L""; @@ -474,7 +472,7 @@ ThisTree::interval Terminal::GetHyperlinkIntervalFromPosition(const COORD positi } } } - return ThisTree::interval(til::point{}, til::point{}, size_t{}); + return ThisTree::interval(); } // Method Description: diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index d318501b7a6..548ad1a0fdf 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -16,6 +16,8 @@ #include "../../cascadia/terminalcore/ITerminalApi.hpp" #include "../../cascadia/terminalcore/ITerminalInput.hpp" +typedef interval_tree::IntervalTree ThisTree; + // You have to forward decl the ICoreSettings here, instead of including the header. // If you include the header, there will be compilation errors with other // headers that include Terminal.hpp @@ -131,7 +133,7 @@ class Microsoft::Terminal::Core::Terminal final : std::wstring GetHyperlinkAtPosition(const COORD position); uint16_t GetHyperlinkIdAtPosition(const COORD position); - interval_tree::Interval GetHyperlinkIntervalFromPosition(const COORD position); + ThisTree::interval GetHyperlinkIntervalFromPosition(const COORD position); #pragma endregion #pragma region IBaseData(base to IRenderData and IUiaData) diff --git a/src/renderer/base/renderer.cpp b/src/renderer/base/renderer.cpp index cff0142cd5f..1677d2b436c 100644 --- a/src/renderer/base/renderer.cpp +++ b/src/renderer/base/renderer.cpp @@ -30,8 +30,7 @@ Renderer::Renderer(IRenderData* pData, _pThread{ std::move(thread) }, _destructing{ false }, _clusterBuffer{}, - _viewport{ pData->GetViewport() }, - _hoveredInterval{ til::point{}, til::point{}, size_t{} } + _viewport{ pData->GetViewport() } { for (size_t i = 0; i < cEngines; i++) { From 06b4a98af43961260b04392595f84cfd3c2139e3 Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Mon, 5 Oct 2020 12:03:23 -0400 Subject: [PATCH 26/48] render data returns vector of pattern ids --- src/cascadia/TerminalCore/Terminal.hpp | 2 +- .../TerminalCore/terminalrenderdata.cpp | 23 +++++++++++-------- src/host/renderData.cpp | 4 ++-- src/host/renderData.hpp | 2 +- src/host/ut_host/VtIoTests.cpp | 4 ++-- src/renderer/base/renderer.cpp | 14 +++++------ src/renderer/inc/IRenderData.hpp | 2 +- 7 files changed, 27 insertions(+), 24 deletions(-) diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index 548ad1a0fdf..cec5013c752 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -163,7 +163,7 @@ class Microsoft::Terminal::Core::Terminal final : const bool IsGridLineDrawingAllowed() noexcept override; const std::wstring GetHyperlinkUri(uint16_t id) const noexcept override; const std::wstring GetHyperlinkCustomId(uint16_t id) const noexcept override; - const size_t GetPatternId(const COORD location) const noexcept override; + const std::vector GetPatternId(const COORD location) const noexcept override; #pragma endregion #pragma region IUiaData diff --git a/src/cascadia/TerminalCore/terminalrenderdata.cpp b/src/cascadia/TerminalCore/terminalrenderdata.cpp index f4270c388b4..dba607efdab 100644 --- a/src/cascadia/TerminalCore/terminalrenderdata.cpp +++ b/src/cascadia/TerminalCore/terminalrenderdata.cpp @@ -132,26 +132,29 @@ const std::wstring Microsoft::Terminal::Core::Terminal::GetHyperlinkCustomId(uin } // Method Description: -// - Gets the regex pattern id of a location +// - Gets the regex pattern ids of a location // Arguments: // - The location // Return value: -// - The pattern ID of the location -const size_t Terminal::GetPatternId(const COORD location) const noexcept +// - The pattern IDs of the location +const std::vector Terminal::GetPatternId(const COORD location) const noexcept { + std::vector result{}; // Look through our interval tree for this location - const auto results = _patternIntervalTree.findOverlapping(COORD{ location.X + 1, location.Y }, location); - if (results.size() == 0) + const auto intervals = _patternIntervalTree.findOverlapping(COORD{ location.X + 1, location.Y }, location); + if (intervals.size() == 0) { - return 0; + return {}; } else { - // At some point, this should be updated to return a vector of IDs - // which will represent all the patterns this location is a part of - return results.at(0).value; + for (const auto interval : intervals) + { + result.emplace_back(interval.value); + } + return result; } - return 0; + return {}; } std::vector Terminal::GetSelectionRects() noexcept diff --git a/src/host/renderData.cpp b/src/host/renderData.cpp index 9b50f11194c..41d41d4d035 100644 --- a/src/host/renderData.cpp +++ b/src/host/renderData.cpp @@ -349,9 +349,9 @@ const std::wstring RenderData::GetHyperlinkCustomId(uint16_t id) const noexcept } // For now, we ignore regex patterns in conhost -const size_t RenderData::GetPatternId(const COORD /*location*/) const noexcept +const std::vector RenderData::GetPatternId(const COORD /*location*/) const noexcept { - return 0; + return {}; } // Routine Description: diff --git a/src/host/renderData.hpp b/src/host/renderData.hpp index 368b5e2c962..26f291477d0 100644 --- a/src/host/renderData.hpp +++ b/src/host/renderData.hpp @@ -59,7 +59,7 @@ class RenderData final : const std::wstring GetHyperlinkUri(uint16_t id) const noexcept override; const std::wstring GetHyperlinkCustomId(uint16_t id) const noexcept override; - const size_t GetPatternId(const COORD location) const noexcept override; + const std::vector GetPatternId(const COORD location) const noexcept override; #pragma endregion #pragma region IUiaData diff --git a/src/host/ut_host/VtIoTests.cpp b/src/host/ut_host/VtIoTests.cpp index 21def6f814c..ef5afa3a5dc 100644 --- a/src/host/ut_host/VtIoTests.cpp +++ b/src/host/ut_host/VtIoTests.cpp @@ -402,9 +402,9 @@ class MockRenderData : public IRenderData, IUiaData return {}; } - const size_t GetPatternId(const COORD /*location*/) const noexcept + const std::vector GetPatternId(const COORD /*location*/) const noexcept { - return 0; + return {}; } }; diff --git a/src/renderer/base/renderer.cpp b/src/renderer/base/renderer.cpp index 1677d2b436c..99873878b40 100644 --- a/src/renderer/base/renderer.cpp +++ b/src/renderer/base/renderer.cpp @@ -694,7 +694,7 @@ void Renderer::_PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine, // Retrieve the first color. auto color = it->TextAttr(); // Retrieve the first pattern id - auto patternId = _pData->GetPatternId(target); + auto patternIds = _pData->GetPatternId(target); // And hold the point where we should start drawing. auto screenPoint = target; @@ -709,7 +709,7 @@ void Renderer::_PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine, const auto currentRunColor = color; // Hold onto the current pattern id as well - const auto currentPatternId = patternId; + const auto currentPatternId = patternIds; // Update the drawing brushes with our color. THROW_IF_FAILED(_UpdateDrawingBrushes(pEngine, currentRunColor, false)); @@ -740,16 +740,16 @@ void Renderer::_PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine, do { COORD thisPoint{ screenPoint.X + gsl::narrow(cols), screenPoint.Y }; - const auto thisPointPattern = _pData->GetPatternId(thisPoint); - if (color != it->TextAttr() || patternId != thisPointPattern) + const auto thisPointPatterns = _pData->GetPatternId(thisPoint); + if (color != it->TextAttr() || patternIds != thisPointPatterns) { auto newAttr{ it->TextAttr() }; // foreground doesn't matter for runs of spaces (!) // if we trick it . . . we call Paint far fewer times for cmatrix - if (!_IsAllSpaces(it->Chars()) || !newAttr.HasIdenticalVisualRepresentationForBlankSpace(color, globalInvert) || patternId != thisPointPattern) + if (!_IsAllSpaces(it->Chars()) || !newAttr.HasIdenticalVisualRepresentationForBlankSpace(color, globalInvert) || patternIds != thisPointPatterns) { color = newAttr; - patternId = thisPointPattern; + patternIds = thisPointPatterns; break; // vend this run } } @@ -911,7 +911,7 @@ void Renderer::_PaintBufferOutputGridLineHelper(_In_ IRenderEngine* const pEngin IRenderEngine::GridLines lines = Renderer::s_GetGridlines(textAttribute); // For now, we dash underline patterns and switch to regular underline on hover - if (_pData->GetPatternId(coordTarget) != 0) + if (_pData->GetPatternId(coordTarget).size() > 0) { if (_hoveredInterval.start <= til::point{ coordTarget } && til::point{ coordTarget } <= _hoveredInterval.stop) { diff --git a/src/renderer/inc/IRenderData.hpp b/src/renderer/inc/IRenderData.hpp index 0bf95e47ba4..47da94b8def 100644 --- a/src/renderer/inc/IRenderData.hpp +++ b/src/renderer/inc/IRenderData.hpp @@ -69,7 +69,7 @@ namespace Microsoft::Console::Render virtual const std::wstring GetHyperlinkUri(uint16_t id) const noexcept = 0; virtual const std::wstring GetHyperlinkCustomId(uint16_t id) const noexcept = 0; - virtual const size_t GetPatternId(const COORD location) const noexcept = 0; + virtual const std::vector GetPatternId(const COORD location) const noexcept = 0; protected: IRenderData() = default; From 99b42a6ec2ab4c48d3b38a681b8a0ed3420bdbc2 Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Mon, 5 Oct 2020 12:13:46 -0400 Subject: [PATCH 27/48] interval tree equality redundancy --- oss/interval_tree/IntervalTree.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/oss/interval_tree/IntervalTree.h b/oss/interval_tree/IntervalTree.h index 8480614c4bc..372258bfca3 100644 --- a/oss/interval_tree/IntervalTree.h +++ b/oss/interval_tree/IntervalTree.h @@ -36,9 +36,7 @@ namespace interval_tree constexpr bool operator!=(const Interval& other) const noexcept { - return start != other.start || - stop != other.stop || - value != other.value; + return !(*this == other); } }; From aced0e494e9bc9ecb32c122690ce840674336d74 Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Tue, 6 Oct 2020 14:18:33 -0400 Subject: [PATCH 28/48] fix rendering issue --- src/cascadia/TerminalControl/TermControl.cpp | 2 ++ src/cascadia/TerminalCore/Terminal.cpp | 11 +++++++++-- src/cascadia/TerminalCore/Terminal.hpp | 1 + 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index a5c53206346..de19bdedfbb 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -1617,6 +1617,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation _SetEndSelectionPointAtCursor(point); } + // Clear the regex pattern tree so the renderer does not try to render them while scrolling + _terminal->ClearPatternTree(); _updatePatternLocations->Run(); } diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index a3788825747..c462f26f886 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -1120,12 +1120,19 @@ bool Terminal::IsCursorBlinkingAllowed() const noexcept // region changes (for example by text entering the buffer or scrolling) void Terminal::UpdatePatterns() noexcept { - auto old = _patternIntervalTree; _patternIntervalTree = _buffer->GetPatterns(_VisibleStartIndex(), _VisibleEndIndex()); - _InvalidatePatternTree(old); _InvalidatePatternTree(_patternIntervalTree); } +// Method Description: +// - Clears our interval pattern tree +// - This is called to prevent the renderer from rendering patterns while the +// visible region is changing +void Terminal::ClearPatternTree() noexcept +{ + _patternIntervalTree = {}; +} + const std::optional Terminal::GetTabColor() const noexcept { return _tabColor; diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index cec5013c752..6825049947b 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -190,6 +190,7 @@ class Microsoft::Terminal::Core::Terminal final : bool IsCursorBlinkingAllowed() const noexcept; void UpdatePatterns() noexcept; + void ClearPatternTree() noexcept; const std::optional GetTabColor() const noexcept; From 3d4d4c70829755bb236f32190c0dcf08f0cedb85 Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Thu, 8 Oct 2020 11:23:17 -0400 Subject: [PATCH 29/48] typedef fixes, tests --- src/buffer/out/textBuffer.cpp | 10 +-- src/cascadia/TerminalControl/TermControl.h | 4 +- src/cascadia/TerminalCore/Terminal.cpp | 8 ++- src/cascadia/TerminalCore/Terminal.hpp | 4 +- src/renderer/base/renderer.cpp | 4 +- src/renderer/base/renderer.hpp | 6 +- src/til/ut_til/PointTests.cpp | 76 ++++++++++++++++++++++ 7 files changed, 93 insertions(+), 19 deletions(-) diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index caa806c56cc..fcc4c433016 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -14,7 +14,7 @@ using namespace Microsoft::Console; using namespace Microsoft::Console::Types; -typedef interval_tree::IntervalTree ThisTree; +using PointTree = interval_tree::IntervalTree; // Routine Description: // - Creates a new instance of TextBuffer @@ -2378,9 +2378,9 @@ const size_t TextBuffer::AddPatternRecognizer(const std::string_view regexString // - The lastRow to search // Return value: // - An interval tree containing the patterns found -interval_tree::IntervalTree TextBuffer::GetPatterns(const size_t firstRow, const size_t lastRow) const +PointTree TextBuffer::GetPatterns(const size_t firstRow, const size_t lastRow) const { - ThisTree::interval_vector intervals; + PointTree::interval_vector intervals; std::wstring concatAll; const auto rowSize = GetRowByOffset(0).size(); @@ -2423,9 +2423,9 @@ interval_tree::IntervalTree TextBuffer::GetPatterns(const si // Keeping these relative to the viewport for now because its the renderer // that actually uses these locations and the renderer works relative to // the viewport - intervals.push_back(ThisTree::interval(startCoord, endCoord, idAndPattern.first)); + intervals.push_back(PointTree::interval(startCoord, endCoord, idAndPattern.first)); } } - ThisTree result(std::move(intervals)); + PointTree result(std::move(intervals)); return result; } diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index fbb190e0a60..84b021b2a64 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -17,8 +17,6 @@ #include "SearchBoxControl.h" #include "ThrottledFunc.h" -typedef interval_tree::IntervalTree ThisTree; - namespace Microsoft::Console::VirtualTerminal { struct MouseButtonState; @@ -217,7 +215,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation // Track the last hyperlink ID we hovered over uint16_t _lastHoveredId; - ThisTree::interval _lastHoveredInterval; + interval_tree::IntervalTree::interval _lastHoveredInterval; using Timestamp = uint64_t; diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index c462f26f886..61b848496ad 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -19,6 +19,8 @@ using namespace Microsoft::Console::Render; using namespace Microsoft::Console::Types; using namespace Microsoft::Console::VirtualTerminal; +using PointTree = interval_tree::IntervalTree; + static std::wstring _KeyEventsToText(std::deque>& inEventsToWrite) { std::wstring wstr = L""; @@ -459,7 +461,7 @@ uint16_t Terminal::GetHyperlinkIdAtPosition(const COORD position) // - The position // Return value: // - The interval representing the start and end coordinates -ThisTree::interval Terminal::GetHyperlinkIntervalFromPosition(const COORD position) +PointTree::interval Terminal::GetHyperlinkIntervalFromPosition(const COORD position) { const auto results = _patternIntervalTree.findOverlapping(COORD{ position.X + 1, position.Y }, position); if (results.size() > 0) @@ -472,7 +474,7 @@ ThisTree::interval Terminal::GetHyperlinkIntervalFromPosition(const COORD positi } } } - return ThisTree::interval(); + return PointTree::interval(); } // Method Description: @@ -640,7 +642,7 @@ bool Terminal::SendCharEvent(const wchar_t ch, const WORD scanCode, const Contro void Terminal::_InvalidatePatternTree(interval_tree::IntervalTree& tree) { const auto vis = _VisibleStartIndex(); - auto invalidate = [=](const ThisTree::interval& interval) { + auto invalidate = [=](const PointTree::interval& interval) { COORD startCoord{ gsl::narrow(interval.start.x()), gsl::narrow(interval.start.y() + vis) }; COORD endCoord{ gsl::narrow(interval.stop.x()), gsl::narrow(interval.stop.y() + vis) }; _InvalidateFromCoords(startCoord, endCoord); diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index 6825049947b..2dbae64fa1c 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -16,8 +16,6 @@ #include "../../cascadia/terminalcore/ITerminalApi.hpp" #include "../../cascadia/terminalcore/ITerminalInput.hpp" -typedef interval_tree::IntervalTree ThisTree; - // You have to forward decl the ICoreSettings here, instead of including the header. // If you include the header, there will be compilation errors with other // headers that include Terminal.hpp @@ -133,7 +131,7 @@ class Microsoft::Terminal::Core::Terminal final : std::wstring GetHyperlinkAtPosition(const COORD position); uint16_t GetHyperlinkIdAtPosition(const COORD position); - ThisTree::interval GetHyperlinkIntervalFromPosition(const COORD position); + interval_tree::IntervalTree::interval GetHyperlinkIntervalFromPosition(const COORD position); #pragma endregion #pragma region IBaseData(base to IRenderData and IUiaData) diff --git a/src/renderer/base/renderer.cpp b/src/renderer/base/renderer.cpp index 99873878b40..6fd082a4d70 100644 --- a/src/renderer/base/renderer.cpp +++ b/src/renderer/base/renderer.cpp @@ -10,6 +10,8 @@ using namespace Microsoft::Console::Render; using namespace Microsoft::Console::Types; +using PointTree = interval_tree::IntervalTree; + static constexpr auto maxRetriesForRenderEngine = 3; // The renderer will wait this number of milliseconds * how many tries have elapsed before trying again. static constexpr auto renderBackoffBaseTimeMilliseconds{ 150 }; @@ -1239,7 +1241,7 @@ void Renderer::ResetErrorStateAndResume() EnablePainting(); } -void Microsoft::Console::Render::Renderer::UpdateLastHoveredInterval(const ThisTree::interval newInterval) +void Renderer::UpdateLastHoveredInterval(const PointTree::interval newInterval) { _hoveredInterval = newInterval; } diff --git a/src/renderer/base/renderer.hpp b/src/renderer/base/renderer.hpp index bf549f171e7..39a417d5b65 100644 --- a/src/renderer/base/renderer.hpp +++ b/src/renderer/base/renderer.hpp @@ -25,8 +25,6 @@ Author(s): #include "../../buffer/out/textBuffer.hpp" #include "../../buffer/out/CharRow.hpp" -typedef interval_tree::IntervalTree ThisTree; - namespace Microsoft::Console::Render { class Renderer sealed : public IRenderer @@ -82,7 +80,7 @@ namespace Microsoft::Console::Render void SetRendererEnteredErrorStateCallback(std::function pfn); void ResetErrorStateAndResume(); - void UpdateLastHoveredInterval(const ThisTree::interval newInterval); + void UpdateLastHoveredInterval(const interval_tree::IntervalTree::interval newInterval); private: std::deque _rgpEngines; @@ -92,7 +90,7 @@ namespace Microsoft::Console::Render std::unique_ptr _pThread; bool _destructing = false; - ThisTree::interval _hoveredInterval; + interval_tree::IntervalTree::interval _hoveredInterval; void _NotifyPaintFrame(); diff --git a/src/til/ut_til/PointTests.cpp b/src/til/ut_til/PointTests.cpp index 496fbc65d3e..00a25b193ed 100644 --- a/src/til/ut_til/PointTests.cpp +++ b/src/til/ut_til/PointTests.cpp @@ -175,6 +175,82 @@ class PointTests } } + TEST_METHOD(LessThanOrEqual) + { + Log::Comment(L"0.) Equal."); + { + const til::point s1{ 5, 10 }; + const til::point s2{ 5, 10 }; + VERIFY_IS_TRUE(s1 <= s2); + } + + Log::Comment(L"1.) Left Width changed."); + { + const til::point s1{ 4, 10 }; + const til::point s2{ 5, 10 }; + VERIFY_IS_TRUE(s1 <= s2); + } + + Log::Comment(L"2.) Right Width changed."); + { + const til::point s1{ 5, 10 }; + const til::point s2{ 6, 10 }; + VERIFY_IS_TRUE(s1 <= s2); + } + + Log::Comment(L"3.) Left Height changed."); + { + const til::point s1{ 5, 9 }; + const til::point s2{ 5, 10 }; + VERIFY_IS_TRUE(s1 <= s2); + } + + Log::Comment(L"4.) Right Height changed."); + { + const til::point s1{ 5, 10 }; + const til::point s2{ 5, 11 }; + VERIFY_IS_TRUE(s1 <= s2); + } + } + + TEST_METHOD(GreaterThanOrEqual) + { + Log::Comment(L"0.) Equal."); + { + const til::point s1{ 5, 10 }; + const til::point s2{ 5, 10 }; + VERIFY_IS_TRUE(s1 >= s2); + } + + Log::Comment(L"1.) Left Width changed."); + { + const til::point s1{ 4, 10 }; + const til::point s2{ 5, 10 }; + VERIFY_IS_FALSE(s1 >= s2); + } + + Log::Comment(L"2.) Right Width changed."); + { + const til::point s1{ 5, 10 }; + const til::point s2{ 6, 10 }; + VERIFY_IS_FALSE(s1 >= s2); + } + + Log::Comment(L"3.) Left Height changed."); + { + const til::point s1{ 5, 9 }; + const til::point s2{ 5, 10 }; + VERIFY_IS_FALSE(s1 >= s2); + } + + Log::Comment(L"4.) Right Height changed."); + { + const til::point s1{ 5, 10 }; + const til::point s2{ 5, 11 }; + VERIFY_IS_FALSE(s1 >= s2); + } + } + TEST_METHOD(Addition) { Log::Comment(L"0.) Addition of two things that should be in bounds."); From a6a6aa2fc7c5b0e4a590cbd0365ad6b89d192cdb Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Mon, 12 Oct 2020 16:16:01 -0400 Subject: [PATCH 30/48] update patterns when scroll bar changes, lock for writing --- src/cascadia/TerminalControl/TermControl.cpp | 2 ++ src/cascadia/TerminalCore/Terminal.cpp | 1 + 2 files changed, 3 insertions(+) diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index d1c20a04804..b055804d2b4 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -2256,6 +2256,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation update.newValue = viewTop; _updateScrollBar->Run(update); + _terminal->ClearPatternTree(); + _updatePatternLocations->Run(); } // Method Description: diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 9351b3679e9..009bdced307 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -1127,6 +1127,7 @@ bool Terminal::IsCursorBlinkingAllowed() const noexcept // region changes (for example by text entering the buffer or scrolling) void Terminal::UpdatePatterns() noexcept { + auto lock = LockForWriting(); _patternIntervalTree = _buffer->GetPatterns(_VisibleStartIndex(), _VisibleEndIndex()); _InvalidatePatternTree(_patternIntervalTree); } From ce579ead9b72ce3ee1f3b91511ce3a55f6479fd3 Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Mon, 12 Oct 2020 16:20:57 -0400 Subject: [PATCH 31/48] format --- src/cascadia/TerminalCore/Terminal.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 009bdced307..93a09df6879 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -1135,7 +1135,7 @@ void Terminal::UpdatePatterns() noexcept // Method Description: // - Clears our interval pattern tree // - This is called to prevent the renderer from rendering patterns while the -// visible region is changing +// visible region is changing void Terminal::ClearPatternTree() noexcept { _patternIntervalTree = {}; From e2535ad1351445a5e847464d42ae586c585bfdc9 Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Tue, 13 Oct 2020 10:03:47 -0400 Subject: [PATCH 32/48] erase interval tree when buffer scrolls below viewport --- src/cascadia/TerminalCore/Terminal.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 93a09df6879..2abb7881673 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -936,6 +936,9 @@ void Terminal::_AdjustCursorPosition(const COORD proposedPosition) proposedCursorPosition.Y--; rowsPushedOffTopOfBuffer++; } + + // manually erase our pattern intervals since the locations have changed now + _patternIntervalTree = {}; } // Update Cursor Position From e5c8da0c803aa8098e544d99ed4a9eeca9848e6e Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Tue, 13 Oct 2020 11:36:25 -0400 Subject: [PATCH 33/48] longer timer for performance --- src/cascadia/TerminalControl/TermControl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index b055804d2b4..e7308b93cd1 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -36,7 +36,7 @@ constexpr const auto ScrollBarUpdateInterval = std::chrono::milliseconds(8); constexpr const auto TsfRedrawInterval = std::chrono::milliseconds(100); // The minimum delay between updating the locations of regex patterns -constexpr const auto UpdatePatternLocationsInterval = std::chrono::milliseconds(50); +constexpr const auto UpdatePatternLocationsInterval = std::chrono::milliseconds(500); DEFINE_ENUM_FLAG_OPERATORS(winrt::Microsoft::Terminal::TerminalControl::CopyFormat); From f8726c2088d5030a9443deced7691c03022f72be Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Mon, 19 Oct 2020 11:53:27 -0400 Subject: [PATCH 34/48] regex fix --- src/buffer/out/textBuffer.cpp | 4 ++-- src/buffer/out/textBuffer.hpp | 4 ++-- src/cascadia/TerminalCore/Terminal.cpp | 3 ++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index cde3945fe6f..fc638441fb4 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -2369,7 +2369,7 @@ void TextBuffer::CopyHyperlinkMaps(const TextBuffer& other) // - The regex pattern // Return value: // - An ID that the caller should associate with the given pattern -const size_t TextBuffer::AddPatternRecognizer(const std::string_view regexString) +const size_t TextBuffer::AddPatternRecognizer(const std::wstring_view regexString) { ++_currentPatternId; _IdsAndPatterns.emplace(std::make_pair(_currentPatternId, regexString)); @@ -2402,7 +2402,7 @@ PointTree TextBuffer::GetPatterns(const size_t firstRow, const size_t lastRow) c // for each pattern we know of, iterate through the string for (const auto& idAndPattern : _IdsAndPatterns) { - std::wregex regexObj{ til::u8u16(idAndPattern.second) }; + std::wregex regexObj{ idAndPattern.second }; // search through the run with our regex object auto words_begin = std::wsregex_iterator(concatAll.begin(), concatAll.end(), regexObj); diff --git a/src/buffer/out/textBuffer.hpp b/src/buffer/out/textBuffer.hpp index bebe23b00f3..cedb68b34e0 100644 --- a/src/buffer/out/textBuffer.hpp +++ b/src/buffer/out/textBuffer.hpp @@ -182,7 +182,7 @@ class TextBuffer final const std::optional lastCharacterViewport, std::optional> positionInfo); - const size_t AddPatternRecognizer(const std::string_view regexString); + const size_t AddPatternRecognizer(const std::wstring_view regexString); interval_tree::IntervalTree GetPatterns(const size_t firstRow, const size_t lastRow) const; private: @@ -232,7 +232,7 @@ class TextBuffer final void _PruneHyperlinks(); - std::unordered_map _IdsAndPatterns; + std::unordered_map _IdsAndPatterns; size_t _currentPatternId; #ifdef UNIT_TESTING diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index eca87296d97..eddbfe02b0c 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -82,7 +82,8 @@ void Terminal::Create(COORD viewportSize, SHORT scrollbackLines, IRenderTarget& _buffer = std::make_unique(bufferSize, attr, cursorSize, renderTarget); // Add regex pattern recognizers to the buffer // For now, we only add the URI regex pattern - _hyperlinkPatternId = _buffer->AddPatternRecognizer(R"([a-z]+[:.].*?(?=\s))"); + std::wstring_view linkPattern{ LR"(\b(https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|$!:,.;]*[A-Za-z0-9+&@#/%=~_|$])" }; + _hyperlinkPatternId = _buffer->AddPatternRecognizer(linkPattern); } // Method Description: From 1f451fd30f55283b06fc7cdbe990a575fdc2d5d2 Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Mon, 19 Oct 2020 12:13:59 -0400 Subject: [PATCH 35/48] spell/linter --- .github/actions/spell-check/dictionary/dictionary.txt | 1 + oss/interval_tree/MAINTAINER_README.md | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/actions/spell-check/dictionary/dictionary.txt b/.github/actions/spell-check/dictionary/dictionary.txt index 2c562c0f85e..950536cead2 100644 --- a/.github/actions/spell-check/dictionary/dictionary.txt +++ b/.github/actions/spell-check/dictionary/dictionary.txt @@ -152433,6 +152433,7 @@ ft-lb ftncmd ftnerr FTP +ftp ft-pdl FTPI FTS diff --git a/oss/interval_tree/MAINTAINER_README.md b/oss/interval_tree/MAINTAINER_README.md index 7b6422d5227..648db77d6a3 100644 --- a/oss/interval_tree/MAINTAINER_README.md +++ b/oss/interval_tree/MAINTAINER_README.md @@ -1,4 +1,4 @@ -### Notes for Future Maintainers +# Notes for Future Maintainers This was originally imported by @PankajBhojwani in September 2020. @@ -11,7 +11,7 @@ That provenance file is automatically read and inventoried by Microsoft systems 1. Go to ekg/intervaltreerepository on GitHub. 2. Take the file IntervalTree.h wholesale and drop it into the directory here. 3. Don't change anything about it. -4. Validate that the license in the root of the repository didn't change and update it if so. It is sitting in the same directory as this readme. +4. Validate that the license in the root of the repository didn't change and update it if so. It is sitting in the same directory as this readme. If it changed dramatically, ensure that it is still compatible with our license scheme. Also update the NOTICE file in the root of our repository to declare the third-party usage. 5. Submit the pull. From 1745293ebd81884f0e3dab303719ec3b1603d3b1 Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Mon, 19 Oct 2020 12:31:53 -0400 Subject: [PATCH 36/48] linter. --- NOTICE.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/NOTICE.md b/NOTICE.md index be82042836c..d8fd3e9b052 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -2,7 +2,7 @@ Do Not Translate or Localize This software incorporates material from third parties. Microsoft makes certain -open source code available at http://3rdpartysource.microsoft.com, or you may +open source code available at [http://3rdpartysource.microsoft.com](http://3rdpartysource.microsoft.com), or you may send a check or money order for US $5.00, including the product name, the open source component name, and version number, to: @@ -20,7 +20,7 @@ General Public License. ## jsoncpp -**Source**: https://github.com/open-source-parsers/jsoncpp +**Source**: [https://github.com/open-source-parsers/jsoncpp](https://github.com/open-source-parsers/jsoncpp) ### License @@ -50,7 +50,7 @@ SOFTWARE. ## chromium/base/numerics -**Source**: https://github.com/chromium/chromium/tree/master/base/numerics +**Source**: [https://github.com/chromium/chromium/tree/master/base/numerics](https://github.com/chromium/chromium/tree/master/base/numerics) ### License @@ -86,7 +86,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## kimwalisch/libpopcnt -**Source**: https://github.com/kimwalisch/libpopcnt +**Source**: [https://github.com/kimwalisch/libpopcnt](https://github.com/kimwalisch/libpopcnt) ### License @@ -122,7 +122,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## dynamic_bitset -**Source**: https://github.com/pinam45/dynamic_bitset +**Source**: [https://github.com/pinam45/dynamic_bitset](https://github.com/pinam45/dynamic_bitset) ### License @@ -151,9 +151,9 @@ SOFTWARE. ``` -## {fmt} +## \{fmt\} -**Source**: https://github.com/fmtlib/fmt +**Source**: [https://github.com/fmtlib/fmt](https://github.com/fmtlib/fmt) ### License @@ -191,7 +191,7 @@ without including the above copyright and permission notices. ## interval_tree -**Source**: https://github.com/ekg/IntervalTree +**Source**: [https://github.com/ekg/IntervalTree](https://github.com/ekg/IntervalTree) ### License From 2bd0b2158a77dbaf147391f3ecb532aea65d1446 Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Mon, 19 Oct 2020 12:36:54 -0400 Subject: [PATCH 37/48] disable parts of linter --- .github/linters/.markdown-lint.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/linters/.markdown-lint.yml b/.github/linters/.markdown-lint.yml index 2c59fec6c1d..bb6f3660d52 100644 --- a/.github/linters/.markdown-lint.yml +++ b/.github/linters/.markdown-lint.yml @@ -28,11 +28,13 @@ MD007: indent: 2 # Unordered list indentation MD013: line_length: 400 # Line length 80 is far to short +MD024: false # Allow multiple headings with same content MD026: punctuation: ".,;:!。,;:" # List of not allowed MD029: false # Ordered list item prefix MD033: false # Allow inline HTML MD036: false # Emphasis used instead of a heading +MO040: false # Allow ``` blocks in md files with no language specified ################# # Rules by tags # From 2cf0132b50d0453a595e14deb27069b58c415767 Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Mon, 19 Oct 2020 12:43:20 -0400 Subject: [PATCH 38/48] linter 2 --- .github/linters/.markdown-lint.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/linters/.markdown-lint.yml b/.github/linters/.markdown-lint.yml index bb6f3660d52..e5e2e6b85ac 100644 --- a/.github/linters/.markdown-lint.yml +++ b/.github/linters/.markdown-lint.yml @@ -28,13 +28,13 @@ MD007: indent: 2 # Unordered list indentation MD013: line_length: 400 # Line length 80 is far to short -MD024: false # Allow multiple headings with same content +MD024: false # Allow multiple headings with same content MD026: punctuation: ".,;:!。,;:" # List of not allowed MD029: false # Ordered list item prefix MD033: false # Allow inline HTML MD036: false # Emphasis used instead of a heading -MO040: false # Allow ``` blocks in md files with no language specified +MO040: false # Allow ``` blocks in md files with no language specified ################# # Rules by tags # From 2c81f1ee7cd070a32633b2d2eb11b284db09329e Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Mon, 19 Oct 2020 12:49:05 -0400 Subject: [PATCH 39/48] typo --- .github/linters/.markdown-lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/linters/.markdown-lint.yml b/.github/linters/.markdown-lint.yml index e5e2e6b85ac..9128b389dd6 100644 --- a/.github/linters/.markdown-lint.yml +++ b/.github/linters/.markdown-lint.yml @@ -34,7 +34,7 @@ MD026: MD029: false # Ordered list item prefix MD033: false # Allow inline HTML MD036: false # Emphasis used instead of a heading -MO040: false # Allow ``` blocks in md files with no language specified +MD040: false # Allow ``` blocks in md files with no language specified ################# # Rules by tags # From 37a188dc8866d4f76928077d3a6a606813565ade Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Wed, 21 Oct 2020 10:49:05 -0400 Subject: [PATCH 40/48] copy patterns on resize --- src/buffer/out/textBuffer.cpp | 10 ++++++++++ src/buffer/out/textBuffer.hpp | 1 + 2 files changed, 11 insertions(+) diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index fc638441fb4..c4d5db5463c 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -2192,6 +2192,7 @@ HRESULT TextBuffer::Reflow(TextBuffer& oldBuffer, // Finish copying remaining parameters from the old text buffer to the new one newBuffer.CopyProperties(oldBuffer); newBuffer.CopyHyperlinkMaps(oldBuffer); + newBuffer.CopyPatterns(oldBuffer); // If we found where to put the cursor while placing characters into the buffer, // just put the cursor there. Otherwise we have to advance manually. @@ -2376,6 +2377,15 @@ const size_t TextBuffer::AddPatternRecognizer(const std::wstring_view regexStrin return _currentPatternId; } +// Method Description: +// - Copies the patterns the other buffer knows about into this one +// Arguments: +// - The other buffer +void TextBuffer::CopyPatterns(const TextBuffer& OtherBuffer) +{ + _IdsAndPatterns = OtherBuffer._IdsAndPatterns; +} + // Method Description: // - Finds patterns within the requested region of the text buffer // Arguments: diff --git a/src/buffer/out/textBuffer.hpp b/src/buffer/out/textBuffer.hpp index cedb68b34e0..0b5f479ee1b 100644 --- a/src/buffer/out/textBuffer.hpp +++ b/src/buffer/out/textBuffer.hpp @@ -183,6 +183,7 @@ class TextBuffer final std::optional> positionInfo); const size_t AddPatternRecognizer(const std::wstring_view regexString); + void CopyPatterns(const TextBuffer& OtherBuffer); interval_tree::IntervalTree GetPatterns(const size_t firstRow, const size_t lastRow) const; private: From a968187a923ab24c2bd42e1e38e5867046a1d5fa Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Thu, 22 Oct 2020 10:44:43 -0400 Subject: [PATCH 41/48] (0,0) fix, invalidation fix --- src/cascadia/TerminalControl/TermControl.cpp | 1 + src/cascadia/TerminalControl/TermControl.h | 2 +- src/cascadia/TerminalCore/Terminal.cpp | 10 +++++----- src/cascadia/TerminalCore/Terminal.hpp | 2 +- src/renderer/base/renderer.cpp | 6 ++++-- src/renderer/base/renderer.hpp | 4 ++-- 6 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 7f78466c685..bec4764ab24 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -110,6 +110,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation // This event is explicitly revoked in the destructor: does not need weak_ref auto onReceiveOutputFn = [this](const hstring str) { _terminal->Write(str); + _terminal->ClearPatternTree(); _updatePatternLocations->Run(); }; _connectionOutputEventToken = _connection.TerminalOutput(onReceiveOutputFn); diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 51ed4d1c9c5..fa1b7721354 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -217,7 +217,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation // Track the last hyperlink ID we hovered over uint16_t _lastHoveredId; - interval_tree::IntervalTree::interval _lastHoveredInterval; + std::optional::interval> _lastHoveredInterval; using Timestamp = uint64_t; diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index eddbfe02b0c..9d31c604d0f 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -429,10 +429,10 @@ std::wstring Terminal::GetHyperlinkAtPosition(const COORD position) } // also look through our known pattern locations in our pattern interval tree const auto result = GetHyperlinkIntervalFromPosition(position); - if (result.value == _hyperlinkPatternId) + if (result.has_value() && result.value().value == _hyperlinkPatternId) { - const auto start = result.start; - const auto end = result.stop; + const auto start = result.value().start; + const auto end = result.value().stop; std::wstring uri; const auto startIter = _buffer->GetCellDataAt(_ConvertToBufferCell(start)); @@ -463,7 +463,7 @@ uint16_t Terminal::GetHyperlinkIdAtPosition(const COORD position) // - The position // Return value: // - The interval representing the start and end coordinates -PointTree::interval Terminal::GetHyperlinkIntervalFromPosition(const COORD position) +std::optional Terminal::GetHyperlinkIntervalFromPosition(const COORD position) { const auto results = _patternIntervalTree.findOverlapping(COORD{ position.X + 1, position.Y }, position); if (results.size() > 0) @@ -476,7 +476,7 @@ PointTree::interval Terminal::GetHyperlinkIntervalFromPosition(const COORD posit } } } - return PointTree::interval(); + return std::optional{}; } // Method Description: diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index 2f0d872d25e..c9b30378dfb 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -132,7 +132,7 @@ class Microsoft::Terminal::Core::Terminal final : std::wstring GetHyperlinkAtPosition(const COORD position); uint16_t GetHyperlinkIdAtPosition(const COORD position); - interval_tree::IntervalTree::interval GetHyperlinkIntervalFromPosition(const COORD position); + std::optional::interval> GetHyperlinkIntervalFromPosition(const COORD position); #pragma endregion #pragma region IBaseData(base to IRenderData and IUiaData) diff --git a/src/renderer/base/renderer.cpp b/src/renderer/base/renderer.cpp index 6fd082a4d70..4f77fbcaf85 100644 --- a/src/renderer/base/renderer.cpp +++ b/src/renderer/base/renderer.cpp @@ -915,7 +915,9 @@ void Renderer::_PaintBufferOutputGridLineHelper(_In_ IRenderEngine* const pEngin // For now, we dash underline patterns and switch to regular underline on hover if (_pData->GetPatternId(coordTarget).size() > 0) { - if (_hoveredInterval.start <= til::point{ coordTarget } && til::point{ coordTarget } <= _hoveredInterval.stop) + if (_hoveredInterval.has_value() && + _hoveredInterval.value().start <= til::point{ coordTarget } && + til::point{ coordTarget } <= _hoveredInterval.value().stop) { lines |= IRenderEngine::GridLines::Underline; } @@ -1241,7 +1243,7 @@ void Renderer::ResetErrorStateAndResume() EnablePainting(); } -void Renderer::UpdateLastHoveredInterval(const PointTree::interval newInterval) +void Renderer::UpdateLastHoveredInterval(const std::optional newInterval) { _hoveredInterval = newInterval; } diff --git a/src/renderer/base/renderer.hpp b/src/renderer/base/renderer.hpp index 39a417d5b65..3fb98164f1a 100644 --- a/src/renderer/base/renderer.hpp +++ b/src/renderer/base/renderer.hpp @@ -80,7 +80,7 @@ namespace Microsoft::Console::Render void SetRendererEnteredErrorStateCallback(std::function pfn); void ResetErrorStateAndResume(); - void UpdateLastHoveredInterval(const interval_tree::IntervalTree::interval newInterval); + void UpdateLastHoveredInterval(const std::optional::interval> newInterval); private: std::deque _rgpEngines; @@ -90,7 +90,7 @@ namespace Microsoft::Console::Render std::unique_ptr _pThread; bool _destructing = false; - interval_tree::IntervalTree::interval _hoveredInterval; + std::optional::interval> _hoveredInterval; void _NotifyPaintFrame(); From 6437187350366aecece82c6ee919014ee8a46cfc Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Thu, 22 Oct 2020 13:23:19 -0400 Subject: [PATCH 42/48] small fixes from comments --- src/cascadia/TerminalCore/Terminal.cpp | 8 ++++---- src/renderer/base/renderer.cpp | 2 +- src/renderer/base/renderer.hpp | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 9d31c604d0f..68cb5178c10 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -429,10 +429,10 @@ std::wstring Terminal::GetHyperlinkAtPosition(const COORD position) } // also look through our known pattern locations in our pattern interval tree const auto result = GetHyperlinkIntervalFromPosition(position); - if (result.has_value() && result.value().value == _hyperlinkPatternId) + if (result.has_value() && result->value == _hyperlinkPatternId) { - const auto start = result.value().start; - const auto end = result.value().stop; + const auto start = result->start; + const auto end = result->stop; std::wstring uri; const auto startIter = _buffer->GetCellDataAt(_ConvertToBufferCell(start)); @@ -476,7 +476,7 @@ std::optional Terminal::GetHyperlinkIntervalFromPosition(co } } } - return std::optional{}; + return std::nullopt; } // Method Description: diff --git a/src/renderer/base/renderer.cpp b/src/renderer/base/renderer.cpp index 4f77fbcaf85..7fd64f169e4 100644 --- a/src/renderer/base/renderer.cpp +++ b/src/renderer/base/renderer.cpp @@ -1243,7 +1243,7 @@ void Renderer::ResetErrorStateAndResume() EnablePainting(); } -void Renderer::UpdateLastHoveredInterval(const std::optional newInterval) +void Renderer::UpdateLastHoveredInterval(const std::optional& newInterval) { _hoveredInterval = newInterval; } diff --git a/src/renderer/base/renderer.hpp b/src/renderer/base/renderer.hpp index 3fb98164f1a..ba9963e4eb3 100644 --- a/src/renderer/base/renderer.hpp +++ b/src/renderer/base/renderer.hpp @@ -80,7 +80,7 @@ namespace Microsoft::Console::Render void SetRendererEnteredErrorStateCallback(std::function pfn); void ResetErrorStateAndResume(); - void UpdateLastHoveredInterval(const std::optional::interval> newInterval); + void UpdateLastHoveredInterval(const std::optional::interval>& newInterval); private: std::deque _rgpEngines; From a4b0df8e7b9bbe15d7a30cb32fe917532e4b3959 Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Thu, 22 Oct 2020 16:49:45 -0400 Subject: [PATCH 43/48] real invalidation fix --- src/cascadia/TerminalControl/TermControl.cpp | 1 - src/cascadia/TerminalCore/Terminal.cpp | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index bec4764ab24..7f78466c685 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -110,7 +110,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation // This event is explicitly revoked in the destructor: does not need weak_ref auto onReceiveOutputFn = [this](const hstring str) { _terminal->Write(str); - _terminal->ClearPatternTree(); _updatePatternLocations->Run(); }; _connectionOutputEventToken = _connection.TerminalOutput(onReceiveOutputFn); diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 68cb5178c10..5ca1d3ae525 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -1133,7 +1133,9 @@ bool Terminal::IsCursorBlinkingAllowed() const noexcept void Terminal::UpdatePatterns() noexcept { auto lock = LockForWriting(); + auto oldTree = _patternIntervalTree; _patternIntervalTree = _buffer->GetPatterns(_VisibleStartIndex(), _VisibleEndIndex()); + _InvalidatePatternTree(oldTree); _InvalidatePatternTree(_patternIntervalTree); } From 96e2e5286f2e7fb4448ae0efcd0ce45ba8a1bf1d Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Fri, 23 Oct 2020 10:40:43 -0400 Subject: [PATCH 44/48] ClearPatternTree now also invalidates the tree, call ClearPatternTree before any scrolling happens --- src/cascadia/TerminalControl/TermControl.cpp | 9 ++++++--- src/cascadia/TerminalCore/Terminal.cpp | 4 +++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 7f78466c685..a17d65e18ad 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -1612,6 +1612,9 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation const Windows::Foundation::Point point, const bool isLeftButtonPressed) { + // Clear the regex pattern tree so the renderer does not try to render them while scrolling + _terminal->ClearPatternTree(); + const auto currentOffset = ScrollBar().Value(); // negative = down, positive = up @@ -1640,8 +1643,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation _SetEndSelectionPointAtCursor(point); } - // Clear the regex pattern tree so the renderer does not try to render them while scrolling - _terminal->ClearPatternTree(); _updatePatternLocations->Run(); } @@ -2247,6 +2248,9 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation return; } + // Clear the regex pattern tree so the renderer does not try to render them while scrolling + _terminal->ClearPatternTree(); + _scrollPositionChangedHandlers(viewTop, viewHeight, bufferSize); ScrollBarUpdate update; @@ -2257,7 +2261,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation update.newValue = viewTop; _updateScrollBar->Run(update); - _terminal->ClearPatternTree(); _updatePatternLocations->Run(); } diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 5ca1d3ae525..3ea47a4a578 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -1140,12 +1140,14 @@ void Terminal::UpdatePatterns() noexcept } // Method Description: -// - Clears our interval pattern tree +// - Clears and invalidates the interval pattern tree // - This is called to prevent the renderer from rendering patterns while the // visible region is changing void Terminal::ClearPatternTree() noexcept { + auto oldTree = _patternIntervalTree; _patternIntervalTree = {}; + _InvalidatePatternTree(oldTree); } const std::optional Terminal::GetTabColor() const noexcept From f5ac946e3abf2466397da9de555d05f61f797d28 Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Sat, 24 Oct 2020 12:28:36 -0400 Subject: [PATCH 45/48] move updating pattern tree to _ScrollbarChangeHandler --- src/cascadia/TerminalControl/TermControl.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index a17d65e18ad..0a9b092249e 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -1612,9 +1612,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation const Windows::Foundation::Point point, const bool isLeftButtonPressed) { - // Clear the regex pattern tree so the renderer does not try to render them while scrolling - _terminal->ClearPatternTree(); - const auto currentOffset = ScrollBar().Value(); // negative = down, positive = up @@ -1642,8 +1639,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation // Make sure selection reflects that immediately. _SetEndSelectionPointAtCursor(point); } - - _updatePatternLocations->Run(); } void TermControl::_ScrollbarChangeHandler(Windows::Foundation::IInspectable const& /*sender*/, @@ -1657,6 +1652,9 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation return; } + // Clear the regex pattern tree so the renderer does not try to render them while scrolling + _terminal->ClearPatternTree(); + const auto newValue = static_cast(args.NewValue()); // This is a scroll event that wasn't initiated by the terminal @@ -1668,6 +1666,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation _updateScrollBar->ModifyPending([](auto& update) { update.newValue.reset(); }); + + _updatePatternLocations->Run(); } // Method Description: From 8d355608e599d4e5ec95b5d18966f03e05cf6bc5 Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Wed, 28 Oct 2020 10:35:03 -0400 Subject: [PATCH 46/48] fixes from comments --- src/buffer/out/textBuffer.cpp | 1 + src/cascadia/TerminalCore/terminalrenderdata.cpp | 4 ++-- .../WindowsTerminalUniversal/WindowsTerminalUniversal.vcxproj | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index c4d5db5463c..5f4f90a0aa8 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -2384,6 +2384,7 @@ const size_t TextBuffer::AddPatternRecognizer(const std::wstring_view regexStrin void TextBuffer::CopyPatterns(const TextBuffer& OtherBuffer) { _IdsAndPatterns = OtherBuffer._IdsAndPatterns; + _currentPatternId = OtherBuffer._currentPatternId; } // Method Description: diff --git a/src/cascadia/TerminalCore/terminalrenderdata.cpp b/src/cascadia/TerminalCore/terminalrenderdata.cpp index ae7d26e472f..212f7c7681f 100644 --- a/src/cascadia/TerminalCore/terminalrenderdata.cpp +++ b/src/cascadia/TerminalCore/terminalrenderdata.cpp @@ -141,7 +141,6 @@ const std::wstring Microsoft::Terminal::Core::Terminal::GetHyperlinkCustomId(uin // - The pattern IDs of the location const std::vector Terminal::GetPatternId(const COORD location) const noexcept { - std::vector result{}; // Look through our interval tree for this location const auto intervals = _patternIntervalTree.findOverlapping(COORD{ location.X + 1, location.Y }, location); if (intervals.size() == 0) @@ -150,7 +149,8 @@ const std::vector Terminal::GetPatternId(const COORD location) const noe } else { - for (const auto interval : intervals) + std::vector result{}; + for (const auto& interval : intervals) { result.emplace_back(interval.value); } diff --git a/src/cascadia/WindowsTerminalUniversal/WindowsTerminalUniversal.vcxproj b/src/cascadia/WindowsTerminalUniversal/WindowsTerminalUniversal.vcxproj index 40618d42886..e3422b000e1 100644 --- a/src/cascadia/WindowsTerminalUniversal/WindowsTerminalUniversal.vcxproj +++ b/src/cascadia/WindowsTerminalUniversal/WindowsTerminalUniversal.vcxproj @@ -87,7 +87,7 @@ - $(OpenConsoleDir)\src\inc;$(OpenConsoleDir)\dep;$(OpenConsoleDir)\dep\Console;$(OpenConsoleDir)\dep\Win32K;$(OpenConsoleDir)\dep\gsl\include;$(OpenConsoleDir)\dep\wil\include;$(SolutionDir)\oss\chromium;$(SolutionDir)\oss\fmt\include;$(SolutionDir)\oss\dynamic_bitset;$(SolutionDir)\oss\libpopcnt;$(SolutionDir)\oss\interval_tree;%(AdditionalIncludeDirectories); + $(OpenConsoleDir)\src\inc;$(OpenConsoleDir)\dep;$(OpenConsoleDir)\dep\Console;$(OpenConsoleDir)\dep\Win32K;$(OpenConsoleDir)\dep\gsl\include;$(OpenConsoleDir)\dep\wil\include;$(SolutionDir)\oss\chromium;$(SolutionDir)\oss\fmt\include;$(SolutionDir)\oss\dynamic_bitset;$(SolutionDir)\oss\libpopcnt;%(AdditionalIncludeDirectories); gdi32.lib;dwmapi.lib;Shcore.lib;%(AdditionalDependencies) From 54f48f88eee0e54eee4067700adcdaced4d3a839 Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Wed, 28 Oct 2020 13:33:40 -0400 Subject: [PATCH 47/48] make invalidating neater --- src/buffer/out/textBuffer.cpp | 6 +++--- src/buffer/out/textBuffer.hpp | 2 +- src/cascadia/TerminalCore/Terminal.cpp | 21 +++------------------ 3 files changed, 7 insertions(+), 22 deletions(-) diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index 5f4f90a0aa8..4779cacacec 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -2373,7 +2373,7 @@ void TextBuffer::CopyHyperlinkMaps(const TextBuffer& other) const size_t TextBuffer::AddPatternRecognizer(const std::wstring_view regexString) { ++_currentPatternId; - _IdsAndPatterns.emplace(std::make_pair(_currentPatternId, regexString)); + _idsAndPatterns.emplace(std::make_pair(_currentPatternId, regexString)); return _currentPatternId; } @@ -2383,7 +2383,7 @@ const size_t TextBuffer::AddPatternRecognizer(const std::wstring_view regexStrin // - The other buffer void TextBuffer::CopyPatterns(const TextBuffer& OtherBuffer) { - _IdsAndPatterns = OtherBuffer._IdsAndPatterns; + _idsAndPatterns = OtherBuffer._idsAndPatterns; _currentPatternId = OtherBuffer._currentPatternId; } @@ -2411,7 +2411,7 @@ PointTree TextBuffer::GetPatterns(const size_t firstRow, const size_t lastRow) c } // for each pattern we know of, iterate through the string - for (const auto& idAndPattern : _IdsAndPatterns) + for (const auto& idAndPattern : _idsAndPatterns) { std::wregex regexObj{ idAndPattern.second }; diff --git a/src/buffer/out/textBuffer.hpp b/src/buffer/out/textBuffer.hpp index 0b5f479ee1b..b1e0be1c783 100644 --- a/src/buffer/out/textBuffer.hpp +++ b/src/buffer/out/textBuffer.hpp @@ -233,7 +233,7 @@ class TextBuffer final void _PruneHyperlinks(); - std::unordered_map _IdsAndPatterns; + std::unordered_map _idsAndPatterns; size_t _currentPatternId; #ifdef UNIT_TESTING diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index a279ddf1c82..49ef578a498 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -663,24 +663,9 @@ void Terminal::_InvalidatePatternTree(interval_tree::IntervalTreeGetRenderTarget().TriggerRedraw(Viewport::FromInclusive(region)); - } - else - { - const auto rowSize = gsl::narrow(_buffer->GetRowByOffset(0).size()); - SMALL_RECT region{ start.X, start.Y, rowSize - 1, start.Y }; - _buffer->GetRenderTarget().TriggerRedraw(Viewport::FromInclusive(region)); - for (SHORT row = start.Y + 1; row < end.Y; ++row) - { - region = til::rectangle(0, row, rowSize - 1, row); - _buffer->GetRenderTarget().TriggerRedraw(Viewport::FromInclusive(region)); - } - region = til::rectangle(0, end.Y, end.X, end.Y); - _buffer->GetRenderTarget().TriggerRedraw(Viewport::FromInclusive(region)); - } + const auto rowSize = gsl::narrow(_buffer->GetRowByOffset(0).size()); + SMALL_RECT region{ 0, start.Y, rowSize - 1, end.Y }; + _buffer->GetRenderTarget().TriggerRedraw(Viewport::FromInclusive(region)); } // Method Description: From c6434ae886cdf33c7b22e521a24e7d3e618e9120 Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Wed, 28 Oct 2020 13:52:20 -0400 Subject: [PATCH 48/48] make invalidating neater 2 --- src/cascadia/TerminalCore/Terminal.cpp | 27 +++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 49ef578a498..10dc06c19f0 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -663,9 +663,30 @@ void Terminal::_InvalidatePatternTree(interval_tree::IntervalTree(_buffer->GetRowByOffset(0).size()); - SMALL_RECT region{ 0, start.Y, rowSize - 1, end.Y }; - _buffer->GetRenderTarget().TriggerRedraw(Viewport::FromInclusive(region)); + if (start.Y == end.Y) + { + SMALL_RECT region{ start.X, start.Y, end.X, end.Y }; + _buffer->GetRenderTarget().TriggerRedraw(Viewport::FromInclusive(region)); + } + else + { + const auto rowSize = gsl::narrow(_buffer->GetRowByOffset(0).size()); + + // invalidate the first line + SMALL_RECT region{ start.X, start.Y, rowSize - 1, start.Y }; + _buffer->GetRenderTarget().TriggerRedraw(Viewport::FromInclusive(region)); + + if ((end.Y - start.Y) > 1) + { + // invalidate the lines in between the first and last line + region = til::rectangle(0, start.Y + 1, rowSize - 1, end.Y - 1); + _buffer->GetRenderTarget().TriggerRedraw(Viewport::FromInclusive(region)); + } + + // invalidate the last line + region = til::rectangle(0, end.Y, end.X, end.Y); + _buffer->GetRenderTarget().TriggerRedraw(Viewport::FromInclusive(region)); + } } // Method Description: