mirror of
https://github.com/microsoft/terminal.git
synced 2025-12-11 13:56:33 -06:00
## Summary of the Pull Request Fixes a bug where search would not scroll to results just below the viewport. This was caused by code intended to scroll the search result in such a way that it isn't covered by the search box. The scroll offset is calculated in `TermControl::_calculateSearchScrollOffset()` then handed down in the `SearchRequest` when conducting a search. This would get to `Terminal::ScrollToSearchHighlight()` where the offset is applied to the search result's position so that we would scroll to the adjusted position. The adjustment was overly aggressive in that it would apply it to both "start" and "end". In reality, we don't need to apply it to "end" because it wouldn't be covered by the search box (we only scroll to end if it's past the end of the current view anyways). The fix applies the adjustment only to "start" and only does so if it's actually in the first few rows that would be covered by the search box. That unveiled another bug where `Terminal::_ScrollToPoints()` would also be too aggressive about scrolling the "end" into view. In some testing, it would generally end up scrolling to the end of the buffer. To fix this cascading bug, I just had `_ScrollToPoints()` just call `Terminal::_ScrollToPoint()` (singular, not plural) which is consistently used throughout the Terminal code for selection (so it's battle tested). `_ScrollToPoints()` was kept since it's still used for accessibility when selecting a new region to keep the new selection in view. It's also just a nice wrapper that ensures a range is visible (or at least as much as it could be). ## References and Relevant Issues Scroll offset was added in #17516 ## Validation Steps Performed ✅ search results that would be covered by the search box are still adjusted ✅ search results that are past the end of the view become visible ✅ UIA still selects properly and brings the selection into view ## PR Checklist Duncan reported this bug internally, but there doesn't seem to be one on the repo.
220 lines
6.4 KiB
C++
220 lines
6.4 KiB
C++
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT license.
|
|
|
|
#include "pch.h"
|
|
#include "Terminal.hpp"
|
|
|
|
using namespace Microsoft::Terminal::Core;
|
|
using namespace Microsoft::Console::Types;
|
|
using namespace Microsoft::Console::Render;
|
|
|
|
Viewport Terminal::GetViewport() noexcept
|
|
{
|
|
return _GetVisibleViewport();
|
|
}
|
|
|
|
til::point Terminal::GetTextBufferEndPosition() const noexcept
|
|
{
|
|
// We use the end line of mutableViewport as the end
|
|
// of the text buffer, it always moves with the written
|
|
// text
|
|
return { _GetMutableViewport().Width() - 1, ViewEndIndex() };
|
|
}
|
|
|
|
TextBuffer& Terminal::GetTextBuffer() const noexcept
|
|
{
|
|
return _activeBuffer();
|
|
}
|
|
|
|
const FontInfo& Terminal::GetFontInfo() const noexcept
|
|
{
|
|
_assertLocked();
|
|
return _fontInfo;
|
|
}
|
|
|
|
void Terminal::SetFontInfo(const FontInfo& fontInfo)
|
|
{
|
|
_assertLocked();
|
|
_fontInfo = fontInfo;
|
|
}
|
|
|
|
TimerDuration Terminal::GetBlinkInterval() noexcept
|
|
{
|
|
if (!_cursorBlinkInterval)
|
|
{
|
|
const auto enabled = GetSystemMetrics(SM_CARETBLINKINGENABLED);
|
|
const auto interval = GetCaretBlinkTime();
|
|
// >10s --> no blinking. The limit is arbitrary, because technically the valid range
|
|
// on Windows is 200-1200ms. GetCaretBlinkTime() returns INFINITE for no blinking, 0 for errors.
|
|
_cursorBlinkInterval = enabled && interval <= 10000 ? std ::chrono::milliseconds(interval) : TimerDuration::max();
|
|
}
|
|
return *_cursorBlinkInterval;
|
|
}
|
|
|
|
ULONG Terminal::GetCursorPixelWidth() const noexcept
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
bool Terminal::IsGridLineDrawingAllowed() noexcept
|
|
{
|
|
return true;
|
|
}
|
|
|
|
std::wstring Microsoft::Terminal::Core::Terminal::GetHyperlinkUri(uint16_t id) const
|
|
{
|
|
return _activeBuffer().GetHyperlinkUriFromId(id);
|
|
}
|
|
|
|
std::wstring Microsoft::Terminal::Core::Terminal::GetHyperlinkCustomId(uint16_t id) const
|
|
{
|
|
return _activeBuffer().GetCustomIdFromId(id);
|
|
}
|
|
|
|
// Method Description:
|
|
// - Gets the regex pattern ids of a location
|
|
// Arguments:
|
|
// - The location
|
|
// Return value:
|
|
// - The pattern IDs of the location
|
|
std::vector<size_t> Terminal::GetPatternId(const til::point location) const
|
|
{
|
|
_assertLocked();
|
|
|
|
// Look through our interval tree for this location
|
|
const auto intervals = _patternIntervalTree.findOverlapping({ location.x + 1, location.y }, location);
|
|
if (intervals.size() == 0)
|
|
{
|
|
return {};
|
|
}
|
|
else
|
|
{
|
|
std::vector<size_t> result{};
|
|
for (const auto& interval : intervals)
|
|
{
|
|
result.emplace_back(interval.value);
|
|
}
|
|
return result;
|
|
}
|
|
return {};
|
|
}
|
|
|
|
std::pair<COLORREF, COLORREF> Terminal::GetAttributeColors(const TextAttribute& attr) const noexcept
|
|
{
|
|
return GetRenderSettings().GetAttributeColors(attr);
|
|
}
|
|
|
|
std::span<const til::point_span> Terminal::GetSelectionSpans() const noexcept
|
|
try
|
|
{
|
|
if (_selection.generation() != _lastSelectionGeneration)
|
|
{
|
|
_lastSelectionSpans = _GetSelectionSpans();
|
|
_lastSelectionGeneration = _selection.generation();
|
|
}
|
|
|
|
return _lastSelectionSpans;
|
|
}
|
|
catch (...)
|
|
{
|
|
LOG_CAUGHT_EXCEPTION();
|
|
return {};
|
|
}
|
|
|
|
// Method Description:
|
|
// - Helper to determine the search highlights in the buffer. Used for rendering.
|
|
// Return Value:
|
|
// - A vector of rectangles representing the regions to select, line by line. They are absolute coordinates relative to the buffer origin.
|
|
std::span<const til::point_span> Terminal::GetSearchHighlights() const noexcept
|
|
{
|
|
_assertLocked();
|
|
return _searchHighlights;
|
|
}
|
|
|
|
const til::point_span* Terminal::GetSearchHighlightFocused() const noexcept
|
|
{
|
|
_assertLocked();
|
|
if (_searchHighlightFocused < _searchHighlights.size())
|
|
{
|
|
return &til::at(_searchHighlights, _searchHighlightFocused);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
// Method Description:
|
|
// - If necessary, scrolls the viewport such that the start point is in the
|
|
// viewport, and if that's already the case, also brings the end point inside
|
|
// the viewport
|
|
// Arguments:
|
|
// - coordStart - The start point
|
|
// - coordEnd - The end point
|
|
// Return Value:
|
|
// - The updated scroll offset
|
|
til::CoordType Terminal::_ScrollToPoints(const til::point coordStart, const til::point coordEnd)
|
|
{
|
|
if (coordStart.y < _VisibleStartIndex())
|
|
{
|
|
_ScrollToPoint(coordStart);
|
|
}
|
|
else if (coordEnd.y > _VisibleEndIndex())
|
|
{
|
|
_ScrollToPoint(coordEnd);
|
|
}
|
|
return _VisibleStartIndex();
|
|
}
|
|
|
|
// Method Description:
|
|
// - selects the region from coordStart to coordEnd
|
|
// Arguments:
|
|
// - coordStart - The start point (inclusive)
|
|
// - coordEnd - The end point (inclusive)
|
|
void Terminal::SelectNewRegion(const til::point coordStart, const til::point coordEnd)
|
|
{
|
|
const auto newScrollOffset = _ScrollToPoints(coordStart, coordEnd);
|
|
|
|
// update the selection coordinates so they're relative to the new scroll-offset
|
|
const auto newCoordStart = til::point{ coordStart.x, coordStart.y - newScrollOffset };
|
|
const auto newCoordEnd = til::point{ coordEnd.x, coordEnd.y - newScrollOffset };
|
|
SetSelectionAnchor(newCoordStart);
|
|
SetSelectionEnd(newCoordEnd, SelectionExpansion::Char);
|
|
_activeBuffer().TriggerSelection();
|
|
}
|
|
|
|
std::wstring_view Terminal::GetConsoleTitle() const noexcept
|
|
{
|
|
_assertLocked();
|
|
if (_title.has_value())
|
|
{
|
|
return *_title;
|
|
}
|
|
return _startingTitle;
|
|
}
|
|
|
|
// Method Description:
|
|
// - Lock the terminal for reading the contents of the buffer. Ensures that the
|
|
// contents of the terminal won't be changed in the middle of a paint
|
|
// operation.
|
|
// Callers should make sure to also call Terminal::UnlockConsole once
|
|
// they're done with any querying they need to do.
|
|
void Terminal::LockConsole() noexcept
|
|
{
|
|
_readWriteLock.lock();
|
|
}
|
|
|
|
// Method Description:
|
|
// - Unlocks the terminal after a call to Terminal::LockConsole.
|
|
void Terminal::UnlockConsole() noexcept
|
|
{
|
|
_readWriteLock.unlock();
|
|
}
|
|
|
|
bool Terminal::IsUiaDataInitialized() const noexcept
|
|
{
|
|
// GH#11135: Windows Terminal needs to create and return an automation peer
|
|
// when a screen reader requests it. However, the terminal might not be fully
|
|
// initialized yet. So we use this to check if any crucial components of
|
|
// UiaData are not yet initialized.
|
|
_assertLocked();
|
|
return !!_mainBuffer;
|
|
}
|