Repository: microsoft/terminal
File: src/types/UiaTextRangeBase.cpp (C++)

Code before:
            textData.reserve(gsl::narrow<size_t>(maxLength));
        }
        else if (textRects.size() == 1)
        {
            const auto rect = textRects.front();
            textData.reserve(base::ClampedNumeric<size_t>(rect.right) - rect.left + 1);
        }
        else
        {
            const auto firstRect = textRects.front();
            const auto firstWidth = firstRect.right - firstRect.left + 1;

            const auto lastRect = textRects.back();
            const auto lastWidth = lastRect.right - lastRect.left + 1;

            const auto otherWidth = base::ClampedNumeric<size_t>(textRects.size()) - 2 * bufferSize.Width();
            textData.reserve(firstWidth + otherWidth + lastWidth);
        }

        // actually add the text
        for (const auto& rect : textRects)
        {
            const auto bufferData = buffer.GetText(true,
                                                   false,
                                                   { rect });
            const auto& text = bufferData.text.front();
            textData += text;
            if (rect != textRects.back())
            {
                textData.push_back(UNICODE_CARRIAGERETURN);
                textData.push_back(UNICODE_LINEFEED);
            }

            if (maxLength > 0 && textData.size() >= gsl::narrow_cast<size_t>(maxLength))
            {
                textData.resize(maxLength);
                break;
            }
        }
    }

    return textData;
}
#pragma warning(pop)

IFACEMETHODIMP UiaTextRangeBase::Move(_In_ TextUnit unit,
                                      _In_ int count,
                                      _Out_ int* pRetVal) noexcept
try
{
    RETURN_HR_IF(E_INVALIDARG, pRetVal == nullptr);

Review comment (suggestion):
I'm worried that moving from "request multiple rects simultaneously" to "request one rect at a time" will break TextBuffer's understanding of line wrapping. In general, does this change cause other trouble?

I'm sorry I pushed you into overcomplicating this PR. That's on me. I think Leonard was right -- we should keep what we had and fix it up in whatever ways we can. The cost of extra copying is minimal and won't really be a problem.

Code after:
        const auto textRects = buffer.GetTextRects(_start, inclusiveEnd, _blockRange, true);
        const auto bufferData = buffer.GetText(true,
                                               false,
                                               textRects);
        const size_t textDataSize = bufferData.text.size() * bufferSize.Width();
        textData.reserve(textDataSize);
        for (const auto& text : bufferData.text)
        {
            if (textData.size() >= maxLengthAsSize)
            {
                // early exit; we're already at/past max length
                break;
            }
            textData += text;
        }

        // only use maxLength to resize down.
        // if maxLength > size, we don't want to resize and append unnecessary L'\0'.
        if (textData.size() > maxLengthAsSize)
        {
            textData.resize(maxLengthAsSize);
        }
    }

    return textData;
}
#pragma warning(pop)

IFACEMETHODIMP UiaTextRangeBase::Move(_In_ TextUnit unit,
                                      _In_ int count,
                                      _Out_ int* pRetVal) noexcept
try
{
    RETURN_HR_IF(E_INVALIDARG, pRetVal == nullptr);
    *pRetVal = 0;

    _pData->LockConsole();
    auto Unlock = wil::scope_exit([&]() noexcept {
        _pData->UnlockConsole();
    });
    RETURN_HR_IF(E_FAIL, !_pData->IsUiaDataInitialized());

    // We can abstract this movement by moving _start
    // GH#7342: check if we're past the documentEnd
    // If so, clamp each endpoint to the end of the document.
    constexpr auto endpoint = TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start;
    const auto bufferSize{ _pData->GetTextBuffer().GetSize() };
    const auto documentEnd = _getDocumentEnd();
    if (_start > documentEnd)
    {
        _start = documentEnd;