Repository: microsoft/terminal
File: src/cascadia/TerminalCore/TerminalSelection.cpp (C++)

Code before:
        break;
    case SelectionDirection::Right:
        _buffer->GetSize().IncrementInBounds(pos);
        pos = _buffer->GetGlyphEnd(pos);
        break;
    case SelectionDirection::Up:
    {
        const auto bufferSize{ _buffer->GetSize() };
        pos = { pos.X, std::clamp(base::ClampSub<short, short>(pos.Y, 1).RawValue(), bufferSize.Top(), bufferSize.BottomInclusive()) };
        break;
    }
    case SelectionDirection::Down:
    {
        const auto bufferSize{ _buffer->GetSize() };
        pos = { pos.X, std::clamp(base::ClampAdd<short, short>(pos.Y, 1).RawValue(), bufferSize.Top(), bufferSize.BottomInclusive()) };
        break;
    }
    }
}

void Terminal::_MoveByWord(SelectionDirection direction, COORD& pos)
{
    switch (direction)
    {
    case SelectionDirection::Left:
        const auto wordStartPos{ _buffer->GetWordStart(pos, _wordDelimiters) };
        if (_buffer->GetSize().CompareInBounds(_selection->pivot, pos) < 0)
        {
            // If we're moving towards the pivot, move one more cell
            pos = wordStartPos;
            _buffer->GetSize().DecrementInBounds(pos);
        }
        else if (wordStartPos == pos)
        {
            // already at the beginning of the current word,
            // move to the beginning of the previous word
            _buffer->GetSize().DecrementInBounds(pos);
            pos = _buffer->GetWordStart(pos, _wordDelimiters);
        }
        else
        {
            // move to the beginning of the current word
            pos = wordStartPos;
        }
        break;
    case SelectionDirection::Right:
        const auto wordEndPos{ _buffer->GetWordEnd(pos, _wordDelimiters) };
        if (_buffer->GetSize().CompareInBounds(pos, _selection->pivot) < 0)
        {
            // If we're moving towards the pivot, move one more cell
            pos = _buffer->GetWordEnd(pos, _wordDelimiters);

Review comment (bug):
IIRC, `_NotifyScrollEvent` doesn't do a `_buffer->GetRenderTarget().TriggerScroll` which is why you might be seeing rendering bugs

Code after:
        break;
    case SelectionExpansion::Buffer:
        _MoveByBuffer(direction, targetPos);
        break;
    }

    // 3. Actually modify the selection
    // NOTE: targetStart doesn't matter here
    bool targetStart = false;
    std::tie(_selection->start, _selection->end) = _PivotSelection(targetPos, targetStart);

    // 4. Scroll (if necessary)
    if (const auto viewport = _GetVisibleViewport(); !viewport.IsInBounds(targetPos))
    {
        if (const auto amtAboveView = viewport.Top() - targetPos.Y; amtAboveView > 0)
        {
            // anchor is above visible viewport, scroll by that amount
            _scrollOffset += amtAboveView;
        }
        else
        {
            // anchor is below visible viewport, scroll by that amount
            const auto amtBelowView = targetPos.Y - viewport.BottomInclusive();
            _scrollOffset -= amtBelowView;
        }
        _NotifyScrollEvent();
        _buffer->GetRenderTarget().TriggerScroll();
    }
}

void Terminal::_MoveByChar(SelectionDirection direction, COORD& pos)
{
    switch (direction)
    {
    case SelectionDirection::Left:
        _buffer->GetSize().DecrementInBounds(pos);
        pos = _buffer->GetGlyphStart(pos);
        break;
    case SelectionDirection::Right:
        _buffer->GetSize().IncrementInBounds(pos);
        pos = _buffer->GetGlyphEnd(pos);
        break;
    case SelectionDirection::Up:
    {
        const auto bufferSize{ _buffer->GetSize() };
        pos = { pos.X, std::clamp(base::ClampSub<short, short>(pos.Y, 1).RawValue(), bufferSize.Top(), bufferSize.BottomInclusive()) };
        break;
    }
    case SelectionDirection::Down:
    {
        const auto bufferSize{ _buffer->GetSize() };