Compare commits

...

1 Commits

Author SHA1 Message Date
Leonard Hecker
8cfbae30b0 wip 2024-08-08 19:01:48 +02:00
23 changed files with 305 additions and 689 deletions

View File

@@ -1116,7 +1116,6 @@ msrc
MSVCRTD
MTSM
Munged
munges
murmurhash
muxes
myapplet

View File

@@ -5,7 +5,6 @@
#include "OutputCell.hpp"
#include "../../types/inc/GlyphWidth.hpp"
#include "../../types/inc/convert.hpp"
#include "../../inc/conattrs.hpp"

View File

@@ -8,27 +8,10 @@
#include <til/unicode.h>
#include "../../types/inc/convert.hpp"
#include "../../types/inc/GlyphWidth.hpp"
#include "../../inc/conattrs.hpp"
static constexpr TextAttribute InvalidTextAttribute{ INVALID_COLOR, INVALID_COLOR, INVALID_COLOR };
// Routine Description:
// - This is a fill-mode iterator for one particular wchar. It will repeat forever if fillLimit is 0.
// Arguments:
// - wch - The character to use for filling
// - fillLimit - How many times to allow this value to be viewed/filled. Infinite if 0.
OutputCellIterator::OutputCellIterator(const wchar_t& wch, const size_t fillLimit) noexcept :
_mode(Mode::Fill),
_currentView(s_GenerateView(wch)),
_run(),
_attr(InvalidTextAttribute),
_pos(0),
_distance(0),
_fillLimit(fillLimit)
{
}
// Routine Description:
// - This is a fill-mode iterator for one particular color. It will repeat forever if fillLimit is 0.
// Arguments:
@@ -45,70 +28,6 @@ OutputCellIterator::OutputCellIterator(const TextAttribute& attr, const size_t f
{
}
// Routine Description:
// - This is a fill-mode iterator for one particular character and color. It will repeat forever if fillLimit is 0.
// Arguments:
// - wch - The character to use for filling
// - attr - The color attribute to use for filling
// - fillLimit - How many times to allow this value to be viewed/filled. Infinite if 0.
OutputCellIterator::OutputCellIterator(const wchar_t& wch, const TextAttribute& attr, const size_t fillLimit) noexcept :
_mode(Mode::Fill),
_currentView(s_GenerateView(wch, attr)),
_run(),
_attr(InvalidTextAttribute),
_pos(0),
_distance(0),
_fillLimit(fillLimit)
{
}
// Routine Description:
// - This is a fill-mode iterator for one particular CHAR_INFO. It will repeat forever if fillLimit is 0.
// Arguments:
// - charInfo - The legacy character and color data to use for filling (uses Unicode portion of text data)
// - fillLimit - How many times to allow this value to be viewed/filled. Infinite if 0.
OutputCellIterator::OutputCellIterator(const CHAR_INFO& charInfo, const size_t fillLimit) noexcept :
_mode(Mode::Fill),
_currentView(s_GenerateView(charInfo)),
_run(),
_attr(InvalidTextAttribute),
_pos(0),
_distance(0),
_fillLimit(fillLimit)
{
}
// Routine Description:
// - This is an iterator over a range of text only. No color data will be modified as the text is inserted.
// Arguments:
// - utf16Text - UTF-16 text range
OutputCellIterator::OutputCellIterator(const std::wstring_view utf16Text) noexcept :
_mode(Mode::LooseTextOnly),
_currentView(s_GenerateView(utf16Text)),
_run(utf16Text),
_attr(InvalidTextAttribute),
_pos(0),
_distance(0),
_fillLimit(0)
{
}
// Routine Description:
// - This is an iterator over a range text that will apply the same color to every position.
// Arguments:
// - utf16Text - UTF-16 text range
// - attribute - Color to apply over the entire range
OutputCellIterator::OutputCellIterator(const std::wstring_view utf16Text, const TextAttribute& attribute, const size_t fillLimit) noexcept :
_mode(Mode::Loose),
_currentView(s_GenerateView(utf16Text, attribute)),
_run(utf16Text),
_attr(attribute),
_distance(0),
_pos(0),
_fillLimit(fillLimit)
{
}
// Routine Description:
// - This is an iterator over legacy colors only. The text is not modified.
// Arguments:
@@ -124,21 +43,6 @@ OutputCellIterator::OutputCellIterator(const std::span<const WORD> legacyAttrs)
{
}
// Routine Description:
// - This is an iterator over legacy cell data. We will use the unicode text and the legacy color attribute.
// Arguments:
// - charInfos - Multiple cell with unicode text and legacy color data.
OutputCellIterator::OutputCellIterator(const std::span<const CHAR_INFO> charInfos) noexcept :
_mode(Mode::CharInfo),
_currentView(s_GenerateView(til::at(charInfos, 0))),
_run(charInfos),
_attr(InvalidTextAttribute),
_distance(0),
_pos(0),
_fillLimit(0)
{
}
// Routine Description:
// - This is an iterator over existing OutputCells with full text and color data.
// Arguments:
@@ -164,13 +68,6 @@ OutputCellIterator::operator bool() const noexcept
{
switch (_mode)
{
case Mode::Loose:
case Mode::LooseTextOnly:
{
// In lieu of using start and end, this custom iterator type simply becomes bool false
// when we run out of items to iterate over.
return _pos < std::get<std::wstring_view>(_run).length();
}
case Mode::Fill:
{
if (_fillLimit > 0)
@@ -183,10 +80,6 @@ OutputCellIterator::operator bool() const noexcept
{
return _pos < std::get<std::span<const OutputCell>>(_run).size();
}
case Mode::CharInfo:
{
return _pos < std::get<std::span<const CHAR_INFO>>(_run).size();
}
case Mode::LegacyAttr:
{
return _pos < std::get<std::span<const WORD>>(_run).size();
@@ -214,34 +107,6 @@ OutputCellIterator& OutputCellIterator::operator++()
switch (_mode)
{
case Mode::Loose:
{
if (!_TryMoveTrailing())
{
// When walking through a text sequence, we need to move forward by the number of wchar_ts consumed in the previous view
// in case we had a surrogate pair (or wider complex sequence) in the previous view.
_pos += _currentView.Chars().size();
if (operator bool())
{
_currentView = s_GenerateView(std::get<std::wstring_view>(_run).substr(_pos), _attr);
}
}
break;
}
case Mode::LooseTextOnly:
{
if (!_TryMoveTrailing())
{
// When walking through a text sequence, we need to move forward by the number of wchar_ts consumed in the previous view
// in case we had a surrogate pair (or wider complex sequence) in the previous view.
_pos += _currentView.Chars().size();
if (operator bool())
{
_currentView = s_GenerateView(std::get<std::wstring_view>(_run).substr(_pos));
}
}
break;
}
case Mode::Fill:
{
if (!_TryMoveTrailing())
@@ -272,16 +137,6 @@ OutputCellIterator& OutputCellIterator::operator++()
}
break;
}
case Mode::CharInfo:
{
// Walk forward by one because charinfos are just the legacy version of cells and prealigned to columns
_pos++;
if (operator bool())
{
_currentView = s_GenerateView(til::at(std::get<std::span<const CHAR_INFO>>(_run), _pos));
}
break;
}
case Mode::LegacyAttr:
{
// Walk forward by one because color attributes apply cell by cell (no complex text information)
@@ -353,69 +208,6 @@ bool OutputCellIterator::_TryMoveTrailing() noexcept
}
}
// Routine Description:
// - Static function to create a view.
// - It's pulled out statically so it can be used during construction with just the given
// variables (so OutputCellView doesn't need an empty default constructor)
// - This will infer the width of the glyph and specify that the attributes shouldn't be changed.
// Arguments:
// - view - View representing characters corresponding to a single glyph
// Return Value:
// - Object representing the view into this cell
OutputCellView OutputCellIterator::s_GenerateView(const std::wstring_view view) noexcept
{
return s_GenerateView(view, InvalidTextAttribute, TextAttributeBehavior::Current);
}
// Routine Description:
// - Static function to create a view.
// - It's pulled out statically so it can be used during construction with just the given
// variables (so OutputCellView doesn't need an empty default constructor)
// - This will infer the width of the glyph and apply the appropriate attributes to the view.
// Arguments:
// - view - View representing characters corresponding to a single glyph
// - attr - Color attributes to apply to the text
// Return Value:
// - Object representing the view into this cell
OutputCellView OutputCellIterator::s_GenerateView(const std::wstring_view view, const TextAttribute attr) noexcept
{
return s_GenerateView(view, attr, TextAttributeBehavior::Stored);
}
// Routine Description:
// - Static function to create a view.
// - It's pulled out statically so it can be used during construction with just the given
// variables (so OutputCellView doesn't need an empty default constructor)
// - This will infer the width of the glyph and apply the appropriate attributes to the view.
// Arguments:
// - view - View representing characters corresponding to a single glyph
// - attr - Color attributes to apply to the text
// - behavior - Behavior of the given text attribute (used when writing)
// Return Value:
// - Object representing the view into this cell
OutputCellView OutputCellIterator::s_GenerateView(const std::wstring_view view, const TextAttribute attr, const TextAttributeBehavior behavior) noexcept
{
const auto glyph = til::utf16_next(view);
const auto dbcsAttr = IsGlyphFullWidth(glyph) ? DbcsAttribute::Leading : DbcsAttribute::Single;
return OutputCellView(glyph, dbcsAttr, attr, behavior);
}
// Routine Description:
// - Static function to create a view.
// - It's pulled out statically so it can be used during construction with just the given
// variables (so OutputCellView doesn't need an empty default constructor)
// - This will infer the width of the glyph and apply the appropriate attributes to the view.
// Arguments:
// - wch - View representing a single UTF-16 character (that can be represented without surrogates)
// Return Value:
// - Object representing the view into this cell
OutputCellView OutputCellIterator::s_GenerateView(const wchar_t& wch) noexcept
{
const auto glyph = std::wstring_view(&wch, 1);
const auto dbcsAttr = IsGlyphFullWidth(wch) ? DbcsAttribute::Leading : DbcsAttribute::Single;
return OutputCellView(glyph, dbcsAttr, InvalidTextAttribute, TextAttributeBehavior::Current);
}
// Routine Description:
// - Static function to create a view.
// - It's pulled out statically so it can be used during construction with just the given
@@ -430,23 +222,6 @@ OutputCellView OutputCellIterator::s_GenerateView(const TextAttribute& attr) noe
return OutputCellView({}, {}, attr, TextAttributeBehavior::StoredOnly);
}
// Routine Description:
// - Static function to create a view.
// - It's pulled out statically so it can be used during construction with just the given
// variables (so OutputCellView doesn't need an empty default constructor)
// - This will infer the width of the glyph and apply the appropriate attributes to the view.
// Arguments:
// - wch - View representing a single UTF-16 character (that can be represented without surrogates)
// - attr - View representing a single color
// Return Value:
// - Object representing the view into this cell
OutputCellView OutputCellIterator::s_GenerateView(const wchar_t& wch, const TextAttribute& attr) noexcept
{
const auto glyph = std::wstring_view(&wch, 1);
const auto dbcsAttr = IsGlyphFullWidth(wch) ? DbcsAttribute::Leading : DbcsAttribute::Single;
return OutputCellView(glyph, dbcsAttr, attr, TextAttributeBehavior::Stored);
}
// Routine Description:
// - Static function to create a view.
// - It's pulled out statically so it can be used during construction with just the given
@@ -465,35 +240,6 @@ OutputCellView OutputCellIterator::s_GenerateViewLegacyAttr(const WORD& legacyAt
return s_GenerateView(attr);
}
// Routine Description:
// - Static function to create a view.
// - It's pulled out statically so it can be used during construction with just the given
// variables (so OutputCellView doesn't need an empty default constructor)
// - This will infer the width of the glyph and apply the appropriate attributes to the view.
// Arguments:
// - charInfo - character and attribute pair representing a single cell
// Return Value:
// - Object representing the view into this cell
OutputCellView OutputCellIterator::s_GenerateView(const CHAR_INFO& charInfo) noexcept
{
const auto glyph = std::wstring_view(&charInfo.Char.UnicodeChar, 1);
DbcsAttribute dbcsAttr = DbcsAttribute::Single;
if (WI_IsFlagSet(charInfo.Attributes, COMMON_LVB_LEADING_BYTE))
{
dbcsAttr = DbcsAttribute::Leading;
}
else if (WI_IsFlagSet(charInfo.Attributes, COMMON_LVB_TRAILING_BYTE))
{
dbcsAttr = DbcsAttribute::Trailing;
}
const TextAttribute textAttr(charInfo.Attributes);
const auto behavior = TextAttributeBehavior::Stored;
return OutputCellView(glyph, dbcsAttr, textAttr, behavior);
}
// Routine Description:
// - Static function to create a view.
// - It's pulled out statically so it can be used during construction with just the given

View File

@@ -34,14 +34,8 @@ public:
using reference = OutputCellView&;
OutputCellIterator() = default;
OutputCellIterator(const wchar_t& wch, const size_t fillLimit = 0) noexcept;
OutputCellIterator(const TextAttribute& attr, const size_t fillLimit = 0) noexcept;
OutputCellIterator(const wchar_t& wch, const TextAttribute& attr, const size_t fillLimit = 0) noexcept;
OutputCellIterator(const CHAR_INFO& charInfo, const size_t fillLimit = 0) noexcept;
OutputCellIterator(const std::wstring_view utf16Text) noexcept;
OutputCellIterator(const std::wstring_view utf16Text, const TextAttribute& attribute, const size_t fillLimit = 0) noexcept;
OutputCellIterator(const std::span<const WORD> legacyAttributes) noexcept;
OutputCellIterator(const std::span<const CHAR_INFO> charInfos) noexcept;
OutputCellIterator(const std::span<const OutputCell> cells);
~OutputCellIterator() = default;
@@ -63,14 +57,6 @@ public:
private:
enum class Mode
{
// Loose mode is where we're given text and attributes in a raw sort of form
// like while data is being inserted from an API call.
Loose,
// Loose mode with only text is where we're given just text and we want
// to use the attribute already in the buffer when writing
LooseTextOnly,
// Fill mode is where we were given one thing and we just need to keep giving
// that back over and over for eternity.
Fill,
@@ -78,10 +64,6 @@ private:
// Given a run of legacy attributes, convert each of them and insert only attribute data.
LegacyAttr,
// CharInfo mode is where we've been given a pair of text and attribute for each
// cell in the legacy format from an API call.
CharInfo,
// Cell mode is where we have an already fully structured cell data usually
// from accessing/copying data already put into the OutputBuffer.
Cell,
@@ -91,9 +73,7 @@ private:
std::span<const WORD> _legacyAttrs;
std::variant<
std::wstring_view,
std::span<const WORD>,
std::span<const CHAR_INFO>,
std::span<const OutputCell>,
std::monostate>
_run;
@@ -102,14 +82,10 @@ private:
bool _TryMoveTrailing() noexcept;
static OutputCellView s_GenerateView(const std::wstring_view view) noexcept;
static OutputCellView s_GenerateView(const std::wstring_view view, const TextAttribute attr) noexcept;
static OutputCellView s_GenerateView(const std::wstring_view view, const TextAttribute attr, const TextAttributeBehavior behavior) noexcept;
static OutputCellView s_GenerateView(const wchar_t& wch) noexcept;
static OutputCellView s_GenerateViewLegacyAttr(const WORD& legacyAttr) noexcept;
static OutputCellView s_GenerateView(const TextAttribute& attr) noexcept;
static OutputCellView s_GenerateView(const wchar_t& wch, const TextAttribute& attr) noexcept;
static OutputCellView s_GenerateView(const CHAR_INFO& charInfo) noexcept;
static OutputCellView s_GenerateView(const OutputCell& cell);

View File

@@ -374,22 +374,30 @@ TextBufferCellIterator TextBuffer::GetCellDataAt(const til::point at, const View
// Given the character offset `position` in the `chars` string, this function returns the starting position of the next grapheme.
// For instance, given a `chars` of L"x\uD83D\uDE42y" and a `position` of 1 it'll return 3.
// GraphemePrev would do the exact inverse of this operation.
size_t TextBuffer::GraphemeNext(const std::wstring_view& chars, size_t position) noexcept
size_t TextBuffer::GraphemeNext(const std::wstring_view& chars, size_t position, til::CoordType* width) noexcept
{
auto& cwd = CodepointWidthDetector::Singleton();
#pragma warning(suppress : 26481) // Don't use pointer arithmetic. Use span instead (bounds.1).
GraphemeState state{ .beg = chars.data() + position };
cwd.GraphemeNext(state, chars);
if (width)
{
*width = state.width;
}
return position + state.len;
}
// It's the counterpart to GraphemeNext. See GraphemeNext.
size_t TextBuffer::GraphemePrev(const std::wstring_view& chars, size_t position) noexcept
size_t TextBuffer::GraphemePrev(const std::wstring_view& chars, size_t position, til::CoordType* width) noexcept
{
auto& cwd = CodepointWidthDetector::Singleton();
#pragma warning(suppress : 26481) // Don't use pointer arithmetic. Use span instead (bounds.1).
GraphemeState state{ .beg = chars.data() + position };
cwd.GraphemePrev(state, chars);
if (width)
{
*width = state.width;
}
return position - state.len;
}

View File

@@ -99,8 +99,8 @@ public:
size_t GetCellDistance(const til::point from, const til::point to) const;
static size_t GraphemeNext(const std::wstring_view& chars, size_t position) noexcept;
static size_t GraphemePrev(const std::wstring_view& chars, size_t position) noexcept;
static size_t GraphemeNext(const std::wstring_view& chars, size_t position, til::CoordType* width = nullptr) noexcept;
static size_t GraphemePrev(const std::wstring_view& chars, size_t position, til::CoordType* width = nullptr) noexcept;
static size_t FitTextIntoColumns(const std::wstring_view& chars, til::CoordType columnLimit, til::CoordType& columns) noexcept;
til::point NavigateCursor(til::point position, til::CoordType distance) const;

View File

@@ -7,7 +7,6 @@
#include "../textBuffer.hpp"
#include "../../renderer/inc/DummyRenderer.hpp"
#include "../../types/inc/GlyphWidth.hpp"
#include <IDataSource.h>

View File

@@ -7,7 +7,6 @@
#include <unicode.hpp>
#include <Utils.h>
#include <LibraryResources.h>
#include "../../types/inc/GlyphWidth.hpp"
#include "../../types/inc/Utils.hpp"
#include "../../buffer/out/search.h"

View File

@@ -12,7 +12,6 @@
#include "../../terminal/parser/StateMachine.hpp"
#include "../../terminal/input/terminalInput.hpp"
#include "../../types/inc/Viewport.hpp"
#include "../../types/inc/GlyphWidth.hpp"
#include "../../cascadia/terminalcore/ITerminalInput.hpp"
#include <til/ticket_lock.h>

View File

@@ -712,68 +712,3 @@ void VtIo::Writer::WriteAttributes(const TextAttribute& attributes) const
{
FormatAttributes(_io->_back, attributes);
}
void VtIo::Writer::WriteInfos(til::point target, std::span<const CHAR_INFO> infos) const
{
const auto beg = infos.begin();
const auto end = infos.end();
const auto last = end - 1;
WORD attributes = 0xffff;
WriteCUP(target);
for (auto it = beg; it != end; ++it)
{
const auto& ci = *it;
auto ch = ci.Char.UnicodeChar;
auto wide = WI_IsAnyFlagSet(ci.Attributes, COMMON_LVB_LEADING_BYTE | COMMON_LVB_TRAILING_BYTE);
if (wide)
{
if (WI_IsAnyFlagSet(ci.Attributes, COMMON_LVB_LEADING_BYTE))
{
if (it == last)
{
// The leading half of a wide glyph won't fit into the last remaining column.
// --> Replace it with a space.
ch = L' ';
wide = false;
}
}
else
{
if (it == beg)
{
// The trailing half of a wide glyph won't fit into the first column. It's incomplete.
// --> Replace it with a space.
ch = L' ';
wide = false;
}
else
{
// Trailing halves of glyphs are ignored within the run. We only emit the leading half.
continue;
}
}
}
if (attributes != ci.Attributes)
{
attributes = ci.Attributes;
WriteAttributes(TextAttribute{ attributes });
}
int repeat = 1;
if (wide && (til::is_surrogate(ch) || IsControlCharacter(ch)))
{
// Control characters, U+FFFD, etc. are narrow characters, so if the caller
// asked for a wide glyph we need to repeat the replacement character twice.
repeat++;
}
do
{
WriteUCS2StripControlChars(ch);
} while (--repeat);
}
}

View File

@@ -44,7 +44,6 @@ namespace Microsoft::Console::VirtualTerminal
void WriteWindowVisibility(bool visible) const;
void WriteWindowTitle(std::wstring_view title) const;
void WriteAttributes(const TextAttribute& attributes) const;
void WriteInfos(til::point target, std::span<const CHAR_INFO> infos) const;
private:
VtIo* _io = nullptr;

View File

@@ -12,9 +12,10 @@
#include "../interactivity/inc/ServiceLocator.hpp"
#include "../types/inc/convert.hpp"
#include "../types/inc/GlyphWidth.hpp"
#include "../types/inc/Viewport.hpp"
#include <til/unicode.h>
using namespace Microsoft::Console::Types;
using Microsoft::Console::Interactivity::ServiceLocator;
@@ -76,171 +77,166 @@ static FillConsoleResult FillConsoleImpl(SCREEN_INFORMATION& screenInfo, FillCon
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
auto& screenBuffer = screenInfo.GetActiveBuffer();
auto& textBuffer = screenBuffer.GetTextBuffer();
const auto bufferSize = screenBuffer.GetBufferSize();
FillConsoleResult result;
if (!bufferSize.IsInBounds(startingCoordinate))
{
return {};
}
if (auto writer = gci.GetVtWriterForBuffer(&screenInfo))
auto writer = gci.GetVtWriterForBuffer(&screenInfo);
if (writer)
{
writer.BackupCursor();
const auto h = bufferSize.Height();
const auto w = bufferSize.Width();
auto y = startingCoordinate.y;
auto input = static_cast<const uint16_t*>(data);
size_t inputPos = 0;
til::small_vector<CHAR_INFO, 1024> infoBuffer;
Viewport unused;
infoBuffer.resize(gsl::narrow_cast<size_t>(w));
while (y < h && inputPos < lengthToWrite)
{
const auto beg = y == startingCoordinate.y ? startingCoordinate.x : 0;
const auto columnsAvailable = w - beg;
til::CoordType columns = 0;
const auto readViewport = Viewport::FromInclusive({ beg, y, w - 1, y });
THROW_IF_FAILED(ReadConsoleOutputWImplHelper(screenInfo, infoBuffer, readViewport, unused));
switch (mode)
{
case FillConsoleMode::WriteAttribute:
for (; columns < columnsAvailable && inputPos < lengthToWrite; ++columns, ++inputPos)
{
infoBuffer[columns].Attributes = input[inputPos];
}
break;
case FillConsoleMode::FillAttribute:
for (const auto attr = input[0]; columns < columnsAvailable && inputPos < lengthToWrite; ++columns, ++inputPos)
{
infoBuffer[columns].Attributes = attr;
}
break;
case FillConsoleMode::WriteCharacter:
for (; columns < columnsAvailable && inputPos < lengthToWrite; ++inputPos)
{
const auto ch = input[inputPos];
if (ch >= 0x80 && IsGlyphFullWidth(ch))
{
// If the wide glyph doesn't fit into the last column, pad it with whitespace.
if ((columns + 1) >= columnsAvailable)
{
auto& lead = infoBuffer[columns++];
lead.Char.UnicodeChar = L' ';
lead.Attributes = lead.Attributes & ~(COMMON_LVB_LEADING_BYTE | COMMON_LVB_TRAILING_BYTE);
break;
}
auto& lead = infoBuffer[columns++];
lead.Char.UnicodeChar = ch;
lead.Attributes = lead.Attributes & ~COMMON_LVB_TRAILING_BYTE | COMMON_LVB_LEADING_BYTE;
auto& trail = infoBuffer[columns++];
trail.Char.UnicodeChar = ch;
trail.Attributes = trail.Attributes & ~COMMON_LVB_LEADING_BYTE | COMMON_LVB_TRAILING_BYTE;
}
else
{
auto& lead = infoBuffer[columns++];
lead.Char.UnicodeChar = ch;
lead.Attributes = lead.Attributes & ~(COMMON_LVB_LEADING_BYTE | COMMON_LVB_TRAILING_BYTE);
}
}
break;
case FillConsoleMode::FillCharacter:
// Identical to WriteCharacter above, but with the if() and for() swapped.
if (const auto ch = input[0]; ch >= 0x80 && IsGlyphFullWidth(ch))
{
for (; columns < columnsAvailable && inputPos < lengthToWrite; ++inputPos)
{
// If the wide glyph doesn't fit into the last column, pad it with whitespace.
if ((columns + 1) >= columnsAvailable)
{
auto& lead = infoBuffer[columns++];
lead.Char.UnicodeChar = L' ';
lead.Attributes = lead.Attributes & ~(COMMON_LVB_LEADING_BYTE | COMMON_LVB_TRAILING_BYTE);
break;
}
auto& lead = infoBuffer[columns++];
lead.Char.UnicodeChar = ch;
lead.Attributes = lead.Attributes & ~COMMON_LVB_TRAILING_BYTE | COMMON_LVB_LEADING_BYTE;
auto& trail = infoBuffer[columns++];
trail.Char.UnicodeChar = ch;
trail.Attributes = trail.Attributes & ~COMMON_LVB_LEADING_BYTE | COMMON_LVB_TRAILING_BYTE;
}
}
else
{
for (; columns < columnsAvailable && inputPos < lengthToWrite; ++inputPos)
{
auto& lead = infoBuffer[columns++];
lead.Char.UnicodeChar = ch;
lead.Attributes = lead.Attributes & ~(COMMON_LVB_LEADING_BYTE | COMMON_LVB_TRAILING_BYTE);
}
}
break;
}
const auto writeViewport = Viewport::FromInclusive({ beg, y, beg + columns - 1, y });
THROW_IF_FAILED(WriteConsoleOutputWImplHelper(screenInfo, infoBuffer, w, writeViewport, unused));
y += 1;
result.cellsModified += columns;
}
result.lengthRead = inputPos;
writer.Submit();
}
else
{
// Technically we could always pass `data` as `uint16_t*`, because `wchar_t` is guaranteed to be 16 bits large.
// However, OutputCellIterator is terrifyingly unsafe code and so we don't do that.
//
// Constructing an OutputCellIterator with a `wchar_t` takes the `wchar_t` by reference, so that it can reference
// it in a `wstring_view` forever. That's of course really bad because passing a `const uint16_t&` to a
// `const wchar_t&` argument implicitly converts the types. To do so, the implicit conversion allocates a
// `wchar_t` value on the stack. The lifetime of that copy DOES NOT get extended beyond the constructor call.
// The result is that OutputCellIterator would read random data from the stack.
//
// Don't ever assume the lifetime of implicitly convertible types given by reference.
// Ironically that's a bug that cannot happen with C pointers. To no ones surprise, C keeps on winning.
auto attrs = static_cast<const uint16_t*>(data);
auto chars = static_cast<const wchar_t*>(data);
OutputCellIterator it;
FillConsoleResult result;
const auto h = bufferSize.Height();
const auto w = bufferSize.Width();
auto y = startingCoordinate.y;
auto input = static_cast<const uint16_t*>(data);
const std::wstring_view inputText{ (const wchar_t*)input, lengthToWrite };
size_t inputPos = 0;
til::small_vector<CHAR_INFO, 1024> infoBuffer;
Viewport unused;
infoBuffer.resize(gsl::narrow_cast<size_t>(w));
auto fillCharacter = UNICODE_REPLACEMENT;
til::CoordType fillCharacterWidth = 1;
if (mode == FillConsoleMode::FillCharacter)
{
wchar_t chars[3] = { L'a', inputText[0], L'a' };
std::wstring_view foobar{ &chars[0], 3 };
til::CoordType width = 1;
const auto stop1 = textBuffer.GraphemeNext(foobar, 0, nullptr);
const auto stop2 = textBuffer.GraphemeNext(foobar, stop1, &width);
if (stop1 == 1 && stop2 == 2 && !til::is_surrogate(foobar[1]))
{
fillCharacter = foobar[1];
fillCharacterWidth = width;
}
}
while (y < h && inputPos < lengthToWrite)
{
const auto beg = y == startingCoordinate.y ? startingCoordinate.x : 0;
const auto columnsAvailable = w - beg;
til::CoordType columns = 0;
const auto readViewport = Viewport::FromInclusive({ beg, y, w - 1, y });
THROW_IF_FAILED(ReadConsoleOutputWImplHelper(screenInfo, infoBuffer, readViewport, unused));
switch (mode)
{
case FillConsoleMode::WriteAttribute:
it = OutputCellIterator({ attrs, lengthToWrite });
break;
case FillConsoleMode::WriteCharacter:
it = OutputCellIterator({ chars, lengthToWrite });
for (; columns < columnsAvailable && inputPos < lengthToWrite; ++columns, ++inputPos)
{
infoBuffer[columns].Attributes = input[inputPos];
}
break;
case FillConsoleMode::FillAttribute:
it = OutputCellIterator(TextAttribute(*attrs), lengthToWrite);
for (const auto attr = input[0]; columns < columnsAvailable && inputPos < lengthToWrite; ++columns, ++inputPos)
{
infoBuffer[columns].Attributes = attr;
}
break;
case FillConsoleMode::WriteCharacter:
for (; columns < columnsAvailable && inputPos < lengthToWrite; ++inputPos)
{
auto ch = input[inputPos];
auto next = inputPos + 1;
til::CoordType width = 1;
if (ch >= 0x80)
{
next = textBuffer.GraphemeNext(inputText, inputPos, &width);
if (next - inputPos != 1 || til::is_surrogate(ch))
{
ch = UNICODE_REPLACEMENT;
}
}
if (width > 1)
{
const auto columnEnd = columns + width;
// If the wide glyph doesn't fit into the last column, pad it with whitespace.
if (columnEnd > columnsAvailable)
{
auto& lead = infoBuffer[columns++];
lead.Char.UnicodeChar = L' ';
lead.Attributes = lead.Attributes & ~(COMMON_LVB_LEADING_BYTE | COMMON_LVB_TRAILING_BYTE);
break;
}
auto& lead = infoBuffer[columns++];
lead.Char.UnicodeChar = ch;
lead.Attributes = lead.Attributes & ~COMMON_LVB_TRAILING_BYTE | COMMON_LVB_LEADING_BYTE;
auto& trail = infoBuffer[columns++];
trail.Char.UnicodeChar = ch;
trail.Attributes = trail.Attributes & ~COMMON_LVB_LEADING_BYTE | COMMON_LVB_TRAILING_BYTE;
}
else
{
auto& lead = infoBuffer[columns++];
lead.Char.UnicodeChar = ch;
lead.Attributes = lead.Attributes & ~(COMMON_LVB_LEADING_BYTE | COMMON_LVB_TRAILING_BYTE);
}
}
break;
case FillConsoleMode::FillCharacter:
it = OutputCellIterator(*chars, lengthToWrite);
{
// Identical to WriteCharacter above, but with the if() and for() swapped.
if (fillCharacterWidth > 1)
{
for (; columns < columnsAvailable && inputPos < lengthToWrite; ++inputPos)
{
// If the wide glyph doesn't fit into the last column, pad it with whitespace.
if ((columns + fillCharacterWidth) > columnsAvailable)
{
auto& lead = infoBuffer[columns++];
lead.Char.UnicodeChar = L' ';
lead.Attributes = lead.Attributes & ~(COMMON_LVB_LEADING_BYTE | COMMON_LVB_TRAILING_BYTE);
break;
}
auto& lead = infoBuffer[columns++];
lead.Char.UnicodeChar = fillCharacter;
lead.Attributes = lead.Attributes & ~COMMON_LVB_TRAILING_BYTE | COMMON_LVB_LEADING_BYTE;
auto& trail = infoBuffer[columns++];
trail.Char.UnicodeChar = fillCharacter;
trail.Attributes = trail.Attributes & ~COMMON_LVB_LEADING_BYTE | COMMON_LVB_TRAILING_BYTE;
}
}
else
{
for (; columns < columnsAvailable && inputPos < lengthToWrite; ++inputPos)
{
auto& lead = infoBuffer[columns++];
lead.Char.UnicodeChar = fillCharacter;
lead.Attributes = lead.Attributes & ~(COMMON_LVB_LEADING_BYTE | COMMON_LVB_TRAILING_BYTE);
}
}
break;
default:
__assume(false);
}
}
const auto done = screenBuffer.Write(it, startingCoordinate, false);
result.lengthRead = done.GetInputDistance(it);
result.cellsModified = done.GetCellDistance(it);
const auto writeViewport = Viewport::FromInclusive({ beg, y, beg + columns - 1, y });
THROW_IF_FAILED(WriteConsoleOutputWImplHelper(screenInfo, infoBuffer, w, writeViewport, unused));
// If we've overwritten image content, it needs to be erased.
ImageSlice::EraseCells(screenInfo.GetTextBuffer(), startingCoordinate, result.cellsModified);
y += 1;
result.cellsModified += columns;
}
result.lengthRead = inputPos;
if (writer)
{
writer.Submit();
}
if (screenBuffer.HasAccessibilityEventing())

View File

@@ -17,7 +17,6 @@
#include "VtIo.hpp"
#include "../types/inc/convert.hpp"
#include "../types/inc/GlyphWidth.hpp"
#include "../types/inc/Viewport.hpp"
#include "../interactivity/inc/ServiceLocator.hpp"

View File

@@ -349,24 +349,3 @@ MidiAudio& CONSOLE_INFORMATION::GetMidiAudio()
{
return _midiAudio;
}
// Method Description:
// - Generates a CHAR_INFO for this output cell, using the TextAttribute
// GetLegacyAttributes method to generate the legacy style attributes.
// Arguments:
// - cell: The cell to get the CHAR_INFO from
// Return Value:
// - a CHAR_INFO containing legacy information about the cell
CHAR_INFO CONSOLE_INFORMATION::AsCharInfo(const OutputCellView& cell) const noexcept
{
CHAR_INFO ci{ 0 };
ci.Char.UnicodeChar = Utf16ToUcs2(cell.Chars());
// If the current text attributes aren't legacy attributes, then
// use gci to look up the correct legacy attributes to use
// (for mapping RGB values to the nearest table value)
const auto& attr = cell.TextAttr();
ci.Attributes = attr.GetLegacyAttributes();
ci.Attributes |= GeneratePublicApiAttributeFormat(cell.DbcsAttr());
return ci;
}

View File

@@ -15,7 +15,6 @@
#include "ApiRoutines.h"
#include "../types/inc/convert.hpp"
#include "../types/inc/GlyphWidth.hpp"
#include "../types/inc/viewport.hpp"
#include "../interactivity/inc/ServiceLocator.hpp"
@@ -413,56 +412,6 @@ CATCH_RETURN();
CATCH_RETURN();
}
[[nodiscard]] static std::vector<CHAR_INFO> _ConvertCellsToMungedW(std::span<CHAR_INFO> buffer, const Viewport& rectangle)
{
std::vector<CHAR_INFO> result;
result.reserve(buffer.size());
const auto size = rectangle.Dimensions();
auto bufferIter = buffer.begin();
for (til::CoordType i = 0; i < size.height; i++)
{
for (til::CoordType j = 0; j < size.width; j++)
{
// Prepare a candidate charinfo on the output side copying the colors but not the lead/trail information.
auto candidate = *bufferIter;
WI_ClearAllFlags(candidate.Attributes, COMMON_LVB_SBCSDBCS);
// If the glyph we're given is full width, it needs to take two cells.
if (IsGlyphFullWidth(candidate.Char.UnicodeChar))
{
// If we're not on the final cell of the row...
if (j < size.width - 1)
{
// Mark that we're consuming two cells.
j++;
// Fill one cell with a copy of the color and character marked leading
WI_SetFlag(candidate.Attributes, COMMON_LVB_LEADING_BYTE);
result.push_back(candidate);
// Fill a second cell with a copy of the color marked trailing and a padding character.
WI_ClearFlag(candidate.Attributes, COMMON_LVB_LEADING_BYTE);
WI_SetFlag(candidate.Attributes, COMMON_LVB_TRAILING_BYTE);
}
else
{
// If we're on the final cell, this won't fit. Replace with a space.
candidate.Char.UnicodeChar = UNICODE_SPACE;
}
}
// Push our candidate in.
result.push_back(candidate);
// Advance to read the next item.
++bufferIter;
}
}
return result;
}
[[nodiscard]] HRESULT ReadConsoleOutputWImplHelper(const SCREEN_INFORMATION& context,
std::span<CHAR_INFO> targetBuffer,
const Viewport& requestRectangle,
@@ -470,8 +419,8 @@ CATCH_RETURN();
{
try
{
const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
auto& storageBuffer = context.GetActiveBuffer();
const auto& storageBuffer = context.GetActiveBuffer();
const auto& textBuffer = storageBuffer.GetTextBuffer();
const auto storageRectangle = storageBuffer.GetBufferSize();
const auto clippedRectangle = storageRectangle.Clamp(requestRectangle);
@@ -482,7 +431,6 @@ CATCH_RETURN();
}
const auto bufferStride = gsl::narrow_cast<size_t>(std::max(0, requestRectangle.Width()));
const auto width = gsl::narrow_cast<size_t>(clippedRectangle.Width());
const auto offsetY = clippedRectangle.Top() - requestRectangle.Top();
const auto offsetX = clippedRectangle.Left() - requestRectangle.Left();
// We always write the intersection between the valid `storageRectangle` and the given `requestRectangle`.
@@ -498,12 +446,53 @@ CATCH_RETURN();
for (til::CoordType y = clippedRectangle.Top(); y <= clippedRectangle.BottomInclusive(); y++)
{
auto it = storageBuffer.GetCellDataAt({ clippedRectangle.Left(), y });
const auto& row = textBuffer.GetRowByOffset(y);
const auto& attributes = row.Attributes();
const auto left = row.AdjustToGlyphStart(clippedRectangle.Left());
const auto right = clippedRectangle.RightExclusive();
for (size_t i = 0; i < width; i++)
{
targetBuffer[totalOffset + i] = gci.AsCharInfo(*it);
++it;
const auto endOff = std::min<til::CoordType>(right, attributes.size());
const auto begOff = std::min<til::CoordType>(left, endOff);
const auto end = attributes.begin() + endOff;
auto it = attributes.begin() + begOff;
auto off = totalOffset;
for (; it != end; ++it, ++off)
{
targetBuffer[off].Attributes = it->GetLegacyAttributes();
}
}
{
auto x = left;
auto off = totalOffset;
while (x < right)
{
const auto next = row.NavigateToNext(x);
const auto text = row.GetText(x, next);
const auto wch = text.size() == 1 ? text.front() : UNICODE_REPLACEMENT;
auto ci = &targetBuffer[off];
ci->Char.UnicodeChar = wch;
off += 1;
x += 1;
if (x < next)
{
ci->Attributes |= COMMON_LVB_LEADING_BYTE;
do
{
ci = &targetBuffer[off];
ci->Char.UnicodeChar = wch;
ci->Attributes |= COMMON_LVB_TRAILING_BYTE;
off += 1;
x += 1;
} while (x < next);
}
}
}
totalOffset += bufferStride;
@@ -575,6 +564,7 @@ CATCH_RETURN();
}
auto& storageBuffer = context.GetActiveBuffer();
auto& textBuffer = storageBuffer.GetTextBuffer();
const auto storageRectangle = storageBuffer.GetBufferSize();
const auto clippedRectangle = storageRectangle.Clamp(requestRectangle);
@@ -600,18 +590,83 @@ CATCH_RETURN();
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
auto writer = gci.GetVtWriterForBuffer(&context);
std::wstring stringBuffer;
for (til::CoordType y = clippedRectangle.Top(); y <= clippedRectangle.BottomInclusive(); y++)
{
const auto charInfos = buffer.subspan(totalOffset, width);
const til::point target{ clippedRectangle.Left(), y };
const auto charInfos = til::safe_slice_len(buffer, totalOffset, width);
if (charInfos.empty())
{
continue;
}
// Make the iterator and write to the target position.
storageBuffer.Write(OutputCellIterator(charInfos), target);
auto left = clippedRectangle.Left();
if (writer)
{
writer.WriteInfos(target, charInfos);
writer.WriteCUP({ left, y });
}
for (size_t end = 0; end < charInfos.size();)
{
const auto attr = charInfos[end].Attributes;
const auto beg = end;
// Find the next attribute change in the given CHAR_INFOs.
while (++end < charInfos.size() && charInfos[end].Attributes == attr)
{
}
const auto infoRun = til::safe_slice_abs(charInfos, beg, end);
THROW_HR_IF(E_UNEXPECTED, infoRun.empty());
// Accumulate this run of CHAR_INFOs with identical attributes into a single string.
{
stringBuffer.clear();
stringBuffer.reserve(infoRun.size() * 2);
if (WI_IsAnyFlagSet(infoRun.front().Attributes, COMMON_LVB_TRAILING_BYTE))
{
stringBuffer.push_back(L' ');
}
for (const auto& ci : infoRun)
{
if (WI_IsAnyFlagClear(ci.Attributes, COMMON_LVB_TRAILING_BYTE))
{
stringBuffer.push_back(Microsoft::Console::VirtualTerminal::VtIo::SanitizeUCS2(ci.Char.UnicodeChar));
}
}
if (WI_IsAnyFlagSet(infoRun.back().Attributes, COMMON_LVB_LEADING_BYTE))
{
stringBuffer.back() = L' ';
}
}
// Write the string into to the text buffer.
// If the character widths assigned by the application via COMMON_LVB_LEADING_BYTE/COMMON_LVB_TRAILING_BYTE
// don't match our idea of the character widths, we'll either pad it with whitespace or truncate it.
stringBuffer.append(infoRun.size(), L' ');
const auto right = gsl::narrow<til::CoordType>(left + infoRun.size());
const TextAttribute textAttr{ attr };
std::wstring_view text{ stringBuffer };
RowWriteState state{
.text = text,
.columnBegin = left,
.columnLimit = right,
};
textBuffer.Replace(y, textAttr, state);
if (writer)
{
text.remove_suffix(state.text.size());
writer.WriteAttributes(textAttr);
writer.WriteUTF16(text);
}
left = right;
}
totalOffset += bufferStride;
@@ -684,17 +739,7 @@ CATCH_RETURN();
writer.BackupCursor();
}
if (!context.GetActiveBuffer().GetCurrentFont().IsTrueTypeFont())
{
// For compatibility reasons, we must maintain the behavior that munges the data if we are writing while a raster font is enabled.
// This can be removed when raster font support is removed.
auto translated = _ConvertCellsToMungedW(buffer, requestRectangle);
RETURN_IF_FAILED(WriteConsoleOutputWImplHelper(context, translated, requestRectangle.Width(), requestRectangle, writtenRectangle));
}
else
{
RETURN_IF_FAILED(WriteConsoleOutputWImplHelper(context, buffer, requestRectangle.Width(), requestRectangle, writtenRectangle));
}
RETURN_IF_FAILED(WriteConsoleOutputWImplHelper(context, buffer, requestRectangle.Width(), requestRectangle, writtenRectangle));
if (writer)
{
@@ -718,10 +763,38 @@ CATCH_RETURN();
try
{
const auto attrs = ReadOutputAttributes(context.GetActiveBuffer(), origin, buffer.size());
std::copy(attrs.cbegin(), attrs.cend(), buffer.begin());
written = attrs.size();
const auto width = context.GetBufferSize().Width();
auto y = origin.y;
size_t read = 0;
til::small_vector<CHAR_INFO, 1024> infoBuffer{ gsl::narrow_cast<size_t>(width) };
while (read < buffer.size())
{
const auto beg = y == origin.y ? origin.x : 0;
const auto bufferRemaining = buffer.size() - read;
const auto columnsRemaining = gsl::narrow_cast<size_t>(width - beg);
const auto actualRemaining = gsl::narrow_cast<til::CoordType>(std::min(bufferRemaining, columnsRemaining));
const auto end = beg + actualRemaining;
const auto wantViewport = Viewport::FromInclusive({ beg, y, end, y });
Viewport actualViewport;
RETURN_IF_FAILED(ReadConsoleOutputWImplHelper(context, infoBuffer, wantViewport, actualViewport));
if (wantViewport != actualViewport)
{
break;
}
for (til::CoordType i = 0; i < actualRemaining; ++i)
{
buffer[read++] = infoBuffer[i].Attributes;
}
y += 1;
}
written = read;
return S_OK;
}
CATCH_RETURN();

View File

@@ -503,16 +503,15 @@ void ApiRoutines::SetConsoleActiveScreenBufferImpl(SCREEN_INFORMATION& newContex
alt.SetViewportSize(&size);
}
Viewport read;
til::small_vector<CHAR_INFO, 1024> infos;
infos.resize(area, CHAR_INFO{ L' ', FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED });
const auto dumpScreenInfo = [&](SCREEN_INFORMATION& screenInfo) {
Viewport read;
THROW_IF_FAILED(ReadConsoleOutputWImpl(screenInfo, infos, viewport, read));
for (til::CoordType i = 0; i < size.height; i++)
{
writer.WriteInfos({ 0, i }, { infos.begin() + i * size.width, static_cast<size_t>(size.width) });
}
Viewport write;
THROW_IF_FAILED(WriteConsoleOutputWImpl(screenInfo, infos, read, write));
writer.WriteCUP(screenInfo.GetTextBuffer().GetCursor().GetPosition());
writer.WriteAttributes(screenInfo.GetAttributes());

View File

@@ -7,7 +7,6 @@
#include <til/unicode.h>
#include "dbcs.h"
#include "../types/inc/GlyphWidth.hpp"
#include "../interactivity/inc/ServiceLocator.hpp"
#pragma hdrstop

View File

@@ -119,63 +119,6 @@ static void _CopyRectangle(SCREEN_INFORMATION& screenInfo,
}
}
// Routine Description:
// - This routine reads a sequence of attributes from the screen buffer.
// Arguments:
// - screenInfo - reference to screen buffer information.
// - coordRead - Screen buffer coordinate to begin reading from.
// - amountToRead - the number of elements to read
// Return Value:
// - vector of attribute data
std::vector<WORD> ReadOutputAttributes(const SCREEN_INFORMATION& screenInfo,
const til::point coordRead,
const size_t amountToRead)
{
// Short circuit. If nothing to read, leave early.
if (amountToRead == 0)
{
return {};
}
// Short circuit, if reading out of bounds, leave early.
if (!screenInfo.GetBufferSize().IsInBounds(coordRead))
{
return {};
}
// Get iterator to the position we should start reading at.
auto it = screenInfo.GetCellDataAt(coordRead);
// Count up the number of cells we've attempted to read.
ULONG amountRead = 0;
// Prepare the return value string.
std::vector<WORD> retVal;
// Reserve the number of cells. If we have >U+FFFF, it will auto-grow later and that's OK.
retVal.reserve(amountToRead);
// While we haven't read enough cells yet and the iterator is still valid (hasn't reached end of buffer)
while (amountRead < amountToRead && it)
{
const auto legacyAttributes = it->TextAttr().GetLegacyAttributes();
// If the first thing we read is trailing, pad with a space.
// OR If the last thing we read is leading, pad with a space.
if ((amountRead == 0 && it->DbcsAttr() == DbcsAttribute::Trailing) ||
(amountRead == (amountToRead - 1) && it->DbcsAttr() == DbcsAttribute::Leading))
{
retVal.push_back(legacyAttributes);
}
else
{
retVal.push_back(legacyAttributes | GeneratePublicApiAttributeFormat(it->DbcsAttr()));
}
amountRead++;
it++;
}
return retVal;
}
// Routine Description:
// - This routine reads a sequence of unicode characters from the screen buffer
// Arguments:

View File

@@ -26,10 +26,6 @@ void ScreenBufferSizeChange(const til::size coordNewSize);
[[nodiscard]] NTSTATUS DoCreateScreenBuffer();
std::vector<WORD> ReadOutputAttributes(const SCREEN_INFORMATION& screenInfo,
const til::point coordRead,
const size_t amountToRead);
std::wstring ReadOutputStringW(const SCREEN_INFORMATION& screenInfo,
const til::point coordRead,
const size_t amountToRead);

View File

@@ -5,7 +5,6 @@
#include "readDataRaw.hpp"
#include "dbcs.h"
#include "stream.h"
#include "../types/inc/GlyphWidth.hpp"
#include "../interactivity/inc/ServiceLocator.hpp"

View File

@@ -142,8 +142,6 @@ public:
MidiAudio& GetMidiAudio();
CHAR_INFO AsCharInfo(const OutputCellView& cell) const noexcept;
RenderData renderData;
private:

View File

@@ -12,7 +12,6 @@
#include "ApiRoutines.h"
#include "../types/inc/GlyphWidth.hpp"
#include "../interactivity/inc/ServiceLocator.hpp"

View File

@@ -305,8 +305,6 @@ class TextBufferIteratorTests
TEST_METHOD(DifferenceOperatorText);
TEST_METHOD(DifferenceOperatorCell);
TEST_METHOD(AsCharInfoCell);
TEST_METHOD(DereferenceOperatorText);
TEST_METHOD(DereferenceOperatorCell);
@@ -442,27 +440,6 @@ void TextBufferIteratorTests::DifferenceOperatorCell()
DifferenceOperatorTestHelper<TextBufferCellIterator>();
}
void TextBufferIteratorTests::AsCharInfoCell()
{
m_state->FillTextBuffer();
const auto it = GetIterator<TextBufferCellIterator>();
const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
const auto& outputBuffer = gci.GetActiveOutputBuffer();
const auto& row = outputBuffer._textBuffer->GetRowByOffset(it._pos.y);
const auto wcharExpected = *row.GlyphAt(it._pos.x).begin();
const auto attrExpected = row.GetAttrByColumn(it._pos.x);
const auto cellActual = gci.AsCharInfo(*it);
const auto wcharActual = cellActual.Char.UnicodeChar;
const auto attrActual = it->TextAttr();
VERIFY_ARE_EQUAL(wcharExpected, wcharActual);
VERIFY_ARE_EQUAL(attrExpected, attrActual);
}
void TextBufferIteratorTests::DereferenceOperatorText()
{
m_state->FillTextBuffer();