mirror of
https://github.com/microsoft/terminal.git
synced 2026-04-12 01:01:05 +00:00
My theory was "fuck perf, let's do it right, and figure out perf later". It didn't work. This wraps lines super weird, probably because I'm copying the whole line to the right side of the row, which is then starting to mark it as wrapped. Also like the test doesn't even pass.
223 lines
7.8 KiB
C++
223 lines
7.8 KiB
C++
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT license.
|
|
|
|
#include "precomp.h"
|
|
#include "Row.hpp"
|
|
#include "CharRow.hpp"
|
|
#include "textBuffer.hpp"
|
|
#include "../types/inc/convert.hpp"
|
|
|
|
// Routine Description:
|
|
// - constructor
|
|
// Arguments:
|
|
// - rowId - the row index in the text buffer
|
|
// - rowWidth - the width of the row, cell elements
|
|
// - fillAttribute - the default text attribute
|
|
// - pParent - the text buffer that this row belongs to
|
|
// Return Value:
|
|
// - constructed object
|
|
ROW::ROW(const SHORT rowId, const unsigned short rowWidth, const TextAttribute fillAttribute, TextBuffer* const pParent) :
|
|
_id{ rowId },
|
|
_rowWidth{ rowWidth },
|
|
_charRow{ rowWidth, this },
|
|
_attrRow{ rowWidth, fillAttribute },
|
|
_lineRendition{ LineRendition::SingleWidth },
|
|
_wrapForced{ false },
|
|
_doubleBytePadded{ false },
|
|
_pParent{ pParent }
|
|
{
|
|
}
|
|
|
|
// Routine Description:
|
|
// - Sets all properties of the ROW to default values
|
|
// Arguments:
|
|
// - Attr - The default attribute (color) to fill
|
|
// Return Value:
|
|
// - <none>
|
|
bool ROW::Reset(const TextAttribute Attr)
|
|
{
|
|
_lineRendition = LineRendition::SingleWidth;
|
|
_wrapForced = false;
|
|
_doubleBytePadded = false;
|
|
_charRow.Reset();
|
|
try
|
|
{
|
|
_attrRow.Reset(Attr);
|
|
}
|
|
catch (...)
|
|
{
|
|
LOG_CAUGHT_EXCEPTION();
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Routine Description:
|
|
// - resizes ROW to new width
|
|
// Arguments:
|
|
// - width - the new width, in cells
|
|
// Return Value:
|
|
// - S_OK if successful, otherwise relevant error
|
|
[[nodiscard]] HRESULT ROW::Resize(const unsigned short width)
|
|
{
|
|
RETURN_IF_FAILED(_charRow.Resize(width));
|
|
try
|
|
{
|
|
_attrRow.Resize(width);
|
|
}
|
|
CATCH_RETURN();
|
|
|
|
_rowWidth = width;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
// Routine Description:
|
|
// - clears char data in column in row
|
|
// Arguments:
|
|
// - column - 0-indexed column index
|
|
// Return Value:
|
|
// - <none>
|
|
void ROW::ClearColumn(const size_t column)
|
|
{
|
|
THROW_HR_IF(E_INVALIDARG, column >= _charRow.size());
|
|
_charRow.ClearCell(column);
|
|
}
|
|
|
|
UnicodeStorage& ROW::GetUnicodeStorage() noexcept
|
|
{
|
|
return _pParent->GetUnicodeStorage();
|
|
}
|
|
|
|
const UnicodeStorage& ROW::GetUnicodeStorage() const noexcept
|
|
{
|
|
return _pParent->GetUnicodeStorage();
|
|
}
|
|
|
|
// Routine Description:
|
|
// - writes cell data to the row
|
|
// Arguments:
|
|
// - it - custom console iterator to use for seeking input data. bool() false when it becomes invalid while seeking.
|
|
// - index - column in row to start writing at
|
|
// - wrap - change the wrap flag if we hit the end of the row while writing and there's still more data in the iterator.
|
|
// - limitRight - right inclusive column ID for the last write in this row. (optional, will just write to the end of row if nullopt)
|
|
// Return Value:
|
|
// - iterator to first cell that was not written to this row.
|
|
OutputCellIterator ROW::WriteCells(OutputCellIterator it, const size_t index, const std::optional<bool> wrap, std::optional<size_t> limitRight)
|
|
{
|
|
THROW_HR_IF(E_INVALIDARG, index >= _charRow.size());
|
|
THROW_HR_IF(E_INVALIDARG, limitRight.value_or(0) >= _charRow.size());
|
|
|
|
// If we're given a right-side column limit, use it. Otherwise, the write limit is the final column index available in the char row.
|
|
const auto finalColumnInRow = limitRight.value_or(_charRow.size() - 1);
|
|
|
|
auto currentColor = it->TextAttr();
|
|
uint16_t colorUses = 0;
|
|
uint16_t colorStarts = gsl::narrow_cast<uint16_t>(index);
|
|
uint16_t currentIndex = colorStarts;
|
|
|
|
while (it && currentIndex <= finalColumnInRow)
|
|
{
|
|
// Fill the color if the behavior isn't set to keeping the current color.
|
|
if (it->TextAttrBehavior() != TextAttributeBehavior::Current)
|
|
{
|
|
// If the color of this cell is the same as the run we're currently on,
|
|
// just increment the counter.
|
|
if (currentColor == it->TextAttr())
|
|
{
|
|
++colorUses;
|
|
}
|
|
else
|
|
{
|
|
// Otherwise, commit this color into the run and save off the new one.
|
|
// Now commit the new color runs into the attr row.
|
|
_attrRow.Replace(colorStarts, currentIndex, currentColor);
|
|
currentColor = it->TextAttr();
|
|
colorUses = 1;
|
|
colorStarts = currentIndex;
|
|
}
|
|
}
|
|
|
|
// Fill the text if the behavior isn't set to saying there's only a color stored in this iterator.
|
|
if (it->TextAttrBehavior() != TextAttributeBehavior::StoredOnly)
|
|
{
|
|
const bool fillingLastColumn = currentIndex == finalColumnInRow;
|
|
|
|
// TODO: MSFT: 19452170 - We need to ensure when writing any trailing byte that the one to the left
|
|
// is a matching leading byte. Likewise, if we're writing a leading byte, we need to make sure we still have space in this loop
|
|
// for the trailing byte coming up before writing it.
|
|
|
|
// If we're trying to fill the first cell with a trailing byte, pad it out instead by clearing it.
|
|
// Don't increment iterator. We'll advance the index and try again with this value on the next round through the loop.
|
|
if (currentIndex == 0 && it->DbcsAttr().IsTrailing())
|
|
{
|
|
_charRow.ClearCell(currentIndex);
|
|
}
|
|
// If we're trying to fill the last cell with a leading byte, pad it out instead by clearing it.
|
|
// Don't increment iterator. We'll exit because we couldn't write a lead at the end of a line.
|
|
else if (fillingLastColumn && it->DbcsAttr().IsLeading())
|
|
{
|
|
_charRow.ClearCell(currentIndex);
|
|
SetDoubleBytePadded(true);
|
|
}
|
|
// Otherwise, copy the data given and increment the iterator.
|
|
else
|
|
{
|
|
_charRow.DbcsAttrAt(currentIndex) = it->DbcsAttr();
|
|
_charRow.GlyphAt(currentIndex) = it->Chars();
|
|
++it;
|
|
}
|
|
|
|
// If we're asked to (un)set the wrap status and we just filled the last column with some text...
|
|
// NOTE:
|
|
// - wrap = std::nullopt --> don't change the wrap value
|
|
// - wrap = true --> we're filling cells as a steam, consider this a wrap
|
|
// - wrap = false --> we're filling cells as a block, unwrap
|
|
if (wrap.has_value() && fillingLastColumn)
|
|
{
|
|
// set wrap status on the row to parameter's value.
|
|
SetWrapForced(*wrap);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
++it;
|
|
}
|
|
|
|
// Move to the next cell for the next time through the loop.
|
|
++currentIndex;
|
|
}
|
|
|
|
// Now commit the final color into the attr row
|
|
if (colorUses)
|
|
{
|
|
_attrRow.Replace(colorStarts, currentIndex, currentColor);
|
|
}
|
|
|
|
return it;
|
|
}
|
|
|
|
size_t ROW::MeasureRightIsh() const
|
|
{
|
|
// const_reverse_iterator it = _data.crbegin();
|
|
// while (it != _data.crend() && it->IsSpace())
|
|
// {
|
|
// ++it;
|
|
// }
|
|
// return _data.crend() - it;
|
|
auto charIter = _charRow.crbegin();
|
|
auto attrIter = _attrRow.crbegin();
|
|
const TextAttribute defaultColor{};
|
|
while (charIter != _charRow.crend() &&
|
|
attrIter != _attrRow.crend() &&
|
|
// (!charIter->IsSpace() || *attrIter != defaultColor))
|
|
// (!charIter->IsSpace() || !attrIter->HasIdenticalVisualRepresentationForBlankSpace(defaultColor)))
|
|
(charIter->IsSpace() && attrIter->HasIdenticalVisualRepresentationForBlankSpace(defaultColor)))
|
|
{
|
|
++charIter;
|
|
++attrIter;
|
|
}
|
|
// return charIter - _charRow.cbegin() - 1;
|
|
return _charRow.crend() - charIter;
|
|
}
|