HAX: rle-based row

This commit is contained in:
Dustin L. Howett 2021-03-21 20:18:04 -05:00 committed by Dustin Howett
parent 227ce8ff20
commit 00af538278
22 changed files with 1407 additions and 872 deletions

1113
rle.h Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,285 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "CharRow.hpp"
#include "unicode.hpp"
#include "Row.hpp"
// Routine Description:
// - constructor
// Arguments:
// - rowWidth - the size (in wchar_t) of the char and attribute rows
// - pParent - the parent ROW
// Return Value:
// - instantiated object
// Note: will through if unable to allocate char/attribute buffers
#pragma warning(push)
#pragma warning(disable : 26447) // small_vector's constructor says it can throw but it should not given how we use it. This suppresses this error for the AuditMode build.
CharRow::CharRow(size_t rowWidth, ROW* const pParent) noexcept :
_data(rowWidth, value_type()),
_pParent{ FAIL_FAST_IF_NULL(pParent) }
{
}
#pragma warning(pop)
// Routine Description:
// - gets the size of the row, in glyph cells
// Arguments:
// - <none>
// Return Value:
// - the size of the row
size_t CharRow::size() const noexcept
{
return _data.size();
}
// Routine Description:
// - Sets all properties of the CharRowBase to default values
// Arguments:
// - sRowWidth - The width of the row.
// Return Value:
// - <none>
void CharRow::Reset() noexcept
{
for (auto& cell : _data)
{
cell.Reset();
}
}
// Routine Description:
// - resizes the width of the CharRowBase
// Arguments:
// - newSize - the new width of the character and attributes rows
// Return Value:
// - S_OK on success, otherwise relevant error code
[[nodiscard]] HRESULT CharRow::Resize(const size_t newSize) noexcept
{
try
{
const value_type insertVals;
_data.resize(newSize, insertVals);
}
CATCH_RETURN();
return S_OK;
}
typename CharRow::iterator CharRow::begin() noexcept
{
return _data.begin();
}
typename CharRow::const_iterator CharRow::cbegin() const noexcept
{
return _data.cbegin();
}
typename CharRow::iterator CharRow::end() noexcept
{
return _data.end();
}
typename CharRow::const_iterator CharRow::cend() const noexcept
{
return _data.cend();
}
// Routine Description:
// - Inspects the current internal string to find the left edge of it
// Arguments:
// - <none>
// Return Value:
// - The calculated left boundary of the internal string.
size_t CharRow::MeasureLeft() const noexcept
{
const_iterator it = _data.cbegin();
while (it != _data.cend() && it->IsSpace())
{
++it;
}
return it - _data.cbegin();
}
// Routine Description:
// - Inspects the current internal string to find the right edge of it
// Arguments:
// - <none>
// Return Value:
// - The calculated right boundary of the internal string.
size_t CharRow::MeasureRight() const
{
const_reverse_iterator it = _data.crbegin();
while (it != _data.crend() && it->IsSpace())
{
++it;
}
return _data.crend() - it;
}
void CharRow::ClearCell(const size_t column)
{
_data.at(column).Reset();
}
// Routine Description:
// - Tells you whether or not this row contains any valid text.
// Arguments:
// - <none>
// Return Value:
// - True if there is valid text in this row. False otherwise.
bool CharRow::ContainsText() const noexcept
{
for (const value_type& cell : _data)
{
if (!cell.IsSpace())
{
return true;
}
}
return false;
}
// Routine Description:
// - gets the attribute at the specified column
// Arguments:
// - column - the column to get the attribute for
// Return Value:
// - the attribute
// Note: will throw exception if column is out of bounds
const DbcsAttribute& CharRow::DbcsAttrAt(const size_t column) const
{
return _data.at(column).DbcsAttr();
}
// Routine Description:
// - gets the attribute at the specified column
// Arguments:
// - column - the column to get the attribute for
// Return Value:
// - the attribute
// Note: will throw exception if column is out of bounds
DbcsAttribute& CharRow::DbcsAttrAt(const size_t column)
{
return _data.at(column).DbcsAttr();
}
// Routine Description:
// - resets text data at column
// Arguments:
// - column - column index to clear text data from
// Return Value:
// - <none>
// Note: will throw exception if column is out of bounds
void CharRow::ClearGlyph(const size_t column)
{
_data.at(column).EraseChars();
}
std::wstring CharRow::GetText() const
{
std::wstring wstr;
wstr.reserve(_data.size());
for (size_t i = 0; i < _data.size(); ++i)
{
if (!_data.at(i).DbcsAttr().IsTrailing())
{
wstr.append(GlyphDataAt(i));
}
}
return wstr;
}
// Method Description:
// - get delimiter class for a position in the char row
// - used for double click selection and uia word navigation
// Arguments:
// - column: column to get text data for
// - wordDelimiters: the delimiters defined as a part of the DelimiterClass::DelimiterChar
// Return Value:
// - the delimiter class for the given char
const DelimiterClass CharRow::DelimiterClassAt(const size_t column, const std::wstring_view wordDelimiters) const
{
THROW_HR_IF(E_INVALIDARG, column >= _data.size());
const auto glyph = *GlyphDataAt(column).begin();
if (glyph <= UNICODE_SPACE)
{
return DelimiterClass::ControlChar;
}
else if (wordDelimiters.find(glyph) != std::wstring_view::npos)
{
return DelimiterClass::DelimiterChar;
}
else
{
return DelimiterClass::RegularChar;
}
}
UnicodeStorage& CharRow::GetUnicodeStorage() noexcept
{
return _pParent->GetUnicodeStorage();
}
const UnicodeStorage& CharRow::GetUnicodeStorage() const noexcept
{
return _pParent->GetUnicodeStorage();
}
// Routine Description:
// - calculates the key used by the given column of the char row to store glyph data in UnicodeStorage
// Arguments:
// - column - the column to generate the key for
// Return Value:
// - the COORD key for data access from UnicodeStorage for the column
COORD CharRow::GetStorageKey(const size_t column) const noexcept
{
return { gsl::narrow<SHORT>(column), _pParent->GetId() };
}
// Routine Description:
// - assignment operator. will store extended glyph data in a separate storage location
// Arguments:
// - chars - the glyph data to store
void CharRow::WriteCharsIntoColumn(size_t column, const std::wstring_view chars)
{
THROW_HR_IF(E_INVALIDARG, chars.empty());
auto& position = _data.at(column);
if (chars.size() == 1)
{
position.Char() = chars.front();
position.DbcsAttr().SetGlyphStored(false);
}
else
{
auto& storage = _pParent->GetUnicodeStorage();
const auto key = GetStorageKey(column);
storage.StoreGlyph(key, { chars.cbegin(), chars.cend() });
position.DbcsAttr().SetGlyphStored(true);
}
}
const std::wstring_view CharRow::GlyphDataAt(const size_t column) const
{
if (_data.at(column).DbcsAttr().IsGlyphStored())
{
const auto& text = GetUnicodeStorage().GetText(GetStorageKey(column));
return { text.data(), text.size() };
}
else
{
return { &_data.at(column).Char(), 1 };
}
}
// - Updates the pointer to the parent row (which might change if we shuffle the rows around)
// Arguments:
// - pParent - Pointer to the parent row
void CharRow::UpdateParent(ROW* const pParent)
{
_pParent = FAIL_FAST_IF_NULL(pParent);
}

View file

@ -1,112 +0,0 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- CharRow.hpp
Abstract:
- contains data structure for UCS2 encoded character data of a row
Author(s):
- Michael Niksa (miniksa) 10-Apr-2014
- Paul Campbell (paulcam) 10-Apr-2014
Revision History:
- From components of output.h/.c
by Therese Stowell (ThereseS) 1990-1991
- Pulled into its own file from textBuffer.hpp/cpp (AustDi, 2017)
--*/
#pragma once
#include "DbcsAttribute.hpp"
#include "CharRowCell.hpp"
#include "UnicodeStorage.hpp"
class ROW;
enum class DelimiterClass
{
ControlChar,
DelimiterChar,
RegularChar
};
// the characters of one row of screen buffer
// we keep the following values so that we don't write
// more pixels to the screen than we have to:
// left is initialized to screenbuffer width. right is
// initialized to zero.
//
// [ foo.bar 12-12-61 ]
// ^ ^ ^ ^
// | | | |
// Chars Left Right end of Chars buffer
class CharRow final
{
public:
using glyph_type = typename wchar_t;
using value_type = typename CharRowCell;
using iterator = typename boost::container::small_vector_base<value_type>::iterator;
using const_iterator = typename boost::container::small_vector_base<value_type>::const_iterator;
using const_reverse_iterator = typename boost::container::small_vector_base<value_type>::const_reverse_iterator;
CharRow(size_t rowWidth, ROW* const pParent) noexcept;
size_t size() const noexcept;
[[nodiscard]] HRESULT Resize(const size_t newSize) noexcept;
size_t MeasureLeft() const noexcept;
size_t MeasureRight() const;
bool ContainsText() const noexcept;
const DbcsAttribute& DbcsAttrAt(const size_t column) const;
DbcsAttribute& DbcsAttrAt(const size_t column);
void ClearGlyph(const size_t column);
const DelimiterClass DelimiterClassAt(const size_t column, const std::wstring_view wordDelimiters) const;
// working with glyphs
const std::wstring_view GlyphDataAt(const size_t column) const;
// iterators
iterator begin() noexcept;
const_iterator cbegin() const noexcept;
const_iterator begin() const noexcept { return cbegin(); }
iterator end() noexcept;
const_iterator cend() const noexcept;
const_iterator end() const noexcept { return cend(); }
UnicodeStorage& GetUnicodeStorage() noexcept;
const UnicodeStorage& GetUnicodeStorage() const noexcept;
COORD GetStorageKey(const size_t column) const noexcept;
void UpdateParent(ROW* const pParent);
friend class ROW;
private:
void Reset() noexcept;
void ClearCell(const size_t column);
std::wstring GetText() const;
void WriteCharsIntoColumn(size_t column, const std::wstring_view chars);
protected:
// storage for glyph data and dbcs attributes
boost::container::small_vector<value_type, 120> _data;
// ROW that this CharRow belongs to
ROW* _pParent;
};
template<typename InputIt1, typename InputIt2>
void OverwriteColumns(InputIt1 startChars, InputIt1 endChars, InputIt2 startAttrs, CharRow::iterator outIt)
{
std::transform(startChars,
endChars,
startAttrs,
outIt,
[](const wchar_t wch, const DbcsAttribute attr) {
return CharRow::value_type{ wch, attr };
});
}

View file

@ -1,73 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "CharRowCell.hpp"
#include "unicode.hpp"
// default glyph value, used for resetting the character data portion of a cell
static constexpr wchar_t DefaultValue = UNICODE_SPACE;
// Routine Description:
// - "erases" the glyph. really sets it back to the default "empty" value
void CharRowCell::EraseChars() noexcept
{
if (_attr.IsGlyphStored())
{
_attr.SetGlyphStored(false);
}
_wch = DefaultValue;
}
// Routine Description:
// - resets this object back to the defaults it would have from the default constructor
void CharRowCell::Reset() noexcept
{
_attr.Reset();
_wch = DefaultValue;
}
// Routine Description:
// - checks if cell contains a space glyph
// Return Value:
// - true if cell contains a space glyph, false otherwise
bool CharRowCell::IsSpace() const noexcept
{
return !_attr.IsGlyphStored() && _wch == UNICODE_SPACE;
}
// Routine Description:
// - Access the DbcsAttribute for the cell
// Return Value:
// - ref to the cells' DbcsAttribute
DbcsAttribute& CharRowCell::DbcsAttr() noexcept
{
return _attr;
}
// Routine Description:
// - Access the DbcsAttribute for the cell
// Return Value:
// - ref to the cells' DbcsAttribute
const DbcsAttribute& CharRowCell::DbcsAttr() const noexcept
{
return _attr;
}
// Routine Description:
// - Access the cell's wchar field. this does not access any char data through UnicodeStorage.
// Return Value:
// - the cell's wchar field
wchar_t& CharRowCell::Char() noexcept
{
return _wch;
}
// Routine Description:
// - Access the cell's wchar field. this does not access any char data through UnicodeStorage.
// Return Value:
// - the cell's wchar field
const wchar_t& CharRowCell::Char() const noexcept
{
return _wch;
}

View file

@ -1,65 +0,0 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- CharRowCell.hpp
Abstract:
- data structure for one cell of a char row. contains the char data for one
coordinate position in the output buffer (leading/trailing information and
the char itself.
Author(s):
- Austin Diviness (AustDi) 02-May-2018
--*/
#pragma once
#include "DbcsAttribute.hpp"
#include "unicode.hpp"
#if (defined(_M_IX86) || defined(_M_AMD64))
// currently CharRowCell's fields use 3 bytes of memory, leaving the 4th byte in unused. this leads
// to a rather large amount of useless memory allocated. so instead, pack CharRowCell by bytes instead of words.
#pragma pack(push, 1)
#endif
class CharRowCell final
{
public:
CharRowCell() noexcept = default;
CharRowCell(const wchar_t wch, const DbcsAttribute attr) noexcept
:
_wch(wch),
_attr(attr)
{
}
void EraseChars() noexcept;
void Reset() noexcept;
bool IsSpace() const noexcept;
DbcsAttribute& DbcsAttr() noexcept;
const DbcsAttribute& DbcsAttr() const noexcept;
wchar_t& Char() noexcept;
const wchar_t& Char() const noexcept;
friend constexpr bool operator==(const CharRowCell& a, const CharRowCell& b) noexcept;
private:
wchar_t _wch{ UNICODE_SPACE };
DbcsAttribute _attr{};
};
#if (defined(_M_IX86) || defined(_M_AMD64))
#pragma pack(pop)
#endif
constexpr bool operator==(const CharRowCell& a, const CharRowCell& b) noexcept
{
return (a._wch == b._wch &&
a._attr == b._attr);
}

View file

@ -27,14 +27,12 @@ public:
};
DbcsAttribute() noexcept :
_attribute{ Attribute::Single },
_glyphStored{ false }
_attribute{ Attribute::Single }
{
}
DbcsAttribute(const Attribute attribute) noexcept :
_attribute{ attribute },
_glyphStored{ false }
_attribute{ attribute }
{
}
@ -58,16 +56,6 @@ public:
return IsLeading() || IsTrailing();
}
constexpr bool IsGlyphStored() const noexcept
{
return _glyphStored;
}
void SetGlyphStored(const bool stored) noexcept
{
_glyphStored = stored;
}
void SetSingle() noexcept
{
_attribute = Attribute::Single;
@ -86,7 +74,6 @@ public:
void Reset() noexcept
{
SetSingle();
SetGlyphStored(false);
}
WORD GeneratePublicApiAttributeFormat() const noexcept
@ -127,7 +114,6 @@ public:
private:
Attribute _attribute : 2;
bool _glyphStored : 1;
#ifdef UNIT_TESTING
friend class TextBufferTests;

View file

@ -3,7 +3,6 @@
#include "precomp.h"
#include "Row.hpp"
#include "CharRow.hpp"
#include "textBuffer.hpp"
#include "../types/inc/convert.hpp"
@ -16,15 +15,14 @@
// - 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 },
ROW::ROW(const SHORT /*rowId*/, const unsigned short rowWidth, const TextAttribute fillAttribute, TextBuffer* const /*pParent*/) :
_attrRow{ rowWidth, fillAttribute },
_lineRendition{ LineRendition::SingleWidth },
_wrapForced{ false },
_doubleBytePadded{ false },
_pParent{ pParent }
_rowWidth(rowWidth),
_cwid(_rowWidth, 1),
_data(_rowWidth, UNICODE_SPACE)
{
}
@ -39,7 +37,13 @@ bool ROW::Reset(const TextAttribute Attr)
_lineRendition = LineRendition::SingleWidth;
_wrapForced = false;
_doubleBytePadded = false;
_charRow.Reset();
#if TIL_RLE_WORKS_LIKE_VECTOR
_cwid.replace(0, _rowWidth, _rowWidth, 1);
#else
_cwid.resize(_rowWidth);
_cwid.fill(1);
#endif
_data.replace(0, _rowWidth, _rowWidth, UNICODE_SPACE);
try
{
_attrRow.Reset(Attr);
@ -60,7 +64,13 @@ bool ROW::Reset(const TextAttribute Attr)
// - S_OK if successful, otherwise relevant error
[[nodiscard]] HRESULT ROW::Resize(const unsigned short width)
{
RETURN_IF_FAILED(_charRow.Resize(width));
_data.resize(width, L' ');
#if TIL_RLE_WORKS_LIKE_VECTOR
_cwid.resize(width, 1);
#else
_cwid.resize(width);
_cwid.fill(1);
#endif
try
{
_attrRow.Resize(width);
@ -80,18 +90,8 @@ bool ROW::Reset(const TextAttribute Attr)
// - <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();
THROW_HR_IF(E_INVALIDARG, column >= _rowWidth);
WriteGlyphAtMeasured(column, 1, L" ");
}
// Routine Description:
@ -105,17 +105,19 @@ const UnicodeStorage& ROW::GetUnicodeStorage() const noexcept
// - 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());
THROW_HR_IF(E_INVALIDARG, index >= _rowWidth);
THROW_HR_IF(E_INVALIDARG, limitRight.value_or(0) >= _rowWidth);
// 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);
const auto finalColumnInRow = limitRight.value_or(_rowWidth - 1);
auto currentColor = it->TextAttr();
uint16_t colorUses = 0;
uint16_t colorStarts = gsl::narrow_cast<uint16_t>(index);
uint16_t currentIndex = colorStarts;
auto [ibegin, ilen, ioff, icols] = _indicesForCol(currentIndex);
auto ihintcol = currentIndex - ioff;
while (it && currentIndex <= finalColumnInRow)
{
// Fill the color if the behavior isn't set to keeping the current color.
@ -151,23 +153,32 @@ OutputCellIterator ROW::WriteCells(OutputCellIterator it, const size_t index, co
// 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);
ClearColumn(currentIndex);
it.AddCellDistanceFault(1); // we couldn't fit a cell here but we skipped a column :|
}
// 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);
ClearColumn(currentIndex);
it.AddCellDistanceFault(1); // we couldn't fit a cell here but we skipped a column :|
SetDoubleBytePadded(true);
}
// Otherwise, copy the data given and increment the iterator.
else
{
_charRow.DbcsAttrAt(currentIndex) = it->DbcsAttr();
_charRow.WriteCharsIntoColumn(currentIndex, it->Chars());
++it;
if (!it->DbcsAttr().IsTrailing())
{
auto d = it->DbcsAttr().IsSingle() ? 1 : 2;
std::tie(ibegin, ihintcol) = WriteGlyphAtMeasured(currentIndex, d, it->Chars(), ibegin, ihintcol);
currentIndex += d - 1;
colorUses += d - 1;
while (d > 0)
{ // TODO(DH) FFS
++it;
--d;
}
}
}
// If we're asked to (un)set the wrap status and we just filled the last column with some text...

View file

@ -24,11 +24,25 @@ Revision History:
#include "LineRendition.hpp"
#include "OutputCell.hpp"
#include "OutputCellIterator.hpp"
#include "CharRow.hpp"
#include "UnicodeStorage.hpp"
#include "unicode.hpp"
#define TIL_RLE_WORKS_LIKE_VECTOR 0
#if !TIL_RLE_WORKS_LIKE_VECTOR
#include "../../../rle.h"
#endif
#pragma warning(push)
#pragma warning(disable : 4267)
class TextBuffer;
enum class DelimiterClass
{
ControlChar,
DelimiterChar,
RegularChar
};
class ROW final
{
public:
@ -42,26 +56,17 @@ public:
void SetDoubleBytePadded(const bool doubleBytePadded) noexcept { _doubleBytePadded = doubleBytePadded; }
bool WasDoubleBytePadded() const noexcept { return _doubleBytePadded; }
const CharRow& GetCharRow() const noexcept { return _charRow; }
CharRow& GetCharRow() noexcept { return _charRow; }
const ATTR_ROW& GetAttrRow() const noexcept { return _attrRow; }
ATTR_ROW& GetAttrRow() noexcept { return _attrRow; }
LineRendition GetLineRendition() const noexcept { return _lineRendition; }
void SetLineRendition(const LineRendition lineRendition) noexcept { _lineRendition = lineRendition; }
SHORT GetId() const noexcept { return _id; }
void SetId(const SHORT id) noexcept { _id = id; }
bool Reset(const TextAttribute Attr);
[[nodiscard]] HRESULT Resize(const unsigned short width);
void ClearColumn(const size_t column);
std::wstring GetText() const { return _charRow.GetText(); }
UnicodeStorage& GetUnicodeStorage() noexcept;
const UnicodeStorage& GetUnicodeStorage() const noexcept;
std::wstring GetText() const { return _data; }
OutputCellIterator WriteCells(OutputCellIterator it, const size_t index, const std::optional<bool> wrap = std::nullopt, std::optional<size_t> limitRight = std::nullopt);
@ -71,23 +76,221 @@ public:
#endif
private:
CharRow _charRow;
ATTR_ROW _attrRow;
LineRendition _lineRendition;
SHORT _id;
unsigned short _rowWidth;
// Occurs when the user runs out of text in a given row and we're forced to wrap the cursor to the next line
bool _wrapForced;
// Occurs when the user runs out of text to support a double byte character and we're forced to the next line
bool _doubleBytePadded;
TextBuffer* _pParent; // non ownership pointer
std::wstring _data;
#if TIL_RLE_WORKS_LIKE_VECTOR
std::basic_string<uint16_t> _cwid;
#else
til::rle<uint8_t, uint16_t> _cwid;
#endif
std::tuple<size_t, size_t, size_t, size_t> _indicesForCol(size_t col, typename decltype(_data)::size_type hint = 0, size_t colstart = 0) const
{
size_t c{ colstart };
auto it{ _cwid.cbegin() + hint };
while (it != _cwid.cend())
{
if (c + *it > col)
{
// if we would overshoot, be done
break;
}
c += *it++;
} // accumulate columns until we hit the requested one
if (it == _cwid.cend())
{
// we never hit it!
throw 0;
}
// it points at the column width of the character that tripped us over the limit (char that was a hit)
auto eit{ std::find_if_not(it + 1, _cwid.cend(), [](auto&& c) { return c == 0; }) };
// eit points at first column that is nonzero after it, or the end
const auto len{ eit - it };
const auto cols{ *it }; // how big was the char we landed on
return { it - _cwid.cbegin(), len, col - c, cols };
}
public:
std::wstring_view GlyphAt(size_t col) const
{
auto [begin, len, off, _] = _indicesForCol(col);
return { _data.data() + begin, len };
}
std::pair<size_t, size_t> WriteGlyphAtMeasured(size_t col, size_t ncols, std::wstring_view glyph, typename decltype(_data)::size_type hint = 0, size_t colstart = 0)
{
// When we want to replace a column, or set of columns, with a glyph, we need to:
// * Figure out the physical extent of the character in that cell (UTF-16 code units).
// * Figure out the columnar extent of the character in that cell (how many columns it covers).
// * In the simple case (1->1, 2->2), there will be no damage.
// * In the complex case (2->1, 1->2, 2->2 with middle overlap), there *WILL* be damage.
// * Replace the physical character data in that cell with the new character data.
// * Insert padding characters to the left and right to account for damage.
//
// ## DAMAGE
// Damage is measured in the number of columns to the left
// and right of the new glyph that are now NO LONGER VALID because
// they were double-width characters that are being cut in half,
// or single-width characters that are collateral damage from stomping
// them with a double-width character.
auto [begin, len, off, cols]{ _indicesForCol(col, hint, colstart) };
const auto minDamageColumn{ col - off }; // Column damage to the left (where we overlapped the right of a wide glyph)
auto maxDamageColumnExclusive{ minDamageColumn + cols }; // Column damage to the right (where we overlapped the left of a wide glyph)
while (maxDamageColumnExclusive < col + ncols)
{
auto [nbegin, nlen, noff, newcols]{ _indicesForCol(maxDamageColumnExclusive, begin, minDamageColumn) };
// *INVARIANT* the beginning of the next column range must have a different beginning byte
// This column began at a different data index, so we have to delete its data too.
// Since it's contiguous, just increment len.
len += nlen;
maxDamageColumnExclusive += newcols;
}
if (minDamageColumn == col && maxDamageColumnExclusive == col + ncols)
{
// We are only damaging as many columns as we are introducing -- no spillover (!)
// We can replace the code units in the data directly, and we can replace the
// column counts with [col, 0, 0...] (with as many zeroes as we need to account
// for any code units past the first.)
_data.replace(begin, len, glyph);
#if TIL_RLE_WORKS_LIKE_VECTOR // rle doesn't work like vec
_cwid.replace(begin, len, glyph.size(), 0);
_cwid.at(begin) = (uint16_t)ncols;
#else
auto old = _cwid.substr(uint16_t(begin + len));
_cwid.fill(ncols, begin);
_cwid.fill(0, begin + 1);
_cwid.assign(old.run_cbegin(), old.run_cend(), begin + glyph.size());
_cwid.resize(_data.size());
#endif
}
else
{
// We are damaging multiple columns -- oops. We need to insert replacement characters
// to get us from the leftmost side of the damaged glyph up to the leftmost side of
// our newly-inserted region. We also need to insert replacement characters from the
// rightmost side of our glyph to the rightmost side of the glyph that was once in
// that column.
// Left side count : col - minDamageColumn
// Right side count: maxDamageColumn - (col + ncols)
const auto replacementCodeUnits{ (col - minDamageColumn) + glyph.size() + (maxDamageColumnExclusive - (col + ncols)) };
std::wstring replacement(replacementCodeUnits, UNICODE_SPACE);
replacement.replace(col - minDamageColumn, glyph.size(), glyph);
// New advances:
// Our glyph and all its trailers
// v-----v
// [1, ..., 1, X, 0, 0, 1, ..., 1]
// ^-------^ ^-------^
// Each replacement space char
// is one column wide. We have
// to insert [1]s for each
// damaged column.
#if TIL_RLE_WORKS_LIKE_VECTOR // rle doesn't work like vector
std::basic_string<uint16_t> cadvs(replacementCodeUnits, 1);
cadvs.replace(col - minDamageColumn, 1, 1, (uint16_t)ncols); // our glyph takes up ncols
cadvs.replace(col - minDamageColumn + 1, glyph.size() - 1, glyph.size() - 1, (uint16_t)0); // and its trailers take up 0
_data.replace(begin, len, replacement);
_cwid.replace(begin, len, cadvs);
#else
auto old = _cwid.substr(uint16_t(begin + replacementCodeUnits));
_data.replace(begin, len, replacement);
_cwid.resize(_data.size());
_cwid.fill(1, begin);
_cwid.fill(ncols, begin + (col - minDamageColumn));
_cwid.fill(0, begin + (col - minDamageColumn) + 1);
_cwid.fill(1, begin + (col - minDamageColumn) + glyph.size());
_cwid.assign(old.run_cbegin(), old.run_cend(), begin + replacementCodeUnits);
_cwid.resize(_data.size());
#endif
}
if (_cwid.size() != _data.size())
{
#if TIL_RLE_WORKS_LIKE_VECTOR // rle doesn't work like vector
_cwid.resize(_data.size(), 0);
#else
const auto old{ _cwid.size() };
_cwid.resize(_data.size());
if (old < _cwid.size())
_cwid.fill(0, old);
#endif
}
// Distance from requested column to final
_maxc = std::max(_maxc, maxDamageColumnExclusive);
return { begin + glyph.size(), col + ncols };
}
DbcsAttribute DbcsAttrAt(size_t col) const
{
auto [begin, len, off, ncols] = _indicesForCol(col);
if (ncols == 1)
{
return DbcsAttribute{ DbcsAttribute::Attribute::Single };
}
else if (off >= 1)
{
return DbcsAttribute{ DbcsAttribute::Attribute::Trailing };
}
else if (off == 0)
{
return DbcsAttribute{ DbcsAttribute::Attribute::Leading };
}
return DbcsAttribute{ DbcsAttribute::Attribute::Single };
}
// Method Description:
// - get delimiter class for a position in the char row
// - used for double click selection and uia word navigation
// Arguments:
// - column: column to get text data for
// - wordDelimiters: the delimiters defined as a part of the DelimiterClass::DelimiterChar
// Return Value:
// - the delimiter class for the given char
const DelimiterClass DelimiterClassAt(const size_t column, const std::wstring_view wordDelimiters) const
{
THROW_HR_IF(E_INVALIDARG, column >= _rowWidth);
const auto glyph = *GlyphAt(column).begin();
if (glyph <= UNICODE_SPACE)
{
return DelimiterClass::ControlChar;
}
else if (wordDelimiters.find(glyph) != std::wstring_view::npos)
{
return DelimiterClass::DelimiterChar;
}
else
{
return DelimiterClass::RegularChar;
}
}
size_t _maxc{};
size_t MeasureRight() const
{
return _maxc;
}
};
#ifdef UNIT_TESTING
constexpr bool operator==(const ROW& a, const ROW& b) noexcept
{
// comparison is only used in the tests; this should suffice.
return (a._pParent == b._pParent &&
a._id == b._id);
return (a._data == b._data &&
a._cwid == b._cwid &&
a._attrRow == b._attrRow &&
a._rowWidth == b._rowWidth &&
a._wrapForced == b._wrapForced &&
a._doubleBytePadded == b._doubleBytePadded);
}
#endif
#pragma warning(pop)

View file

@ -1,98 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "UnicodeStorage.hpp"
UnicodeStorage::UnicodeStorage() noexcept :
_map{}
{
}
// Routine Description:
// - fetches the text associated with key
// Arguments:
// - key - the key into the storage
// Return Value:
// - the glyph data associated with key
// Note: will throw exception if key is not stored yet
const UnicodeStorage::mapped_type& UnicodeStorage::GetText(const key_type key) const
{
return _map.at(key);
}
// Routine Description:
// - stores glyph data associated with key.
// Arguments:
// - key - the key into the storage
// - glyph - the glyph data to store
void UnicodeStorage::StoreGlyph(const key_type key, const mapped_type& glyph)
{
_map.insert_or_assign(key, glyph);
}
// Routine Description:
// - erases key and its associated data from the storage
// Arguments:
// - key - the key to remove
void UnicodeStorage::Erase(const key_type key) noexcept
{
_map.erase(key);
}
// Routine Description:
// - Remaps all of the stored items to new coordinate positions
// based on a bulk rearrangement of row IDs and potential row width resize.
// Arguments:
// - rowMap - A map of the old row IDs to the new row IDs.
// - width - The width of the new row. Remove any items that are beyond the row width.
// - Use nullopt if we're not resizing the width of the row, just renumbering the rows.
void UnicodeStorage::Remap(const std::unordered_map<SHORT, SHORT>& rowMap, const std::optional<SHORT> width)
{
// Make a temporary map to hold all the new row positioning
std::unordered_map<key_type, mapped_type> newMap;
// Walk through every stored item.
for (const auto& pair : _map)
{
// Extract the old coordinate position
const auto oldCoord = pair.first;
// Only try to short-circuit based on width if we were told it changed
// by being given a new width value.
if (width.has_value())
{
// Get the column ID
const auto oldColId = oldCoord.X;
// If the column index is at/beyond the row width, don't bother copying it to the new map.
if (oldColId >= width.value())
{
continue;
}
}
// Get the row ID from the position as that's what we need to remap
const auto oldRowId = oldCoord.Y;
// Use the mapping given to convert the old row ID to the new row ID
const auto mapIter = rowMap.find(oldRowId);
// If there's no mapping to a new row, don't bother copying it to the new map. The row is gone.
if (mapIter == rowMap.end())
{
continue;
}
const auto newRowId = mapIter->second;
// Generate a new coordinate with the same X as the old one, but a new Y value.
const auto newCoord = COORD{ oldCoord.X, newRowId };
// Put the adjusted coordinate into the map with the original value.
newMap.emplace(newCoord, pair.second);
}
// Swap into the stored map, free the temporary when we exit.
_map.swap(newMap);
}

View file

@ -1,67 +0,0 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- UnicodeStorage.hpp
Abstract:
- dynamic storage location for glyphs that can't normally fit in the output buffer
Author(s):
- Austin Diviness (AustDi) 02-May-2018
--*/
#pragma once
#include <vector>
#include <unordered_map>
#include <climits>
// std::unordered_map needs help to know how to hash a COORD
namespace std
{
template<>
struct hash<COORD>
{
// Routine Description:
// - hashes a coord. coord will be hashed by storing the x and y values consecutively in the lower
// bits of a size_t.
// Arguments:
// - coord - the coord to hash
// Return Value:
// - the hashed coord
constexpr size_t operator()(const COORD& coord) const noexcept
{
size_t retVal = coord.Y;
const size_t xCoord = coord.X;
retVal |= xCoord << (sizeof(coord.Y) * CHAR_BIT);
return retVal;
}
};
}
class UnicodeStorage final
{
public:
using key_type = typename COORD;
using mapped_type = typename std::vector<wchar_t>;
UnicodeStorage() noexcept;
const mapped_type& GetText(const key_type key) const;
void StoreGlyph(const key_type key, const mapped_type& glyph);
void Erase(const key_type key) noexcept;
void Remap(const std::unordered_map<SHORT, SHORT>& rowMap, const std::optional<SHORT> width);
private:
std::unordered_map<key_type, mapped_type> _map;
#ifdef UNIT_TESTING
friend class UnicodeStorageTests;
friend class TextBufferTests;
#endif
};

View file

@ -23,18 +23,14 @@
<ClCompile Include="..\textBuffer.cpp" />
<ClCompile Include="..\textBufferCellIterator.cpp" />
<ClCompile Include="..\textBufferTextIterator.cpp" />
<ClCompile Include="..\CharRow.cpp" />
<ClCompile Include="..\CharRowCell.cpp" />
<ClCompile Include="..\precomp.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\UnicodeStorage.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\AttrRow.hpp" />
<ClInclude Include="..\cursor.h" />
<ClInclude Include="..\DbcsAttribute.hpp" />
<ClInclude Include="..\ICharRow.hpp" />
<ClInclude Include="..\LineRendition.hpp" />
<ClInclude Include="..\OutputCell.hpp" />
<ClInclude Include="..\OutputCellIterator.hpp" />
@ -47,10 +43,7 @@
<ClInclude Include="..\textBuffer.hpp" />
<ClInclude Include="..\textBufferCellIterator.hpp" />
<ClInclude Include="..\textBufferTextIterator.hpp" />
<ClInclude Include="..\CharRow.hpp" />
<ClInclude Include="..\CharRowCell.hpp" />
<ClInclude Include="..\precomp.h" />
<ClInclude Include="..\UnicodeStorage.hpp" />
</ItemGroup>
<!-- Careful reordering these. Some default props (contained in these files) are order sensitive. -->
<Import Project="$(SolutionDir)src\common.build.post.props" />

View file

@ -5,7 +5,6 @@
#include "search.h"
#include "CharRow.hpp"
#include "textBuffer.hpp"
#include "../types/inc/Utf16Parser.hpp"
#include "../types/inc/GlyphWidth.hpp"

View file

@ -41,9 +41,6 @@ SOURCES= \
..\textBuffer.cpp \
..\textBufferCellIterator.cpp \
..\textBufferTextIterator.cpp \
..\CharRow.cpp \
..\CharRowCell.cpp \
..\UnicodeStorage.cpp \
..\search.cpp \
INCLUDES= \

View file

@ -4,7 +4,6 @@
#include "precomp.h"
#include "textBuffer.hpp"
#include "CharRow.hpp"
#include "../types/inc/utils.hpp"
#include "../types/inc/convert.hpp"
@ -35,7 +34,6 @@ TextBuffer::TextBuffer(const COORD screenBufferSize,
_currentAttributes{ defaultAttributes },
_cursor{ cursorSize, *this },
_storage{},
_unicodeStorage{},
_renderTarget{ renderTarget },
_size{},
_currentHyperlinkId{ 1 },
@ -419,7 +417,7 @@ COORD TextBuffer::GetLastNonSpaceCharacter(std::optional<const Microsoft::Consol
const auto& currRow = GetRowByOffset(coordEndOfText.Y);
// The X position of the end of the valid text is the Right draw boundary (which is one beyond the final valid character)
coordEndOfText.X = gsl::narrow<short>(currRow.GetCharRow().MeasureRight()) - 1;
coordEndOfText.X = gsl::narrow<short>(currRow.MeasureRight()) - 1;
// If the X coordinate turns out to be -1, the row was empty, we need to search backwards for the real end of text.
const auto viewportTop = viewport.Top();
@ -430,7 +428,7 @@ COORD TextBuffer::GetLastNonSpaceCharacter(std::optional<const Microsoft::Consol
const auto& backupRow = GetRowByOffset(coordEndOfText.Y);
// We need to back up to the previous row if this line is empty, AND there are more rows
coordEndOfText.X = gsl::narrow<short>(backupRow.GetCharRow().MeasureRight()) - 1;
coordEndOfText.X = gsl::narrow<short>(backupRow.MeasureRight()) - 1;
fDoBackUp = (coordEndOfText.X < 0 && coordEndOfText.Y > viewportTop);
}
@ -590,7 +588,6 @@ void TextBuffer::ScrollRows(const SHORT firstRow, const SHORT size, const SHORT
}
// Renumber the IDs now that we've rearranged where the rows sit within the buffer.
// Refreshing should also delegate to the UnicodeStorage to re-key all the stored unicode sequences (where applicable).
_RefreshRowIDs(std::nullopt);
}
@ -743,8 +740,7 @@ void TextBuffer::Reset()
}
// Now that we've tampered with the row placement, refresh all the row IDs.
// Also take advantage of the row ID refresh loop to resize the rows in the X dimension
// and cleanup the UnicodeStorage characters that might fall outside the resized buffer.
// Also take advantage of the row ID refresh loop to resize the rows in the X dimension.
_RefreshRowIDs(newSize.X);
// Update the cached size value
@ -755,40 +751,16 @@ void TextBuffer::Reset()
return S_OK;
}
const UnicodeStorage& TextBuffer::GetUnicodeStorage() const noexcept
{
return _unicodeStorage;
}
UnicodeStorage& TextBuffer::GetUnicodeStorage() noexcept
{
return _unicodeStorage;
}
// Routine Description:
// - Method to help refresh all the Row IDs after manipulating the row
// by shuffling pointers around.
// - This will also update parent pointers that are stored in depth within the buffer
// (e.g. it will update CharRow parents pointing at Rows that might have been moved around)
// - Optionally takes a new row width if we're resizing to perform a resize operation and cleanup
// any high unicode (UnicodeStorage) runs while we're already looping through the rows.
// - Optionally takes a new row width if we're resizing to perform a resize operation
// Arguments:
// - newRowWidth - Optional new value for the row width.
void TextBuffer::_RefreshRowIDs(std::optional<SHORT> newRowWidth)
{
std::unordered_map<SHORT, SHORT> rowMap;
SHORT i = 0;
for (auto& it : _storage)
{
// Build a map so we can update Unicode Storage
rowMap.emplace(it.GetId(), i);
// Update the IDs
it.SetId(i++);
// Also update the char row parent pointers as they can get shuffled up in the rotates.
it.GetCharRow().UpdateParent(&it);
// Resize the rows in the X dimension if we have a new width
if (newRowWidth.has_value())
{
@ -796,9 +768,6 @@ void TextBuffer::_RefreshRowIDs(std::optional<SHORT> newRowWidth)
THROW_IF_FAILED(it.Resize(newRowWidth.value()));
}
}
// Give the new mapping to Unicode Storage
_unicodeStorage.Remap(rowMap, newRowWidth);
}
void TextBuffer::_NotifyPaint(const Viewport& viewport) const
@ -817,27 +786,6 @@ ROW& TextBuffer::_GetFirstRow()
return GetRowByOffset(0);
}
// Routine Description:
// - Retrieves the row that comes before the given row.
// - Does not wrap around the screen buffer.
// Arguments:
// - The current row.
// Return Value:
// - reference to the previous row
// Note:
// - will throw exception if called with the first row of the text buffer
ROW& TextBuffer::_GetPrevRowNoWrap(const ROW& Row)
{
int prevRowIndex = Row.GetId() - 1;
if (prevRowIndex < 0)
{
prevRowIndex = TotalRowCount() - 1;
}
THROW_HR_IF(E_FAIL, Row.GetId() == _firstRow);
return _storage.at(prevRowIndex);
}
// Method Description:
// - Retrieves this buffer's current render target.
// Arguments:
@ -859,7 +807,7 @@ Microsoft::Console::Render::IRenderTarget& TextBuffer::GetRenderTarget() noexcep
// - the delimiter class for the given char
const DelimiterClass TextBuffer::_GetDelimiterClassAt(const COORD pos, const std::wstring_view wordDelimiters) const
{
return GetRowByOffset(pos.Y).GetCharRow().DelimiterClassAt(pos.X, wordDelimiters);
return GetRowByOffset(pos.Y).DelimiterClassAt(pos.X, wordDelimiters);
}
// Method Description:
@ -1951,8 +1899,7 @@ HRESULT TextBuffer::Reflow(TextBuffer& oldBuffer,
// Fetch the row and its "right" which is the last printable character.
const ROW& row = oldBuffer.GetRowByOffset(iOldRow);
const short cOldColsTotal = oldBuffer.GetLineWidth(iOldRow);
const CharRow& charRow = row.GetCharRow();
short iRight = gsl::narrow_cast<short>(charRow.MeasureRight());
short iRight = gsl::narrow_cast<short>(row.MeasureRight());
if (iRight == 0 && !row.WasWrapForced())
{
// don't beef it if iRight=0 on iterator

View file

@ -54,7 +54,6 @@ filling in the last row, and updating the screen.
#include "cursor.h"
#include "Row.hpp"
#include "TextAttribute.hpp"
#include "UnicodeStorage.hpp"
#include "../types/inc/Viewport.hpp"
#include "../buffer/out/textBufferCellIterator.hpp"
@ -134,9 +133,6 @@ public:
[[nodiscard]] HRESULT ResizeTraditional(const COORD newSize) noexcept;
const UnicodeStorage& GetUnicodeStorage() const noexcept;
UnicodeStorage& GetUnicodeStorage() noexcept;
Microsoft::Console::Render::IRenderTarget& GetRenderTarget() noexcept;
const COORD GetWordStart(const COORD target, const std::wstring_view wordDelimiters, bool accessibilityMode = false) const;
@ -208,9 +204,6 @@ private:
TextAttribute _currentAttributes;
// storage location for glyphs that can't fit into the buffer normally
UnicodeStorage _unicodeStorage;
std::unordered_map<uint16_t, std::wstring> _hyperlinkMap;
std::unordered_map<std::wstring, uint16_t> _hyperlinkCustomIdMap;
uint16_t _currentHyperlinkId;
@ -229,7 +222,6 @@ private:
void _NotifyPaint(const Microsoft::Console::Types::Viewport& viewport) const;
ROW& _GetFirstRow();
ROW& _GetPrevRowNoWrap(const ROW& row);
void _ExpandTextRow(SMALL_RECT& selectionRow) const;

View file

@ -5,7 +5,6 @@
#include "textBufferCellIterator.hpp"
#include "CharRow.hpp"
#include "textBuffer.hpp"
#include "../types/inc/convert.hpp"
#include "../types/inc/viewport.hpp"
@ -326,8 +325,8 @@ const ROW* TextBufferCellIterator::s_GetRow(const TextBuffer& buffer, const COOR
// - Updates the internal view. Call after updating row, attribute, or positions.
void TextBufferCellIterator::_GenerateView()
{
_view = OutputCellView(_pRow->GetCharRow().GlyphDataAt(_pos.X),
_pRow->GetCharRow().DbcsAttrAt(_pos.X),
_view = OutputCellView(_pRow->GlyphAt(_pos.X),
_pRow->DbcsAttrAt(_pos.X),
*_attrIter,
TextAttributeBehavior::Stored);
}

View file

@ -15,12 +15,12 @@ Author(s):
#pragma once
#include "CharRow.hpp"
#include "AttrRow.hpp"
#include "OutputCellView.hpp"
#include "../../types/inc/viewport.hpp"
class TextBuffer;
class ROW;
class TextBufferCellIterator
{

View file

@ -5,7 +5,6 @@
#include "textBufferTextIterator.hpp"
#include "CharRow.hpp"
#include "Row.hpp"
#pragma hdrstop

View file

@ -9,8 +9,6 @@
#include "handle.h"
#include "misc.h"
#include "../buffer/out/CharRow.hpp"
#include "../interactivity/inc/ServiceLocator.hpp"
#include "../types/inc/Viewport.hpp"
#include "../types/inc/convert.hpp"

View file

@ -20,6 +20,7 @@
#include "../types/inc/Viewport.hpp"
#include "../interactivity/inc/ServiceLocator.hpp"
#include "../types/inc/Utf16Parser.hpp"
#pragma hdrstop
using namespace Microsoft::Console::Types;
@ -339,7 +340,8 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
COORD CursorPosition = cursor.GetPosition();
NTSTATUS Status = STATUS_SUCCESS;
SHORT XPosition;
WCHAR LocalBuffer[LOCAL_BUFFER_SIZE];
std::wstring local;
local.reserve(LOCAL_BUFFER_SIZE);
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);
@ -351,6 +353,7 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
const size_t BufferSize = *pcb;
*pcb = 0;
const wchar_t* const base = pwchRealUnicode;
const wchar_t* lpString = pwchRealUnicode;
COORD coordScreenBufferSize = screenInfo.GetBufferSize().Dimensions();
@ -387,8 +390,7 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
// As an optimization, collect characters in buffer and print out all at once.
XPosition = cursor.GetPosition().X;
size_t i = 0;
wchar_t* LocalBufPtr = LocalBuffer;
while (*pcb < BufferSize && i < LOCAL_BUFFER_SIZE && XPosition < coordScreenBufferSize.X)
while (*pcb < BufferSize && XPosition < coordScreenBufferSize.X)
{
#pragma prefast(suppress : 26019, "Buffer is taken in multiples of 2. Validation is ok.")
const wchar_t Char = *lpString;
@ -396,18 +398,19 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
// WCL-NOTE: to be identical to lpString. They are incremented in lockstep, never separately, and lpString
// 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) }) };
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.
if (IsGlyphFullWidth(Char))
if (IsGlyphFullWidth(cbuf))
{
if (i < (LOCAL_BUFFER_SIZE - 1) && XPosition < (coordScreenBufferSize.X - 1))
{
*LocalBufPtr++ = Char;
local.append(cbuf);
// cursor adjusted by 2 because the char is double width
XPosition += 2;
i += 1;
i += cbuf.size();
pwchBuffer++;
}
else
@ -417,8 +420,7 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
}
else
{
*LocalBufPtr = Char;
LocalBufPtr++;
local.append(cbuf);
XPosition++;
i++;
pwchBuffer++;
@ -461,8 +463,7 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
for (ULONG j = 0; j < TabSize && i < LOCAL_BUFFER_SIZE; j++, i++)
{
*LocalBufPtr = UNICODE_SPACE;
LocalBufPtr++;
local.append(1, UNICODE_SPACE);
}
pwchBuffer++;
@ -481,13 +482,11 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
{
// WCL-NOTE: We do not properly measure that there is space for two characters
// WCL-NOTE: left on the screen.
*LocalBufPtr = (WCHAR)'^';
LocalBufPtr++;
local.append(1, L'^');
XPosition++;
i++;
*LocalBufPtr = (WCHAR)(RealUnicodeChar + (WCHAR)'@');
LocalBufPtr++;
local.append(1, (WCHAR)(RealUnicodeChar + (WCHAR)'@'));
XPosition++;
i++;
@ -502,7 +501,7 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
{
if (Char == UNICODE_NULL)
{
*LocalBufPtr = UNICODE_SPACE;
local.append(1, UNICODE_SPACE);
}
else
{
@ -513,11 +512,13 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
GetStringTypeW(CT_CTYPE1, &RealUnicodeChar, 1, &CharType);
if (WI_IsFlagSet(CharType, C1_CNTRL))
{
WCHAR ch{};
ConvertOutputToUnicode(gci.OutputCP,
(LPSTR)&RealUnicodeChar,
1,
LocalBufPtr,
&ch,
1);
local.append(1, ch);
}
else
{
@ -525,19 +526,18 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
// WCL-NOTE: 1. Normal characters are handled via the early check for IS_GLYPH_CHAR
// WCL-NOTE: 2. Control characters are handled via the CtrlChar label (if WC_PRINTABLE_CONTROL_CHARS is on)
// WCL-NOTE: And if they are control characters they will trigger the C1_CNTRL check above.
*LocalBufPtr = Char;
local.append(1, Char);
}
}
LocalBufPtr++;
XPosition++;
i++;
pwchBuffer++;
}
}
}
lpString++;
pwchRealUnicode++;
lpString += cbuf.size();
pwchRealUnicode += cbuf.size();
*pcb += sizeof(WCHAR);
}
EndWhile:
@ -553,7 +553,7 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
}
// line was wrapped if we're writing up to the end of the current row
OutputCellIterator it(std::wstring_view(LocalBuffer, i), Attributes);
OutputCellIterator it(std::wstring_view{ local }, Attributes);
const auto itEnd = screenInfo.Write(it);
// Notify accessibility
@ -568,7 +568,8 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
// 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).
CursorPosition.X = XPosition;
CursorPosition.X += (SHORT)TempNumSpaces;
//XPosition;
// enforce a delayed newline if we're about to pass the end and the WC_DELAY_EOL_WRAP flag is set.
if (WI_IsFlagSet(dwFlags, WC_DELAY_EOL_WRAP) && CursorPosition.X >= coordScreenBufferSize.X && fWrapAtEOL)
@ -850,12 +851,11 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
{
const COORD TargetPoint = cursor.GetPosition();
ROW& Row = textBuffer.GetRowByOffset(TargetPoint.Y);
const CharRow& charRow = Row.GetCharRow();
try
{
// If we're on top of a trailing cell, clear it and the previous cell.
if (charRow.DbcsAttrAt(TargetPoint.X).IsTrailing())
if (Row.DbcsAttrAt(TargetPoint.X).IsTrailing())
{
// Space to clear for 2 cells.
OutputCellIterator it(UNICODE_SPACE, 2);

View file

@ -9,7 +9,6 @@
#include "_output.h"
#include "misc.h"
#include "handle.h"
#include "../buffer/out/CharRow.hpp"
#include <cmath>
#include "../interactivity/inc/ServiceLocator.hpp"

View file

@ -23,7 +23,6 @@ Author(s):
#include "thread.hpp"
#include "../../buffer/out/textBuffer.hpp"
#include "../../buffer/out/CharRow.hpp"
namespace Microsoft::Console::Render
{