mirror of
https://github.com/microsoft/terminal.git
synced 2025-12-10 18:43:54 -06:00
Stop scrolling on output when search is open (#17885)
* Don't reset the position entirely when changing the needle * Don't change the scroll position when output arrives * Don't interfere with the search when output arrives constantly Closes #17301 ## Validation Steps Performed * In pwsh, run `10000..20000 | % { sleep 0.25; $_ }` * You can search for e.g. `1004` and it'll find 10 results. ✅ * You can scroll up and down past it and it won't snap back when new output arrives. ✅ * `while ($true) { Write-Host -NoNewline "`e[Ha"; sleep 0.0001; }` * You can cycle between the hits effortlessly. ✅ (This tests that the constantly reset `OutputIdle` event won't interfere.) * On input change, the focused result is near the previous one. ✅
This commit is contained in:
parent
0ce654eaf6
commit
d9131c6889
@ -16,7 +16,7 @@ bool Search::IsStale(const Microsoft::Console::Render::IRenderData& renderData,
|
|||||||
_lastMutationId != renderData.GetTextBuffer().GetLastMutationId();
|
_lastMutationId != renderData.GetTextBuffer().GetLastMutationId();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Search::Reset(Microsoft::Console::Render::IRenderData& renderData, const std::wstring_view& needle, SearchFlag flags, bool reverse)
|
void Search::Reset(Microsoft::Console::Render::IRenderData& renderData, const std::wstring_view& needle, SearchFlag flags, bool reverse)
|
||||||
{
|
{
|
||||||
const auto& textBuffer = renderData.GetTextBuffer();
|
const auto& textBuffer = renderData.GetTextBuffer();
|
||||||
|
|
||||||
@ -30,15 +30,15 @@ bool Search::Reset(Microsoft::Console::Render::IRenderData& renderData, const st
|
|||||||
_results = std::move(result).value_or(std::vector<til::point_span>{});
|
_results = std::move(result).value_or(std::vector<til::point_span>{});
|
||||||
_index = reverse ? gsl::narrow_cast<ptrdiff_t>(_results.size()) - 1 : 0;
|
_index = reverse ? gsl::narrow_cast<ptrdiff_t>(_results.size()) - 1 : 0;
|
||||||
_step = reverse ? -1 : 1;
|
_step = reverse ? -1 : 1;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Search::MoveToCurrentSelection()
|
|
||||||
{
|
|
||||||
if (_renderData->IsSelectionActive())
|
if (_renderData->IsSelectionActive())
|
||||||
{
|
{
|
||||||
MoveToPoint(_renderData->GetTextBuffer().ScreenToBufferPosition(_renderData->GetSelectionAnchor()));
|
MoveToPoint(_renderData->GetTextBuffer().ScreenToBufferPosition(_renderData->GetSelectionAnchor()));
|
||||||
}
|
}
|
||||||
|
else if (const auto span = _renderData->GetSearchHighlightFocused())
|
||||||
|
{
|
||||||
|
MoveToPoint(_step > 0 ? span->start : span->end);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Search::MoveToPoint(const til::point anchor) noexcept
|
void Search::MoveToPoint(const til::point anchor) noexcept
|
||||||
|
|||||||
@ -36,9 +36,8 @@ public:
|
|||||||
Search() = default;
|
Search() = default;
|
||||||
|
|
||||||
bool IsStale(const Microsoft::Console::Render::IRenderData& renderData, const std::wstring_view& needle, SearchFlag flags) const noexcept;
|
bool IsStale(const Microsoft::Console::Render::IRenderData& renderData, const std::wstring_view& needle, SearchFlag flags) const noexcept;
|
||||||
bool Reset(Microsoft::Console::Render::IRenderData& renderData, const std::wstring_view& needle, SearchFlag flags, bool reverse);
|
void Reset(Microsoft::Console::Render::IRenderData& renderData, const std::wstring_view& needle, SearchFlag flags, bool reverse);
|
||||||
|
|
||||||
void MoveToCurrentSelection();
|
|
||||||
void MoveToPoint(til::point anchor) noexcept;
|
void MoveToPoint(til::point anchor) noexcept;
|
||||||
void MovePastPoint(til::point anchor) noexcept;
|
void MovePastPoint(til::point anchor) noexcept;
|
||||||
void FindNext(bool reverse) noexcept;
|
void FindNext(bool reverse) noexcept;
|
||||||
|
|||||||
@ -1697,38 +1697,41 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||||||
SearchResults ControlCore::Search(SearchRequest request)
|
SearchResults ControlCore::Search(SearchRequest request)
|
||||||
{
|
{
|
||||||
const auto lock = _terminal->LockForWriting();
|
const auto lock = _terminal->LockForWriting();
|
||||||
|
|
||||||
SearchFlag flags{};
|
SearchFlag flags{};
|
||||||
WI_SetFlagIf(flags, SearchFlag::CaseInsensitive, !request.CaseSensitive);
|
WI_SetFlagIf(flags, SearchFlag::CaseInsensitive, !request.CaseSensitive);
|
||||||
WI_SetFlagIf(flags, SearchFlag::RegularExpression, request.RegularExpression);
|
WI_SetFlagIf(flags, SearchFlag::RegularExpression, request.RegularExpression);
|
||||||
const auto searchInvalidated = _searcher.IsStale(*_terminal.get(), request.Text, flags);
|
const auto searchInvalidated = _searcher.IsStale(*_terminal.get(), request.Text, flags);
|
||||||
|
|
||||||
if (searchInvalidated || !request.Reset)
|
if (searchInvalidated || !request.ResetOnly)
|
||||||
{
|
{
|
||||||
std::vector<til::point_span> oldResults;
|
std::vector<til::point_span> oldResults;
|
||||||
|
til::point_span oldFocused;
|
||||||
|
|
||||||
|
if (const auto focused = _terminal->GetSearchHighlightFocused())
|
||||||
|
{
|
||||||
|
oldFocused = *focused;
|
||||||
|
}
|
||||||
|
|
||||||
if (searchInvalidated)
|
if (searchInvalidated)
|
||||||
{
|
{
|
||||||
oldResults = _searcher.ExtractResults();
|
oldResults = _searcher.ExtractResults();
|
||||||
_searcher.Reset(*_terminal.get(), request.Text, flags, !request.GoForward);
|
_searcher.Reset(*_terminal.get(), request.Text, flags, !request.GoForward);
|
||||||
|
|
||||||
if (SnapSearchResultToSelection())
|
|
||||||
{
|
|
||||||
_searcher.MoveToCurrentSelection();
|
|
||||||
SnapSearchResultToSelection(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
_terminal->SetSearchHighlights(_searcher.Results());
|
_terminal->SetSearchHighlights(_searcher.Results());
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (!request.ResetOnly)
|
||||||
{
|
{
|
||||||
_searcher.FindNext(!request.GoForward);
|
_searcher.FindNext(!request.GoForward);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (const auto idx = _searcher.CurrentMatch(); idx >= 0)
|
_terminal->SetSearchHighlightFocused(gsl::narrow<size_t>(std::max<ptrdiff_t>(0, _searcher.CurrentMatch())));
|
||||||
{
|
|
||||||
_terminal->SetSearchHighlightFocused(gsl::narrow<size_t>(idx), request.ScrollOffset);
|
|
||||||
}
|
|
||||||
_renderer->TriggerSearchHighlight(oldResults);
|
_renderer->TriggerSearchHighlight(oldResults);
|
||||||
|
|
||||||
|
if (const auto focused = _terminal->GetSearchHighlightFocused(); focused && *focused != oldFocused)
|
||||||
|
{
|
||||||
|
_terminal->ScrollToSearchHighlight(request.ScrollOffset);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t totalMatches = 0;
|
int32_t totalMatches = 0;
|
||||||
@ -1756,27 +1759,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||||||
{
|
{
|
||||||
const auto lock = _terminal->LockForWriting();
|
const auto lock = _terminal->LockForWriting();
|
||||||
_terminal->SetSearchHighlights({});
|
_terminal->SetSearchHighlights({});
|
||||||
_terminal->SetSearchHighlightFocused({}, 0);
|
_terminal->SetSearchHighlightFocused(0);
|
||||||
_renderer->TriggerSearchHighlight(_searcher.Results());
|
_renderer->TriggerSearchHighlight(_searcher.Results());
|
||||||
_searcher = {};
|
_searcher = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Method Description:
|
|
||||||
// - Tells ControlCore to snap the current search result index to currently
|
|
||||||
// selected text if the search was started using it.
|
|
||||||
void ControlCore::SnapSearchResultToSelection(bool shouldSnap) noexcept
|
|
||||||
{
|
|
||||||
_snapSearchResultToSelection = shouldSnap;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method Description:
|
|
||||||
// - Returns true, if we should snap the current search result index to
|
|
||||||
// the currently selected text after a new search is started, else false.
|
|
||||||
bool ControlCore::SnapSearchResultToSelection() const noexcept
|
|
||||||
{
|
|
||||||
return _snapSearchResultToSelection;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ControlCore::Close()
|
void ControlCore::Close()
|
||||||
{
|
{
|
||||||
if (!_IsClosing())
|
if (!_IsClosing())
|
||||||
|
|||||||
@ -228,8 +228,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||||||
SearchResults Search(SearchRequest request);
|
SearchResults Search(SearchRequest request);
|
||||||
const std::vector<til::point_span>& SearchResultRows() const noexcept;
|
const std::vector<til::point_span>& SearchResultRows() const noexcept;
|
||||||
void ClearSearch();
|
void ClearSearch();
|
||||||
void SnapSearchResultToSelection(bool snap) noexcept;
|
|
||||||
bool SnapSearchResultToSelection() const noexcept;
|
|
||||||
|
|
||||||
void LeftClickOnTerminal(const til::point terminalPosition,
|
void LeftClickOnTerminal(const til::point terminalPosition,
|
||||||
const int numberOfClicks,
|
const int numberOfClicks,
|
||||||
|
|||||||
@ -55,7 +55,7 @@ namespace Microsoft.Terminal.Control
|
|||||||
Boolean GoForward;
|
Boolean GoForward;
|
||||||
Boolean CaseSensitive;
|
Boolean CaseSensitive;
|
||||||
Boolean RegularExpression;
|
Boolean RegularExpression;
|
||||||
Boolean Reset;
|
Boolean ResetOnly;
|
||||||
Int32 ScrollOffset;
|
Int32 ScrollOffset;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -148,7 +148,6 @@ namespace Microsoft.Terminal.Control
|
|||||||
|
|
||||||
SearchResults Search(SearchRequest request);
|
SearchResults Search(SearchRequest request);
|
||||||
void ClearSearch();
|
void ClearSearch();
|
||||||
Boolean SnapSearchResultToSelection;
|
|
||||||
|
|
||||||
Microsoft.Terminal.Core.Color ForegroundColor { get; };
|
Microsoft.Terminal.Core.Color ForegroundColor { get; };
|
||||||
Microsoft.Terminal.Core.Color BackgroundColor { get; };
|
Microsoft.Terminal.Core.Color BackgroundColor { get; };
|
||||||
|
|||||||
@ -576,7 +576,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||||||
// but since code paths differ, extra work is required to ensure correctness.
|
// but since code paths differ, extra work is required to ensure correctness.
|
||||||
if (!_core.HasMultiLineSelection())
|
if (!_core.HasMultiLineSelection())
|
||||||
{
|
{
|
||||||
_core.SnapSearchResultToSelection(true);
|
|
||||||
const auto selectedLine{ _core.SelectedText(true) };
|
const auto selectedLine{ _core.SelectedText(true) };
|
||||||
_searchBox->PopulateTextbox(selectedLine);
|
_searchBox->PopulateTextbox(selectedLine);
|
||||||
}
|
}
|
||||||
@ -3861,7 +3860,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||||||
const auto goForward = _searchBox->GoForward();
|
const auto goForward = _searchBox->GoForward();
|
||||||
const auto caseSensitive = _searchBox->CaseSensitive();
|
const auto caseSensitive = _searchBox->CaseSensitive();
|
||||||
const auto regularExpression = _searchBox->RegularExpression();
|
const auto regularExpression = _searchBox->RegularExpression();
|
||||||
const auto request = SearchRequest{ text, goForward, caseSensitive, regularExpression, true, _calculateSearchScrollOffset() };
|
const auto request = SearchRequest{ text, goForward, caseSensitive, regularExpression, true, _searchScrollOffset };
|
||||||
_handleSearchResults(_core.Search(request));
|
_handleSearchResults(_core.Search(request));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1262,15 +1262,17 @@ void Terminal::SetSearchHighlights(const std::vector<til::point_span>& highlight
|
|||||||
// Method Description:
|
// Method Description:
|
||||||
// - Stores the focused search highlighted region in the terminal
|
// - Stores the focused search highlighted region in the terminal
|
||||||
// - If the region isn't empty, it will be brought into view
|
// - If the region isn't empty, it will be brought into view
|
||||||
void Terminal::SetSearchHighlightFocused(const size_t focusedIdx, til::CoordType searchScrollOffset)
|
void Terminal::SetSearchHighlightFocused(const size_t focusedIdx) noexcept
|
||||||
{
|
{
|
||||||
_assertLocked();
|
_assertLocked();
|
||||||
_searchHighlightFocused = focusedIdx;
|
_searchHighlightFocused = focusedIdx;
|
||||||
|
}
|
||||||
|
|
||||||
// bring the focused region into the view if the index is in valid range
|
void Terminal::ScrollToSearchHighlight(til::CoordType searchScrollOffset)
|
||||||
if (focusedIdx < _searchHighlights.size())
|
{
|
||||||
|
if (_searchHighlightFocused < _searchHighlights.size())
|
||||||
{
|
{
|
||||||
const auto focused = til::at(_searchHighlights, focusedIdx);
|
const auto focused = til::at(_searchHighlights, _searchHighlightFocused);
|
||||||
const auto adjustedStart = til::point{ focused.start.x, std::max(0, focused.start.y - searchScrollOffset) };
|
const auto adjustedStart = til::point{ focused.start.x, std::max(0, focused.start.y - searchScrollOffset) };
|
||||||
const auto adjustedEnd = til::point{ focused.end.x, std::max(0, focused.end.y - searchScrollOffset) };
|
const auto adjustedEnd = til::point{ focused.end.x, std::max(0, focused.end.y - searchScrollOffset) };
|
||||||
_ScrollToPoints(adjustedStart, adjustedEnd);
|
_ScrollToPoints(adjustedStart, adjustedEnd);
|
||||||
|
|||||||
@ -234,7 +234,8 @@ public:
|
|||||||
void SetClearQuickFixCallback(std::function<void()> pfn) noexcept;
|
void SetClearQuickFixCallback(std::function<void()> pfn) noexcept;
|
||||||
void SetWindowSizeChangedCallback(std::function<void(int32_t, int32_t)> pfn) noexcept;
|
void SetWindowSizeChangedCallback(std::function<void(int32_t, int32_t)> pfn) noexcept;
|
||||||
void SetSearchHighlights(const std::vector<til::point_span>& highlights) noexcept;
|
void SetSearchHighlights(const std::vector<til::point_span>& highlights) noexcept;
|
||||||
void SetSearchHighlightFocused(const size_t focusedIdx, til::CoordType searchScrollOffset);
|
void SetSearchHighlightFocused(size_t focusedIdx) noexcept;
|
||||||
|
void ScrollToSearchHighlight(til::CoordType searchScrollOffset);
|
||||||
|
|
||||||
void BlinkCursor() noexcept;
|
void BlinkCursor() noexcept;
|
||||||
void SetCursorOn(const bool isOn) noexcept;
|
void SetCursorOn(const bool isOn) noexcept;
|
||||||
|
|||||||
@ -57,7 +57,6 @@ INT_PTR CALLBACK FindDialogProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM l
|
|||||||
if (searcher.IsStale(gci.renderData, lastFindString, flags))
|
if (searcher.IsStale(gci.renderData, lastFindString, flags))
|
||||||
{
|
{
|
||||||
searcher.Reset(gci.renderData, lastFindString, flags, reverse);
|
searcher.Reset(gci.renderData, lastFindString, flags, reverse);
|
||||||
searcher.MoveToCurrentSelection();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user