Repository: microsoft/terminal
File: src/buffer/out/textBuffer.cpp (C++)

Code before:
                    }
                }
            }
        }
    }

    // Finish copying buffer attributes to remaining rows below the last
    // printable character. This is to fix the `color 2f` scenario, where you
    // change the buffer colors then resize and everything below the last
    // printable char gets reset. See GH #12567
    auto newRowY = newCursor.GetPosition().y + 1;
    const auto newHeight = newBuffer.GetSize().Height();
    const auto oldHeight = oldBuffer.GetSize().Height();
    for (;
         iOldRow < oldHeight && newRowY < newHeight;
         iOldRow++)
    {
        const auto& row = oldBuffer.GetRowByOffset(iOldRow);

        // Optimization: Since all these rows are below the last printable char,
        // we can reasonably assume that they are filled with just spaces.
        // That's convenient, we can just copy the attr row from the old buffer
        // into the new one, and resize the row to match. We'll rely on the
        // behavior of ATTR_ROW::Resize to trim down when narrower, or extend
        // the last attr when wider.
        auto& newRow = newBuffer.GetRowByOffset(newRowY);
        const auto newWidth = newBuffer.GetLineWidth(newRowY);
        newRow.TransferAttributes(row.Attributes(), newWidth);

        newRowY++;
    }

    if (SUCCEEDED(hr))
    {
        // 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.
        if (fFoundCursorPos)
        {
            newCursor.SetPosition(cNewCursorPos);
        }
        else
        {
            // Advance the cursor to the same offset as before
            // get the number of newlines and spaces between the old end of text and the old cursor,
            //   then advance that many newlines and chars
            auto iNewlines = cOldCursorPos.y - cOldLastChar.y;

Review comment (suggestion):
I added this, because it's a simple change, but speeds up reflow of empty buffers quite a lot. It needs +1 because `_estimateOffsetOfLastCommittedRow` returns the offset of the last row and not the height (= past the end) of the buffer.

Code after:
                    // So we insert one more newline so a continued reflow of this buffer by resizing larger will
                    // continue to look as the original output intended with the newline data.
                    // After this fix, it looks like this:
                    // |aaaaaaaaaaaaaaaaaaa| no wrap at the end (preserved hard newline)
                    // |                   |
                    //  ^ and the cursor is now here.
                    const auto coordNewCursor = newCursor.GetPosition();
                    if (coordNewCursor.x == 0 && coordNewCursor.y > 0)
                    {
                        if (newBuffer.GetRowByOffset(coordNewCursor.y - 1).WasWrapForced())
                        {
                            hr = newBuffer.NewlineCursor() ? hr : E_OUTOFMEMORY;
                        }
                    }
                }
            }
        }
    }

    // Finish copying buffer attributes to remaining rows below the last
    // printable character. This is to fix the `color 2f` scenario, where you
    // change the buffer colors then resize and everything below the last
    // printable char gets reset. See GH #12567
    auto newRowY = newCursor.GetPosition().y + 1;
    const auto newHeight = newBuffer.GetSize().Height();
    const auto oldHeight = oldBuffer._estimateOffsetOfLastCommittedRow() + 1;
    for (;
         iOldRow < oldHeight && newRowY < newHeight;
         iOldRow++)
    {
        const auto& row = oldBuffer.GetRowByOffset(iOldRow);

        // Optimization: Since all these rows are below the last printable char,
        // we can reasonably assume that they are filled with just spaces.
        // That's convenient, we can just copy the attr row from the old buffer
        // into the new one, and resize the row to match. We'll rely on the
        // behavior of ATTR_ROW::Resize to trim down when narrower, or extend
        // the last attr when wider.
        auto& newRow = newBuffer.GetRowByOffset(newRowY);
        const auto newWidth = newBuffer.GetLineWidth(newRowY);
        newRow.TransferAttributes(row.Attributes(), newWidth);

        newRowY++;
    }

    if (SUCCEEDED(hr))
    {
        // Finish copying remaining parameters from the old text buffer to the new one
        newBuffer.CopyProperties(oldBuffer);
        newBuffer.CopyHyperlinkMaps(oldBuffer);
        newBuffer.CopyPatterns(oldBuffer);