HAX: rle-based row
This commit is contained in:
parent
227ce8ff20
commit
00af538278
|
@ -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);
|
||||
}
|
|
@ -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 };
|
||||
});
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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...
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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
|
||||
};
|
|
@ -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" />
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
|
||||
#include "search.h"
|
||||
|
||||
#include "CharRow.hpp"
|
||||
#include "textBuffer.hpp"
|
||||
#include "../types/inc/Utf16Parser.hpp"
|
||||
#include "../types/inc/GlyphWidth.hpp"
|
||||
|
|
|
@ -41,9 +41,6 @@ SOURCES= \
|
|||
..\textBuffer.cpp \
|
||||
..\textBufferCellIterator.cpp \
|
||||
..\textBufferTextIterator.cpp \
|
||||
..\CharRow.cpp \
|
||||
..\CharRowCell.cpp \
|
||||
..\UnicodeStorage.cpp \
|
||||
..\search.cpp \
|
||||
|
||||
INCLUDES= \
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
|
||||
#include "textBufferTextIterator.hpp"
|
||||
|
||||
#include "CharRow.hpp"
|
||||
#include "Row.hpp"
|
||||
|
||||
#pragma hdrstop
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -23,7 +23,6 @@ Author(s):
|
|||
#include "thread.hpp"
|
||||
|
||||
#include "../../buffer/out/textBuffer.hpp"
|
||||
#include "../../buffer/out/CharRow.hpp"
|
||||
|
||||
namespace Microsoft::Console::Render
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue