Break a huge amount of the console

Writing CHAR_INFO doesn't work
RowImage is half-baked
Row.hpp contains eight reimplementations of the same slgotihm
move shit around
RowImage.split is fucking madness
look for TODO(DH) everywhere

however, termbench performance doubles for non-colored text.
This commit is contained in:
Dustin Howett 2021-07-30 12:34:39 -05:00
parent d2f0f50651
commit 5bdb5e6caa
20 changed files with 422 additions and 459 deletions

View file

@ -31,6 +31,8 @@ public:
using const_iterator = rle_vector::const_iterator;
ATTR_ROW(uint16_t width, TextAttribute attr);
ATTR_ROW(rle_vector&& v) :
_data(std::move(v)) {}
~ATTR_ROW() = default;
@ -57,11 +59,11 @@ public:
friend bool operator==(const ATTR_ROW& a, const ATTR_ROW& b) noexcept;
friend class ROW;
rle_vector _data;
private:
void Reset(const TextAttribute attr);
rle_vector _data;
#ifdef UNIT_TESTING
friend class CommonState;
#endif

View file

@ -42,19 +42,6 @@ gsl::span<OutputCell> OutputCellRect::GetRow(const size_t row)
return gsl::span<OutputCell>(_FindRowOffset(row), _cols);
}
// Routine Description:
// - Gets a read-only iterator view over a single row of the rectangle.
// Arguments:
// - row - The Y position or row index in the buffer.
// Return Value:
// - Read-only iterator of OutputCells
OutputCellIterator OutputCellRect::GetRowIter(const size_t row) const
{
const gsl::span<const OutputCell> view(_FindRowOffset(row), _cols);
return OutputCellIterator(view);
}
// Routine Description:
// - Internal helper to find the pointer to the specific row offset in the giant
// contiguous block of memory allocated for this rectangle.

View file

@ -21,10 +21,8 @@ Revision History:
#pragma once
#include "DbcsAttribute.hpp"
#include "TextAttribute.hpp"
#include "OutputCell.hpp"
#include "OutputCellIterator.hpp"
class OutputCellRect final
{
@ -33,7 +31,6 @@ public:
OutputCellRect(const size_t rows, const size_t cols);
gsl::span<OutputCell> GetRow(const size_t row);
OutputCellIterator GetRowIter(const size_t row) const;
size_t Height() const noexcept;
size_t Width() const noexcept;

View file

@ -91,6 +91,8 @@ void ROW::ClearColumn(const size_t column)
WriteGlyphAtMeasured(column, 1, L" ");
}
#if 0
TODO (DH)
// Routine Description:
// - writes cell data to the row
// Arguments:
@ -203,3 +205,4 @@ OutputCellIterator ROW::WriteCells(OutputCellIterator it, const size_t index, co
return it;
}
#endif

View file

@ -23,13 +23,144 @@ Revision History:
#include "AttrRow.hpp"
#include "LineRendition.hpp"
#include "OutputCell.hpp"
#include "OutputCellIterator.hpp"
#include "unicode.hpp"
#pragma warning(push)
#pragma warning(disable : 4267)
class TextBuffer;
using RowMeasurementBuffer = til::small_rle<uint8_t, uint16_t, 3>;
struct DamageRanges
{
size_t dataOffset;
size_t dataLength;
uint16_t firstColumn;
uint16_t lastColumnExclusive;
};
template<typename TRuns>
static DamageRanges DamageRangesForColumnInMeasurementBuffer(const TRuns& cwid, size_t col)
{
size_t currentCol{ 0 };
size_t currentWchar{ 0 };
auto it{ cwid.runs().cbegin() };
while (it != cwid.runs().cend())
{
// Each compressed pair tells us how many columns x N wchar_t
const auto colsCoveredByRun{ it->value * it->length };
if (currentCol + colsCoveredByRun > col)
{
// We want to break out of the loop to manually handle this run, because
// we've determined that it is the run that covers the column of interest.
break;
}
currentCol += colsCoveredByRun;
currentWchar += it->length;
it++;
}
if (it == cwid.runs().cend())
{
// this is an interesting case- somebody requested a column we cannot answer for.
// The string might actually have data, and the caller might be interested in where that data is.
// Ideally, we would return the index of the first char out-of-bounds, and the length of the remaining data as a single unit.
// We can't answer for how much space it takes up, though.
__debugbreak();
return { 0, 0, 0u, 0u };
//return { currentWchar, _data.size() - currentWchar, 0u, 0u };
}
// currentWchar is how many wchar_t we are into the string before processing this run
// currentCol is how many columns we've covered before processing this run
// We are *guaranteed* that the hit is in this run -- no need to check it->length
// col-currentCol is how many columns are left unaccounted for (how far into this run we need to go)
const auto colsLeftToCountInCurrentRun{ col - currentCol };
currentWchar += colsLeftToCountInCurrentRun / it->value; // one wch per column unit -- rounds down (correct behavior)
size_t lenInWchars{ 1 }; // the first hit takes up one wchar
// We use this to determine if we have exhausted every column this run can cough up.
// colsLeftToCountInCurrentRun is 0-indexed, but colsConsumedByRun is 1-indexed (index 0 consumes N columns, etc.)
// therefore, we reindex colsLeftToCountInCurrentRun and compare it to colsConsumedByRun
const auto colsConsumedFromRun{ colsLeftToCountInCurrentRun + it->value };
const auto colsCoveredByRun{ it->value * it->length };
// If we *have* consumed every column this run can provide, we must check the run after it:
// if it contributes "0" columns, it is actually a set of trailing code units.
if (colsConsumedFromRun >= colsCoveredByRun && it != cwid.runs().cend())
{
const auto nextRunIt{ it + 1 };
if (nextRunIt != cwid.runs().cend() && nextRunIt->value == 0)
{
// we were at the boundary of a column run, so if the next one is 0 it tells us that each
// wchar after it is a trailer
lenInWchars += nextRunIt->length;
}
}
return {
currentWchar, // wchar start
lenInWchars, // wchar size
gsl::narrow_cast<uint16_t>(col - (colsLeftToCountInCurrentRun % it->value)), // Column damage to the left (where we overlapped the right of a wide glyph)
gsl::narrow_cast<uint16_t>(col - (colsLeftToCountInCurrentRun % it->value) + it->value), // Column damage to the right (where we overlapped the left of a wide glyph)
};
}
class ROW;
struct RowImage
{
std::wstring _data;
RowMeasurementBuffer _cwid;
ATTR_ROW _attrRow;
uint16_t _width;
friend class ROW;
friend class TextBuffer;
RowImage() :
_data{}, _cwid{}, _attrRow{ {} }, _width{ 0 } {}
RowImage(const std::wstring& data, const RowMeasurementBuffer& cwid, ATTR_ROW attrRow, uint16_t width):
_data{data}, _cwid{cwid}, _attrRow{std::move(attrRow)}, _width{width} {}
// exclusive
std::tuple<RowImage, RowImage> split(uint16_t col) const
{
if (col >= _width)
{
return { *this, RowImage{} };
}
else if (col == 0)
{
return { RowImage{}, *this };
}
auto yes_more_fucking_damage_ranges = DamageRangesForColumnInMeasurementBuffer(_cwid, col);
// here's a dumb decision: when you split along a wide char, you get spaces over its damage on the left side.
// X X XX X X X
// ^ split
// X X S <- left
// XX X X X <- right
auto chopped_off = col == yes_more_fucking_damage_ranges.lastColumnExclusive ? 0 : /*didn't get whole thing*/ yes_more_fucking_damage_ranges.lastColumnExclusive - col;
auto chopped_on = col == yes_more_fucking_damage_ranges.lastColumnExclusive ? yes_more_fucking_damage_ranges.dataLength : 0;
auto lwid = _cwid.slice(0, yes_more_fucking_damage_ranges.dataOffset + chopped_on);
for (auto z{ chopped_off }; z > 0; --z)
{
lwid.append(1);
}
RowImage left{
_data.substr(0, yes_more_fucking_damage_ranges.dataOffset + chopped_on) + std::wstring(chopped_off, UNICODE_SPACE),
lwid,
_attrRow._data.slice(0, yes_more_fucking_damage_ranges.lastColumnExclusive),
col
};
RowImage right{
_data.substr(yes_more_fucking_damage_ranges.dataOffset + chopped_on),
_cwid.slice(yes_more_fucking_damage_ranges.dataOffset + chopped_on, _data.length()),
// we use first col here to catch the overlap
_attrRow._data.slice(yes_more_fucking_damage_ranges.firstColumn, _width),
::base::ClampSub(_width, col),
};
return { left, right };
}
};
enum class DelimiterClass
{
ControlChar,
@ -62,92 +193,15 @@ public:
void ClearColumn(const size_t column);
std::wstring GetText() const { return _data._data; }
OutputCellIterator WriteCells(OutputCellIterator it, const size_t index, const std::optional<bool> wrap = std::nullopt, std::optional<size_t> limitRight = std::nullopt);
#ifdef UNIT_TESTING
friend constexpr bool operator==(const ROW& a, const ROW& b) noexcept;
friend class RowTests;
#endif
struct DamageRanges
{
size_t dataOffset;
size_t dataLength;
uint16_t firstColumn;
uint16_t lastColumnExclusive;
};
template<typename TRuns>
static DamageRanges DamageRangesForColumnInMeasurementBuffer(const TRuns& cwid, size_t col)
{
size_t currentCol{ 0 };
size_t currentWchar{ 0 };
auto it{ cwid.runs().cbegin() };
while (it != cwid.runs().cend())
{
// Each compressed pair tells us how many columns x N wchar_t
const auto colsCoveredByRun{ it->value * it->length };
if (currentCol + colsCoveredByRun > col)
{
// We want to break out of the loop to manually handle this run, because
// we've determined that it is the run that covers the column of interest.
break;
}
currentCol += colsCoveredByRun;
currentWchar += it->length;
it++;
}
if (it == cwid.runs().cend())
{
// this is an interesting case- somebody requested a column we cannot answer for.
// The string might actually have data, and the caller might be interested in where that data is.
// Ideally, we would return the index of the first char out-of-bounds, and the length of the remaining data as a single unit.
// We can't answer for how much space it takes up, though.
__debugbreak();
return { 0, 0, 0u, 0u };
//return { currentWchar, _data.size() - currentWchar, 0u, 0u };
}
// currentWchar is how many wchar_t we are into the string before processing this run
// currentCol is how many columns we've covered before processing this run
// We are *guaranteed* that the hit is in this run -- no need to check it->length
// col-currentCol is how many columns are left unaccounted for (how far into this run we need to go)
const auto colsLeftToCountInCurrentRun{ col - currentCol };
currentWchar += colsLeftToCountInCurrentRun / it->value; // one wch per column unit -- rounds down (correct behavior)
size_t lenInWchars{ 1 }; // the first hit takes up one wchar
// We use this to determine if we have exhausted every column this run can cough up.
// colsLeftToCountInCurrentRun is 0-indexed, but colsConsumedByRun is 1-indexed (index 0 consumes N columns, etc.)
// therefore, we reindex colsLeftToCountInCurrentRun and compare it to colsConsumedByRun
const auto colsConsumedFromRun{ colsLeftToCountInCurrentRun + it->value };
const auto colsCoveredByRun{ it->value * it->length };
// If we *have* consumed every column this run can provide, we must check the run after it:
// if it contributes "0" columns, it is actually a set of trailing code units.
if (colsConsumedFromRun >= colsCoveredByRun && it != cwid.runs().cend())
{
const auto nextRunIt{ it + 1 };
if (nextRunIt != cwid.runs().cend() && nextRunIt->value == 0)
{
// we were at the boundary of a column run, so if the next one is 0 it tells us that each
// wchar after it is a trailer
lenInWchars += nextRunIt->length;
}
}
return {
currentWchar, // wchar start
lenInWchars, // wchar size
gsl::narrow_cast<uint16_t>(col - (colsLeftToCountInCurrentRun % it->value)), // Column damage to the left (where we overlapped the right of a wide glyph)
gsl::narrow_cast<uint16_t>(col - (colsLeftToCountInCurrentRun % it->value) + it->value), // Column damage to the right (where we overlapped the left of a wide glyph)
};
}
struct RowData
{
std::wstring _data;
til::small_rle<uint8_t, uint16_t, 3> _cwid;
RowMeasurementBuffer _cwid;
ATTR_ROW _attrRow;
unsigned short _width;
@ -184,8 +238,9 @@ public:
return damage;
}
void _strikeDamageAndAdjust(size_t col, size_t ncols, DamageRanges& range)
void _strikeDamageAndAdjust(size_t col, size_t ncols, size_t incomingCodeUnitCount, DamageRanges& range)
{
(void)incomingCodeUnitCount;
const bool damaged{ range.firstColumn < col || col + ncols < range.lastColumnExclusive };
if (damaged)
{
@ -295,7 +350,7 @@ public:
}
}
std::tuple<size_t, uint16_t, uint16_t> WriteStringAtMeasured(uint16_t col, uint16_t colCount, const std::wstring_view& string, const til::small_rle<uint8_t, uint16_t, 3>& measurements)
std::tuple<size_t, uint16_t, uint16_t> WriteStringAtMeasured(uint16_t col, uint16_t colCount, const std::wstring_view& string, const RowMeasurementBuffer& measurements)
{
size_t incomingLastColumn{ std::min<size_t>(_data._width - col, colCount) };
auto incomingLastColumnOffsets{ DamageRangesForColumnInMeasurementBuffer(measurements, incomingLastColumn - 1 /*inclusive*/) };
@ -386,7 +441,7 @@ public:
// If we are filling over the left/right halves of a character
// This is a bit wasteful since it can grow/shrink the buffers and we're about
// to do it again, but I was trying to be expedient.
_data._strikeDamageAndAdjust(col, columnsRequired, damage);
_data._strikeDamageAndAdjust(col, columnsRequired, charsFitOrRemain, damage);
const auto [begin, len, min, max] = damage;
_data._data.replace(begin, len, charsFitOrRemain, ch);
@ -440,6 +495,26 @@ public:
}
}
RowImage Dump(uint16_t left, uint16_t size)
{
auto [begin, len, min, max] = _data._damageForColumns(left, size);
return RowImage{
_data._data.substr(begin, len),
_data._cwid.slice(begin, begin + len),
ATTR_ROW{ _data._attrRow._data.slice(min, max) },
::base::MakeClampedNum(max) - min
};
}
void Reinsert(uint16_t left, const RowImage& ri)
{
auto damage{ _data._damageForColumns(left, ri._width) };
_data._strikeDamageAndAdjust(left, ri._width, ri._data.size(), damage);
_data._data.replace(damage.dataOffset, damage.dataLength, ri._data);
_data._cwid.replace(damage.dataOffset, damage.dataOffset + damage.dataLength, ri._cwid.runs());
_data._attrRow._data.replace(damage.firstColumn, damage.lastColumnExclusive, ri._attrRow._data.runs());
}
uint16_t _maxc{};
size_t MeasureRight() const
{

View file

@ -13,7 +13,6 @@
<ClCompile Include="..\AttrRow.cpp" />
<ClCompile Include="..\cursor.cpp" />
<ClCompile Include="..\OutputCell.cpp" />
<ClCompile Include="..\OutputCellIterator.cpp" />
<ClCompile Include="..\OutputCellRect.cpp" />
<ClCompile Include="..\OutputCellView.cpp" />
<ClCompile Include="..\Row.cpp" />
@ -33,7 +32,6 @@
<ClInclude Include="..\DbcsAttribute.hpp" />
<ClInclude Include="..\LineRendition.hpp" />
<ClInclude Include="..\OutputCell.hpp" />
<ClInclude Include="..\OutputCellIterator.hpp" />
<ClInclude Include="..\OutputCellRect.hpp" />
<ClInclude Include="..\OutputCellView.hpp" />
<ClInclude Include="..\Row.hpp" />

View file

@ -183,90 +183,6 @@ TextBufferCellIterator TextBuffer::GetCellDataAt(const COORD at, const Viewport
return TextBufferCellIterator(*this, at, limit);
}
// Routine Description:
// - Writes cells to the output buffer. Writes at the cursor.
// Arguments:
// - givenIt - Iterator representing output cell data to write
// Return Value:
// - The final position of the iterator
OutputCellIterator TextBuffer::Write(const OutputCellIterator givenIt)
{
const auto& cursor = GetCursor();
const auto target = cursor.GetPosition();
const auto finalIt = Write(givenIt, target);
return finalIt;
}
// Routine Description:
// - Writes cells to the output buffer.
// Arguments:
// - givenIt - Iterator representing output cell data to write
// - target - the row/column to start writing the text to
// - wrap - change the wrap flag if we hit the end of the row while writing and there's still more data
// Return Value:
// - The final position of the iterator
OutputCellIterator TextBuffer::Write(const OutputCellIterator givenIt,
const COORD target,
const std::optional<bool> wrap)
{
// Make mutable copy so we can walk.
auto it = givenIt;
// Make mutable target so we can walk down lines.
auto lineTarget = target;
// Get size of the text buffer so we can stay in bounds.
const auto size = GetSize();
// While there's still data in the iterator and we're still targeting in bounds...
while (it && size.IsInBounds(lineTarget))
{
// Attempt to write as much data as possible onto this line.
// NOTE: if wrap = true/false, we want to set the line's wrap to true/false (respectively) if we reach the end of the line
it = WriteLine(it, lineTarget, wrap);
// Move to the next line down.
lineTarget.X = 0;
++lineTarget.Y;
}
return it;
}
// Routine Description:
// - Writes one line of text to the output buffer.
// Arguments:
// - givenIt - The iterator that will dereference into cell data to insert
// - target - Coordinate targeted within output buffer
// - 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 - Optionally restrict the right boundary for writing (e.g. stop writing earlier than the end of line)
// Return Value:
// - The iterator, but advanced to where we stopped writing. Use to find input consumed length or cells written length.
OutputCellIterator TextBuffer::WriteLine(const OutputCellIterator givenIt,
const COORD target,
const std::optional<bool> wrap,
std::optional<size_t> limitRight)
{
// If we're not in bounds, exit early.
if (!GetSize().IsInBounds(target))
{
return givenIt;
}
// Get the row and write the cells
ROW& row = GetRowByOffset(target.Y);
const auto newIt = row.WriteCells(givenIt, target.X, wrap, limitRight);
// Take the cell distance written and notify that it needs to be repainted.
const auto written = newIt.GetCellDistance(givenIt);
const Viewport paint = Viewport::FromDimensions(target, { gsl::narrow<SHORT>(written), 1 });
_NotifyPaint(paint);
return newIt;
}
//Routine Description:
// - Finds the current row in the buffer (as indicated by the cursor position)
// and specifies that we have forced a line wrap on that row
@ -629,7 +545,7 @@ void TextBuffer::SetCurrentLineRendition(const LineRendition lineRendition)
fillAttrs.SetStandardErase();
const size_t fillOffset = GetLineWidth(rowIndex);
const size_t fillLength = GetSize().Width() - fillOffset;
FillWithCharacterAndAttributeLinear(til::point{fillOffset, ::base::saturated_cast<size_t>(cursorPosition.Y)}, fillLength, UNICODE_SPACE, fillAttrs);
FillWithCharacterAndAttributeLinear(til::point{ fillOffset, ::base::saturated_cast<size_t>(cursorPosition.Y) }, fillLength, UNICODE_SPACE, fillAttrs);
// We also need to make sure the cursor is clamped within the new width.
GetCursor().SetPosition(ClampPositionWithinLine(cursorPosition));
}
@ -1939,10 +1855,14 @@ HRESULT TextBuffer::Reflow(TextBuffer& oldBuffer,
}
}
#if 0
OutputCellIterator it{ oldBuffer.GetCellDataAt({ 0, iOldRow }, Viewport::FromDimensions({ 0, iOldRow }, iRight, 1)) };
auto preCurPos{ newBuffer.GetCursor().GetPosition() };
const auto last = newBuffer.Write(it, preCurPos, true); // true - wrap if we don't fit!
const auto distance = last.GetCellDistance(it);
#endif
auto preCurPos{ newBuffer.GetCursor().GetPosition() };
const auto distance = 5;
if (iOldRow == cOldCursorPos.Y)
{
@ -2318,17 +2238,18 @@ PointTree TextBuffer::GetPatterns(const size_t firstRow, const size_t lastRow) c
return result;
}
void TextBuffer::FillWithAttributeRectangular(const til::rectangle& region, const TextAttribute& attribute)
TextBuffer::WriteResult TextBuffer::FillWithAttributeRectangular(const til::rectangle& region, const TextAttribute& attribute)
{
_WriteRectangular(
return _WriteRectangular(
region,
[&](WriteResult&, auto&& at, auto&& size) {
[&](WriteResult& result, auto&& at, auto&& size) {
auto& row = GetRowByOffset(at.y());
row.GetAttrRow().Replace(::base::saturated_cast<uint16_t>(at.x()), ::base::ClampAdd<uint16_t>(::base::saturated_cast<uint16_t>(at.x()), size), attribute);
result.columnsWritten += size;
});
}
size_t TextBuffer::FillWithAttributeLinear(til::point at, size_t count, const TextAttribute& attribute)
TextBuffer::WriteResult TextBuffer::FillWithAttributeLinear(til::point at, size_t count, const TextAttribute& attribute)
{
auto out = _WriteLinear(
at,
@ -2338,20 +2259,20 @@ size_t TextBuffer::FillWithAttributeLinear(til::point at, size_t count, const Te
auto& row = GetRowByOffset(at.y());
row.GetAttrRow().Replace(::base::saturated_cast<uint16_t>(at.x()), ::base::ClampAdd<uint16_t>(::base::saturated_cast<uint16_t>(at.x()), size), attribute);
result.colsConsumed += w.RawValue();
result.columnsWritten += w.RawValue();
const Viewport paint = Viewport::FromDimensions(at, { w.Cast<SHORT>(), 1 });
_NotifyPaint(paint);
return (count -= w.RawValue()) > 0; // keep going if data remains
});
return out.colsConsumed;
return out;
}
// TODO(DH): make this a member so that IsGlyphFullWidth can be a member ref
static std::tuple<til::small_rle<uint8_t, uint16_t, 3>, uint16_t> _MeasureStringAndCountColumns(std::wstring_view string) //const noexcept
static std::tuple<RowMeasurementBuffer, uint16_t> _MeasureStringAndCountColumns(std::wstring_view string) //const noexcept
{
til::small_rle<uint8_t, uint16_t, 3> measurements;
RowMeasurementBuffer measurements;
uint16_t colCount{};
while (!string.empty())
{
@ -2374,7 +2295,7 @@ struct no_attributes
};
template<typename T>
size_t TextBuffer::_WriteMeasuredStringLinearWithAttributes(const til::point& at, std::wstring_view string, til::small_rle<uint8_t, uint16_t, 3> measurements, [[maybe_unused]] const T& attributes)
TextBuffer::WriteResult TextBuffer::_WriteMeasuredStringLinearWithAttributes(const til::point& at, std::wstring_view string, RowMeasurementBuffer measurements, [[maybe_unused]] const T& attributes)
{
uint16_t colCount = std::accumulate(
measurements.runs().cbegin(),
@ -2391,34 +2312,52 @@ size_t TextBuffer::_WriteMeasuredStringLinearWithAttributes(const til::point& at
{
row.GetAttrRow().Replace(::base::saturated_cast<uint16_t>(at.x()), ::base::ClampAdd<uint16_t>(::base::saturated_cast<uint16_t>(at.x()), consumedDestCols), attributes);
}
#if 0
else if constexpr (std::is_same<T, ATTR_ROW::rle_vector>::value)
{
// user has passed us a packed representation of attributes
// It is a programming error to pass one whose total length does
// not match the measurement buffer
const auto attrRunSlice{ attributes.slice(0, consumedSourceCols) };
attrRunSlice.resize_trailing_extent(consumedDestCols); // this may be very wrong. TODO(DH)
// intent: I believe that this will take the attribute from the cell before the one we had to cut off
// and then extend it to cover our empty column?
row.GetAttrRow()._data.replace(::base::saturated_cast<uint16_t>(at.x()), ::base::ClampAdd<uint16_t>(::base::saturated_cast<uint16_t>(at.x()), consumedSourceCols), attrRunSlice.runs());
}
#endif
const Viewport paint = Viewport::FromDimensions(at, { ::base::saturated_cast<SHORT>(consumedDestCols), 1 });
_NotifyPaint(paint);
colCount -= consumedSourceCols;
result.charsConsumed += consumedWchar;
result.colsConsumed += consumedDestCols;
result.charactersRead += consumedWchar;
result.charactersWritten += consumedWchar;
result.columnsWritten += consumedDestCols;
result.columnsRead += consumedSourceCols;
// unchecked substr
//string = std::wstring_view{ &*string.begin() + consumedWchar, string.size() - consumedWchar };
//string = til::unchecked_substr(string, consumedWchar);
string = string.substr(consumedWchar);
measurements = measurements.slice(gsl::narrow_cast<uint16_t>(consumedWchar), measurements.size());
return !string.empty();
});
return out.colsConsumed;
return out;
}
size_t TextBuffer::WriteMeasuredStringLinear(const til::point& at, const std::wstring_view& string, til::small_rle<uint8_t, uint16_t, 3> measurements)
TextBuffer::WriteResult TextBuffer::WriteMeasuredStringLinear(const til::point& at, const std::wstring_view& string, RowMeasurementBuffer measurements)
{
return _WriteMeasuredStringLinearWithAttributes(at, string, std::move(measurements), _currentAttributes);
}
size_t TextBuffer::WriteStringLinearWithAttributes(const til::point &at, const std::wstring_view& string, const TextAttribute& attr)
TextBuffer::WriteResult TextBuffer::WriteStringLinearWithAttributes(const til::point& at, const std::wstring_view& string, const TextAttribute& attr)
{
auto [measurements, colCount] = _MeasureStringAndCountColumns(string);
return _WriteMeasuredStringLinearWithAttributes(at, string, std::move(measurements), attr);
}
size_t TextBuffer::WriteStringLinearKeepAttributes(const til::point &at, const std::wstring_view& string)
TextBuffer::WriteResult TextBuffer::WriteStringLinearKeepAttributes(const til::point& at, const std::wstring_view& string)
{
auto [measurements, colCount] = _MeasureStringAndCountColumns(string);
return _WriteMeasuredStringLinearWithAttributes(at, string, std::move(measurements), no_attributes{});
@ -2443,7 +2382,7 @@ TextBuffer::WriteResult TextBuffer::_FillWithCharacterAndAttributeRectangular(co
}
template<typename T>
TextBuffer::WriteResult TextBuffer::_FillWithCharacterAndAttributeLinear(const til::point &at, size_t count, const wchar_t character, [[maybe_unused]] const T& attr)
TextBuffer::WriteResult TextBuffer::_FillWithCharacterAndAttributeLinear(const til::point& at, size_t count, const wchar_t character, [[maybe_unused]] const T& attr)
{
const uint8_t width{ IsGlyphFullWidth(character) ? uint8_t{ 2u } : uint8_t{ 1u } };
return _WriteLinear(
@ -2459,8 +2398,8 @@ TextBuffer::WriteResult TextBuffer::_FillWithCharacterAndAttributeLinear(const t
row.GetAttrRow().Replace(::base::saturated_cast<uint16_t>(at.x()), ::base::ClampAdd<uint16_t>(::base::saturated_cast<uint16_t>(at.x()), w), attr);
}
result.charsConsumed += consumedWchars;
result.colsConsumed += w.RawValue();
result.charactersWritten += consumedWchars;
result.columnsWritten += w.RawValue();
const Viewport paint = Viewport::FromDimensions(at, { w.Cast<SHORT>(), 1 });
_NotifyPaint(paint);
@ -2469,26 +2408,31 @@ TextBuffer::WriteResult TextBuffer::_FillWithCharacterAndAttributeLinear(const t
});
}
size_t TextBuffer::FillWithCharacterRectangular(const til::rectangle& region, const wchar_t character)
TextBuffer::WriteResult TextBuffer::FillWithCharacterRectangular(const til::rectangle& region, const wchar_t character)
{
return _FillWithCharacterAndAttributeRectangular(region, character, no_attributes{}).colsConsumed;
return _FillWithCharacterAndAttributeRectangular(region, character, no_attributes{});
}
void TextBuffer::FillWithCharacterAndAttributeRectangular(const til::rectangle& region, const wchar_t character, const TextAttribute& attr)
TextBuffer::WriteResult TextBuffer::FillWithCharacterAndAttributeRectangular(const til::rectangle& region, const wchar_t character, const TextAttribute& attr)
{
_FillWithCharacterAndAttributeRectangular(region, character, attr);
return _FillWithCharacterAndAttributeRectangular(region, character, attr);
}
size_t TextBuffer::FillWithCharacterLinear(const til::point& at, size_t count, const wchar_t character)
TextBuffer::WriteResult TextBuffer::FillWithCharacterLinear(const til::point& at, size_t count, const wchar_t character)
{
const auto out = _FillWithCharacterAndAttributeLinear(at, count, character, no_attributes{});
return out.charsConsumed;
return _FillWithCharacterAndAttributeLinear(at, count, character, no_attributes{});
}
size_t TextBuffer::FillWithCharacterAndAttributeLinear(const til::point &at, size_t count, const wchar_t character, const TextAttribute& attr)
TextBuffer::WriteResult TextBuffer::FillWithCharacterAndAttributeLinear(const til::point& at, size_t count, const wchar_t character, const TextAttribute& attr)
{
const auto out = _FillWithCharacterAndAttributeLinear(at, count, character, attr);
return out.charsConsumed;
return _FillWithCharacterAndAttributeLinear(at, count, character, attr);
}
TextBuffer::WriteResult TextBuffer::WriteRowImage(const til::point at, const RowImage& ri)
{
auto& row{ GetRowByOffset(at.y()) };
row.Reinsert(at.x<uint16_t>(), ri);
return {};
}
template<typename TLambda>

View file

@ -85,17 +85,6 @@ public:
TextBufferTextIterator GetTextDataAt(const COORD at, const Microsoft::Console::Types::Viewport limit) const;
// Text insertion functions
OutputCellIterator Write(const OutputCellIterator givenIt);
OutputCellIterator Write(const OutputCellIterator givenIt,
const COORD target,
const std::optional<bool> wrap = true);
OutputCellIterator WriteLine(const OutputCellIterator givenIt,
const COORD target,
const std::optional<bool> setWrap = std::nullopt,
const std::optional<size_t> limitRight = std::nullopt);
bool IncrementCursor();
bool NewlineCursor();
@ -196,23 +185,27 @@ public:
struct WriteResult
{
size_t charsConsumed;
size_t colsConsumed;
size_t charactersWritten;
size_t charactersRead;
size_t columnsWritten;
size_t columnsRead;
};
size_t WriteMeasuredStringLinear(const til::point& at, const std::wstring_view& string, til::small_rle<uint8_t, uint16_t, 3> measurements);
WriteResult WriteMeasuredStringLinear(const til::point& at, const std::wstring_view& string, RowMeasurementBuffer measurements);
size_t WriteStringLinearWithAttributes(const til::point& at, const std::wstring_view& string, const TextAttribute& attr);
size_t WriteStringLinearKeepAttributes(const til::point& at, const std::wstring_view& string);
WriteResult WriteStringLinearWithAttributes(const til::point& at, const std::wstring_view& string, const TextAttribute& attr);
WriteResult WriteStringLinearKeepAttributes(const til::point& at, const std::wstring_view& string);
size_t FillWithCharacterRectangular(const til::rectangle& region, const wchar_t character);
size_t FillWithCharacterLinear(const til::point& at, size_t count, const wchar_t character);
WriteResult FillWithCharacterRectangular(const til::rectangle& region, const wchar_t character);
WriteResult FillWithCharacterLinear(const til::point& at, size_t count, const wchar_t character);
void FillWithCharacterAndAttributeRectangular(const til::rectangle& region, const wchar_t character, const TextAttribute& attr);
size_t FillWithCharacterAndAttributeLinear(const til::point& at, size_t count, const wchar_t character, const TextAttribute& attr);
WriteResult FillWithCharacterAndAttributeRectangular(const til::rectangle& region, const wchar_t character, const TextAttribute& attr);
WriteResult FillWithCharacterAndAttributeLinear(const til::point& at, size_t count, const wchar_t character, const TextAttribute& attr);
void FillWithAttributeRectangular(const til::rectangle& region, const TextAttribute& attribute);
size_t FillWithAttributeLinear(const til::point at, size_t count, const TextAttribute& attribute);
WriteResult FillWithAttributeRectangular(const til::rectangle& region, const TextAttribute& attribute);
WriteResult FillWithAttributeLinear(const til::point at, size_t count, const TextAttribute& attribute);
WriteResult WriteRowImage(const til::point at, const RowImage& ri);
private:
void _UpdateSize();
@ -266,7 +259,7 @@ private:
WriteResult _FillWithCharacterAndAttributeLinear(const til::point& at, size_t count, const wchar_t character, const T& attr);
template<typename T>
size_t _WriteMeasuredStringLinearWithAttributes(const til::point& at, std::wstring_view string, til::small_rle<uint8_t, uint16_t, 3> measurements, const T& attributes);
WriteResult _WriteMeasuredStringLinearWithAttributes(const til::point& at, std::wstring_view string, RowMeasurementBuffer measurements, const T& attributes);
std::unordered_map<size_t, std::wstring> _idsAndPatterns;
size_t _currentPatternId;

View file

@ -429,7 +429,7 @@ void CommandListPopup::_drawList()
TextAttribute inverted = _attributes;
inverted.Invert();
_screenInfo.GetTextBuffer().FillWithAttributeRectangular(til::rectangle{WriteCoord, til::size{ Width(), 1 }}, inverted);
_screenInfo.GetTextBuffer().FillWithAttributeRectangular(til::rectangle{ WriteCoord, til::size{ Width(), 1 } }, inverted);
}
WriteCoord.Y += 1;
@ -526,11 +526,11 @@ void CommandListPopup::_updateHighlight(const SHORT OldCurrentCommand, const SHO
WriteCoord.X = _region.Left + 1i16;
WriteCoord.Y = _region.Top + 1i16 + OldCurrentCommand - TopIndex;
_screenInfo.GetTextBuffer().FillWithAttributeRectangular(til::rectangle{WriteCoord, til::size{ Width(), 1 }}, _attributes);
_screenInfo.GetTextBuffer().FillWithAttributeRectangular(til::rectangle{ WriteCoord, til::size{ Width(), 1 } }, _attributes);
// highlight new command
WriteCoord.Y = _region.Top + 1i16 + NewCurrentCommand - TopIndex;
TextAttribute inverted = _attributes;
inverted.Invert();
_screenInfo.GetTextBuffer().FillWithAttributeRectangular(til::rectangle{WriteCoord, til::size{ Width(), 1 }}, inverted);
_screenInfo.GetTextBuffer().FillWithAttributeRectangular(til::rectangle{ WriteCoord, til::size{ Width(), 1 } }, inverted);
}

View file

@ -89,10 +89,48 @@ void WriteToScreen(SCREEN_INFORMATION& screenInfo, const Viewport& region)
return E_INVALIDARG;
}
const OutputCellIterator it(attrs);
const auto done = screenInfo.Write(it, target);
//const OutputCellIterator it(attrs);
//const auto done = screenInfo.Write(it, target);
__debugbreak();
// TODO(DH) restore writing runs of attributes
////////////////
#if 0
static rle_container rle_encode(const basic_container_view& from)
{
if (from.empty())
{
return {};
}
used = done.GetCellDistance(it);
rle_container to;
value_type value = from.front();
size_type length = 0;
for (auto v : from)
{
if (v != value)
{
to.emplace_back(value, length);
value = v;
length = 0;
}
length++;
}
if (length)
{
to.emplace_back(value, length);
}
return to;
}
#endif
////////////////
used = 0;
//used = done.GetCellDistance(it);
return S_OK;
}
@ -131,7 +169,8 @@ void WriteToScreen(SCREEN_INFORMATION& screenInfo, const Viewport& region)
try
{
used = screenInfo.GetTextBuffer().WriteStringLinearKeepAttributes(target, chars);
const auto result = screenInfo.GetTextBuffer().WriteStringLinearKeepAttributes(target, chars);
used = result.charactersWritten;
}
CATCH_RETURN();
@ -214,7 +253,8 @@ void WriteToScreen(SCREEN_INFORMATION& screenInfo, const Viewport& region)
try
{
cellsModified = screenBuffer.GetTextBuffer().FillWithAttributeLinear(startingCoordinate, lengthToWrite, TextAttribute{ attribute });
const auto result = screenBuffer.GetTextBuffer().FillWithAttributeLinear(startingCoordinate, lengthToWrite, TextAttribute{ attribute });
cellsModified = result.columnsWritten;
if (screenBuffer.HasAccessibilityEventing())
{
@ -271,12 +311,11 @@ void WriteToScreen(SCREEN_INFORMATION& screenInfo, const Viewport& region)
HRESULT hr = S_OK;
try
{
const OutputCellIterator it(character, lengthToWrite);
// when writing to the buffer, specifically unset wrap if we get to the last column.
// a fill operation should UNSET wrap in that scenario. See GH #1126 for more details.
const auto done = screenInfo.Write(it, startingCoordinate, false);
cellsModified = done.GetInputDistance(it);
// TODO(DH) maintain above fix to clear wrap ^^^
const auto result = screenInfo.GetTextBuffer().FillWithCharacterLinear(startingCoordinate, lengthToWrite, character);
cellsModified = result.columnsWritten;
// Notify accessibility
if (screenInfo.HasAccessibilityEventing())

View file

@ -342,7 +342,7 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
SHORT XPosition;
std::wstring local;
local.reserve(LOCAL_BUFFER_SIZE);
til::small_rle<uint8_t, uint16_t, 3> measurements{ gsl::narrow_cast<uint16_t>(LOCAL_BUFFER_SIZE), 1u };
RowMeasurementBuffer measurements;
size_t TempNumSpaces = 0;
const bool fUnprocessed = WI_IsFlagClear(screenInfo.OutputMode, ENABLE_PROCESSED_OUTPUT);
const bool fWrapAtEOL = WI_IsFlagSet(screenInfo.OutputMode, ENABLE_WRAP_AT_EOL_OUTPUT);
@ -400,7 +400,6 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
// WCL-NOTE: is initialized from pwchRealUnicode.
const wchar_t RealUnicodeChar = *pwchRealUnicode;
const auto cbuf{ Utf16Parser::ParseNext(std::wstring_view{ pwchRealUnicode, (BufferSize / sizeof(WCHAR)) - static_cast<size_t>(pwchRealUnicode - base) }) };
uint16_t lo{ gsl::narrow_cast<uint16_t>(local.size()) };
if (IS_GLYPH_CHAR(RealUnicodeChar) || fUnprocessed)
{
// WCL-NOTE: This operates on a single code unit instead of a whole codepoint. It will mis-measure surrogate pairs.
@ -409,7 +408,7 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
if (XPosition < (coordScreenBufferSize.X - 1))
{
local.append(cbuf);
measurements.replace(lo, lo + 1, { uint8_t{ 2u }, 1u });
measurements.append(uint8_t{ 2u });
// cursor adjusted by 2 because the char is double width
XPosition += 2;
@ -424,14 +423,14 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
else
{
local.append(cbuf);
measurements.replace(lo, lo + 1, { uint8_t{ 1u }, 1u });
measurements.append(uint8_t{ 1u });
XPosition++;
i += cbuf.size();
pwchBuffer += cbuf.size();
}
if (cbuf.size() > 1)
for (auto z = cbuf.size(); z > 1; --z)
{
measurements.replace(lo + 1, gsl::narrow_cast<uint16_t>(lo + 1 + cbuf.size() - 1), typename decltype(measurements)::rle_type{ uint8_t{ 0u }, gsl::narrow_cast<uint16_t>(cbuf.size() - 1) });
measurements.append(uint8_t{ 0 });
}
}
else
@ -561,7 +560,7 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
//OutputCellIterator it(std::wstring_view{ local }, Attributes);
//const auto itEnd = screenInfo.Write(it);
measurements.resize_trailing_extent(gsl::narrow_cast<uint16_t>(local.size()));
auto vcolsTaken = screenInfo.WriteMeasuredStringLinear(local, measurements);
auto result = screenInfo.GetTextBuffer().WriteMeasuredStringLinear(CursorPosition, local, measurements);
local.clear();
measurements.resize_trailing_extent(0);
@ -573,7 +572,7 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
// The number of "spaces" or "cells" we have consumed needs to be reported and stored for later
// when/if we need to erase the command line.
TempNumSpaces += vcolsTaken; //itEnd.GetCellDistance(it);
TempNumSpaces += result.columnsWritten; //itEnd.GetCellDistance(it);
// WCL-NOTE: We are using the "estimated" X position delta instead of the actual delta from
// WCL-NOTE: the iterator. It is not clear why. If they differ, the cursor ends up in the
// WCL-NOTE: wrong place (typically inside another character).
@ -711,7 +710,7 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
{
try
{
screenInfo.Write(OutputCellIterator(UNICODE_SPACE, Attributes, 1), CursorPosition);
screenInfo.GetTextBuffer().FillWithCharacterAndAttributeLinear(CursorPosition, 1, UNICODE_SPACE, Attributes);
Status = STATUS_SUCCESS;
}
CATCH_LOG();
@ -729,7 +728,7 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
{
try
{
screenInfo.Write(OutputCellIterator(UNICODE_SPACE, Attributes, 1), CursorPosition);
screenInfo.GetTextBuffer().FillWithCharacterAndAttributeLinear(CursorPosition, 1, UNICODE_SPACE, Attributes);
Status = STATUS_SUCCESS;
}
CATCH_LOG();
@ -751,7 +750,7 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
{
try
{
screenInfo.Write(OutputCellIterator(UNICODE_SPACE, Attributes, 1), cursor.GetPosition());
screenInfo.GetTextBuffer().FillWithCharacterAndAttributeLinear(cursor.GetPosition(), 1, UNICODE_SPACE, Attributes);
}
CATCH_LOG();
}
@ -806,9 +805,8 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
try
{
const OutputCellIterator it(UNICODE_SPACE, Attributes, NumChars);
const auto done = screenInfo.Write(it, cursor.GetPosition());
NumChars = done.GetCellDistance(it);
const auto result = screenInfo.GetTextBuffer().FillWithCharacterAndAttributeLinear(cursor.GetPosition(), NumChars, UNICODE_SPACE, Attributes);
NumChars = result.columnsWritten;
}
CATCH_LOG();
@ -865,15 +863,12 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
// If we're on top of a trailing cell, clear it and the previous cell.
if (Row.DbcsAttrAt(TargetPoint.X).IsTrailing())
{
// Space to clear for 2 cells.
OutputCellIterator it(UNICODE_SPACE, 2);
// Back target point up one.
auto writeTarget = TargetPoint;
writeTarget.X--;
// Write 2 clear cells.
screenInfo.Write(it, writeTarget);
screenInfo.GetTextBuffer().FillWithCharacterLinear(writeTarget, 2, UNICODE_SPACE);
}
}
catch (...)

View file

@ -109,11 +109,10 @@ void ConversionAreaInfo::SetAttributes(const TextAttribute& attr)
// Arguments:
// - text - Text to insert into the conversion area buffer
// - column - Column to start at (X position)
void ConversionAreaInfo::WriteText(const std::vector<OutputCell>& text,
void ConversionAreaInfo::WriteText(const RowImage& text,
const SHORT column)
{
gsl::span<const OutputCell> view(text.data(), text.size());
_screenBuffer->Write(view, { column, 0 });
_screenBuffer->GetTextBuffer().WriteRowImage(til::point{ column, 0 }, text);
}
// Routine Description:

View file

@ -61,7 +61,7 @@ public:
void SetWindowInfo(const SMALL_RECT view) noexcept;
void Paint() const noexcept;
void WriteText(const std::vector<OutputCell>& text, const SHORT column);
void WriteText(const RowImage& text, const SHORT column);
void SetAttributes(const TextAttribute& attr);
const TextBuffer& GetTextBuffer() const noexcept;

View file

@ -217,76 +217,60 @@ TextAttribute ConsoleImeInfo::s_RetrieveAttributeAt(const size_t pos,
// - colorArray - Array of color values provided by the IME.
// Return Value:
// - Vector of OutputCells where each one represents one cell of the output buffer.
std::vector<OutputCell> ConsoleImeInfo::s_ConvertToCells(const std::wstring_view text,
const gsl::span<const BYTE> attributes,
const gsl::span<const WORD> colorArray)
RowImage ConsoleImeInfo::s_ConvertToCells(std::wstring_view text,
const gsl::span<const BYTE> attributes,
const gsl::span<const WORD> colorArray)
{
RowImage e;
std::vector<OutputCell> cells;
// - Convert incoming wchar_t stream into UTF-16 units.
const auto glyphs = Utf16Parser::Parse(text);
// - Walk through all of the grouped up text, match up the correct attribute to it, and make a new cell.
size_t attributesUsed = 0;
for (const auto& parsedGlyph : glyphs)
size_t attributesUsed{};
while (!text.empty())
{
const std::wstring_view glyph{ parsedGlyph.data(), parsedGlyph.size() };
// Collect up attributes that apply to this glyph range.
auto drawingAttr = s_RetrieveAttributeAt(attributesUsed, attributes, colorArray);
attributesUsed++;
const auto glyph{ Utf16Parser::ParseNext(text) };
text = text.substr(glyph.size());
const uint8_t width{ gsl::narrow_cast<uint8_t>(IsGlyphFullWidth(glyph) ? 2u : 1u) };
// The IME gave us an attribute for every glyph position in a surrogate pair.
// But the only important information will be the cursor position.
// Check all additional attributes to see if the cursor resides on top of them.
for (size_t i = 1; i < glyph.size(); i++)
{
TextAttribute additionalAttr = s_RetrieveAttributeAt(attributesUsed, attributes, colorArray);
attributesUsed++;
if (additionalAttr.IsLeftVerticalDisplayed())
{
drawingAttr.SetLeftVerticalDisplayed(true);
}
if (additionalAttr.IsRightVerticalDisplayed())
{
drawingAttr.SetRightVerticalDisplayed(true);
}
// add glyph
e._data.append(glyph);
// register its width
e._cwid.append(width);
for (auto z{ glyph.size() }; z > 1; --z)
{ // add a trailing 0-width for every surrogate pair trail
e._cwid.append(0);
}
// We have to determine if the glyph range is 1 column or two.
// If it's full width, it's two, and we need to make sure we don't draw the cursor
// right down the middle of the character.
// Otherwise it's one column and we'll push it in with the default empty DbcsAttribute.
DbcsAttribute dbcsAttr;
if (IsGlyphFullWidth(glyph))
auto drawingAttr = s_RetrieveAttributeAt(attributesUsed++, attributes, colorArray);
if (width == 1 || !(drawingAttr.IsLeftVerticalDisplayed() || drawingAttr.IsRightVerticalDisplayed()))
{
auto leftHalfAttr = drawingAttr;
auto rightHalfAttr = drawingAttr;
// Don't draw lines in the middle of full width glyphs.
// If we need a right vertical, don't apply it to the left side of the character
if (leftHalfAttr.IsRightVerticalDisplayed())
for (auto z{ width }; z > 0; --z) // add an attribute for each cell covered
{
leftHalfAttr.SetRightVerticalDisplayed(false);
e._attrRow._data.append(drawingAttr);
}
dbcsAttr.SetLeading();
cells.emplace_back(glyph, dbcsAttr, leftHalfAttr);
dbcsAttr.SetTrailing();
// If we need a left vertical, don't apply it to the right side of the character
if (rightHalfAttr.IsLeftVerticalDisplayed())
{
rightHalfAttr.SetLeftVerticalDisplayed(false);
}
cells.emplace_back(glyph, dbcsAttr, rightHalfAttr);
}
else
{
cells.emplace_back(glyph, dbcsAttr, drawingAttr);
}
}
// the width was >1 and the attribute contained the left or the right cursor indicator; split them into [ ___ ] (left, middle, right)
auto first = drawingAttr;
first.SetRightVerticalDisplayed(false);
auto middle = drawingAttr;
middle.SetLeftVerticalDisplayed(false);
middle.SetRightVerticalDisplayed(false);
auto last = drawingAttr;
last.SetLeftVerticalDisplayed(false);
return cells;
e._attrRow._data.append(first);
for (auto z{ width }; z > 2; --z)
{
e._attrRow._data.append(middle);
}
e._attrRow._data.append(last);
}
e._width += width;
}
return e;
}
// Routine Description:
@ -308,81 +292,55 @@ std::vector<OutputCell> ConsoleImeInfo::s_ConvertToCells(const std::wstring_view
// However, if text couldn't fit in our line (full-width character starting at the very last cell)
// then we will give back the same begin and update the position for the next call to try again.
// If the viewport is deemed too small, we'll skip past it and advance begin past the entire full-width character.
std::vector<OutputCell>::const_iterator ConsoleImeInfo::_WriteConversionArea(const std::vector<OutputCell>::const_iterator begin,
const std::vector<OutputCell>::const_iterator end,
COORD& pos,
const Microsoft::Console::Types::Viewport view,
SCREEN_INFORMATION& screenInfo)
void ConsoleImeInfo::_WriteConversionArea(const RowImage& cells,
COORD pos,
const Microsoft::Console::Types::Viewport view,
SCREEN_INFORMATION& screenInfo)
{
// The position in the viewport where we will start inserting cells for this conversion area
// NOTE: We might exit early if there's not enough space to fit here, so we take a copy of
// the original and increment it up front.
const auto insertionPos = pos;
// Advance the cursor position to set up the next call for success (insert the next conversion area
// at the beginning of the following line)
pos.X = view.Left();
pos.Y++;
// The index of the last column in the viewport. (view is inclusive)
const auto finalViewColumn = view.RightInclusive();
// The maximum number of cells we can insert into a line.
const auto lineWidth = finalViewColumn - insertionPos.X + 1; // +1 because view was inclusive
// The iterator to the beginning position to form our line
const auto lineBegin = begin;
// The total number of cells we could insert.
const auto size = end - begin;
FAIL_FAST_IF(size <= 0); // It's a programming error to have <= 0 cells to insert.
// The end is the smaller of the remaining number of cells or the amount of line cells we can write before
// hitting the right edge of the viewport
auto lineEnd = lineBegin + std::min(size, (ptrdiff_t)lineWidth);
// We must attempt to compensate for ending on a leading byte. We can't split a full-width character across lines.
// As such, if the last item is a leading byte, back the end up by one.
FAIL_FAST_IF(lineEnd <= lineBegin); // We should have at least 1 space we can back up.
// Get the last cell in the run and if it's a leading byte, move the end position back one so we don't
// try to insert it.
const auto lastCell = lineEnd - 1;
if (lastCell->DbcsAttr().IsLeading())
auto remain{ cells };
do
{
lineEnd--;
}
// The maximum number of cells we can insert into a line.
const auto lineWidth = finalViewColumn - pos.X + 1; // +1 because view was inclusive
// Copy out the substring into a vector.
const std::vector<OutputCell> lineVec(lineBegin, lineEnd);
auto [draw, left] = cells.split(::base::saturated_cast<uint16_t>(lineWidth));
// Add a conversion area to the internal state to hold this line.
THROW_IF_FAILED(_AddConversionArea());
// Add a conversion area to the internal state to hold this line.
THROW_IF_FAILED(_AddConversionArea());
// Get the added conversion area.
auto& area = ConvAreaCompStr.back();
// Get the added conversion area.
auto& area = ConvAreaCompStr.back();
// Write our text into the conversion area.
area.WriteText(lineVec, insertionPos.X);
area.WriteText(draw, pos.X);
// Set the viewport and positioning parameters for the conversion area to describe to the renderer
// the appropriate location to overlay this conversion area on top of the main screen buffer inside the viewport.
const SMALL_RECT region{ insertionPos.X, 0, gsl::narrow<SHORT>(insertionPos.X + lineVec.size() - 1), 0 };
area.SetWindowInfo(region);
area.SetViewPos({ 0 - view.Left(), insertionPos.Y - view.Top() });
// Set the viewport and positioning parameters for the conversion area to describe to the renderer
// the appropriate location to overlay this conversion area on top of the main screen buffer inside the viewport.
const SMALL_RECT region{ pos.X, 0, gsl::narrow<SHORT>(pos.X + draw._width - 1), 0 };
area.SetWindowInfo(region);
area.SetViewPos({ 0 - view.Left(), pos.Y - view.Top() });
// Make it visible and paint it.
area.SetHidden(false);
area.Paint();
// Make it visible and paint it.
area.SetHidden(false);
area.Paint();
// Notify accessibility that we have updated the text in this display region within the viewport.
if (screenInfo.HasAccessibilityEventing())
{
screenInfo.NotifyAccessibilityEventing(insertionPos.X, insertionPos.Y, gsl::narrow<SHORT>(insertionPos.X + lineVec.size() - 1), insertionPos.Y);
}
// Notify accessibility that we have updated the text in this display region within the viewport.
if (screenInfo.HasAccessibilityEventing())
{
screenInfo.NotifyAccessibilityEventing(pos.X, pos.Y, gsl::narrow<SHORT>(pos.X + draw._width - 1), pos.Y);
}
// Hand back the iterator representing the end of what we used to be fed into the beginning of the next call.
return lineEnd;
if (remain._data.empty())
break;
// Advance the cursor position to set up the next call for success (insert the next conversion area
// at the beginning of the following line)
pos.X = view.Left();
pos.Y++;
remain = std::move(left);
} while (true);
}
// Routine Description:
@ -432,17 +390,7 @@ void ConsoleImeInfo::_WriteUndeterminedChars(const std::wstring_view text,
const auto view = screenInfo.GetViewport();
// Set cursor position relative to viewport
// Set up our iterators. We will walk through the entire set of cells from beginning to end.
// The first time, we will give the iterators as the whole span and the begin
// will be moved forward by the conversion area write to set up the next call.
auto begin = cells.cbegin();
const auto end = cells.cend();
// Write over and over updating the beginning iterator until we reach the end.
do
{
begin = _WriteConversionArea(begin, end, pos, view, screenInfo);
} while (begin < end);
_WriteConversionArea(cells, pos, view, screenInfo);
}
// Routine Description:

View file

@ -22,6 +22,7 @@ Revision History:
#include "../buffer/out/OutputCell.hpp"
#include "../buffer/out/TextAttribute.hpp"
#include "../renderer/inc/FontInfo.hpp"
#include "../buffer/out/Row.hpp"
#include "../types/inc/viewport.hpp"
#include "conareainfo.h"
@ -73,15 +74,14 @@ private:
const gsl::span<const BYTE> attributes,
const gsl::span<const WORD> colorArray);
static std::vector<OutputCell> s_ConvertToCells(const std::wstring_view text,
const gsl::span<const BYTE> attributes,
const gsl::span<const WORD> colorArray);
static RowImage s_ConvertToCells(std::wstring_view text,
const gsl::span<const BYTE> attributes,
const gsl::span<const WORD> colorArray);
std::vector<OutputCell>::const_iterator _WriteConversionArea(const std::vector<OutputCell>::const_iterator begin,
const std::vector<OutputCell>::const_iterator end,
COORD& pos,
const Microsoft::Console::Types::Viewport view,
SCREEN_INFORMATION& screenInfo);
void _WriteConversionArea(const RowImage& cells,
COORD pos,
const Microsoft::Console::Types::Viewport view,
SCREEN_INFORMATION& screenInfo);
bool _isSavedCursorVisible;

View file

@ -1010,8 +1010,9 @@ void EventsToUnicode(_Inout_ std::deque<std::unique_ptr<IInputEvent>>& inEvents,
const auto charInfos = gsl::span<const CHAR_INFO>(subspan.data(), subspan.size());
// Make the iterator and write to the target position.
OutputCellIterator it(charInfos);
storageBuffer.Write(it, target);
__debugbreak();
//OutputCellIterator it(charInfos);
//storageBuffer.Write(it, target);
}
// Since we've managed to write part of the request, return the clamped part that we actually used.

View file

@ -104,8 +104,9 @@ static void _CopyRectangle(SCREEN_INFORMATION& screenInfo,
do
{
const auto data = OutputCell(*screenInfo.GetCellDataAt(sourcePos));
screenInfo.Write(OutputCellIterator({ &data, 1 }), targetPos);
__debugbreak();
//const auto data = OutputCell(*screenInfo.GetCellDataAt(sourcePos));
//screenInfo.Write(OutputCellIterator({ &data, 1 }), targetPos);
source.WalkInBounds(sourcePos, walkDirection);
} while (target.WalkInBounds(targetPos, walkDirection));

View file

@ -2334,42 +2334,8 @@ OutputCellRect SCREEN_INFORMATION::ReadRect(const Viewport viewport) const
return result;
}
size_t SCREEN_INFORMATION::WriteMeasuredStringLinear(std::wstring_view string, const til::small_rle<uint8_t, uint16_t, 3>& measurements)
{
return _textBuffer->WriteMeasuredStringLinear(_textBuffer->GetCursor().GetPosition(), string, measurements);
}
// Routine Description:
// - Writes cells to the output buffer at the cursor position.
// Arguments:
// - it - Iterator representing output cell data to write.
// Return Value:
// - the iterator at its final position
// Note:
// - will throw exception on error.
OutputCellIterator SCREEN_INFORMATION::Write(const OutputCellIterator it)
{
return _textBuffer->Write(it);
}
// Routine Description:
// - Writes cells to the output buffer.
// Arguments:
// - it - Iterator representing output cell data to write.
// - target - The position 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
// Return Value:
// - the iterator at its final position
// Note:
// - will throw exception on error.
OutputCellIterator SCREEN_INFORMATION::Write(const OutputCellIterator it,
const COORD target,
const std::optional<bool> wrap)
{
// NOTE: if wrap = true/false, we want to set the line's wrap to true/false (respectively) if we reach the end of the line
return _textBuffer->Write(it, target, wrap);
}
#if 0
TODO(DH)
// Routine Description:
// - This routine writes a rectangular region into the screen buffer.
// Arguments:
@ -2393,6 +2359,7 @@ OutputCellIterator SCREEN_INFORMATION::WriteRect(const OutputCellIterator it,
return iter;
}
#endif
// Routine Description:
// - This routine writes a rectangular region into the screen buffer.
@ -2406,6 +2373,10 @@ OutputCellIterator SCREEN_INFORMATION::WriteRect(const OutputCellIterator it,
void SCREEN_INFORMATION::WriteRect(const OutputCellRect& data,
const COORD location)
{
(void)data;
(void)location;
__debugbreak();
#if 0
for (size_t i = 0; i < data.Height(); i++)
{
const auto iter = data.GetRowIter(i);
@ -2414,8 +2385,10 @@ void SCREEN_INFORMATION::WriteRect(const OutputCellRect& data,
point.X = location.X;
point.Y = location.Y + static_cast<short>(i);
_textBuffer->WriteLine(iter, point);
__debugbreak();
//_textBuffer->WriteLine(iter, point);
}
#endif
}
// Routine Description:

View file

@ -135,15 +135,7 @@ public:
TextBufferTextIterator GetTextLineDataAt(const COORD at) const;
TextBufferTextIterator GetTextDataAt(const COORD at, const Microsoft::Console::Types::Viewport limit) const;
size_t WriteMeasuredStringLinear(std::wstring_view string, const til::small_rle<uint8_t, uint16_t, 3>& measurements);
OutputCellIterator Write(const OutputCellIterator it);
OutputCellIterator Write(const OutputCellIterator it,
const COORD target,
const std::optional<bool> wrap = true);
OutputCellIterator WriteRect(const OutputCellIterator it,
const Microsoft::Console::Types::Viewport viewport);
size_t WriteMeasuredStringLinear(std::wstring_view string, const RowMeasurementBuffer& measurements);
void WriteRect(const OutputCellRect& data,
const COORD location);

View file

@ -520,6 +520,22 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
_total_length = new_size;
}
void append(const value_type& value)
{
++_total_length;
if (!_runs.empty())
{
auto& back{ _runs.back() };
if (back.value == value)
{
++back.length;
return;
}
}
// Either runs was empty, or we have to append a new run.
_runs.emplace_back(value, S{ 1 });
}
constexpr bool operator==(const basic_rle& other) const noexcept
{
return _total_length == other._total_length && _runs == other._runs;