Compare commits

...

5 commits

Author SHA1 Message Date
Dustin Howett 17068f4460 HAX: high water mark, remove Copy functions, fix reflow 2021-07-27 13:44:21 -05:00
Dustin Howett 227ce8ff20 beef prevention 2021-07-27 13:42:54 -05:00
Dustin Howett 99d9bac51d Kill GlyphAt (writable and const) 2021-07-27 13:42:52 -05:00
Dustin Howett 97a2dc6878 Rewrite Reflow 2021-07-27 13:41:53 -05:00
Dustin Howett abf66b2ff8 NO TESTS: Add OututCellIterator that takes TextBufferCellIterator 2021-07-27 13:38:05 -05:00
17 changed files with 149 additions and 546 deletions

View file

@ -178,32 +178,6 @@ void CharRow::ClearGlyph(const size_t column)
_data.at(column).EraseChars();
}
// Routine Description:
// - returns text data at column as a const reference.
// Arguments:
// - column - column to get text data for
// Return Value:
// - text data at column
// - Note: will throw exception if column is out of bounds
const CharRow::reference CharRow::GlyphAt(const size_t column) const
{
THROW_HR_IF(E_INVALIDARG, column >= _data.size());
return { const_cast<CharRow&>(*this), column };
}
// Routine Description:
// - returns text data at column as a reference.
// Arguments:
// - column - column to get text data for
// Return Value:
// - text data at column
// - Note: will throw exception if column is out of bounds
CharRow::reference CharRow::GlyphAt(const size_t column)
{
THROW_HR_IF(E_INVALIDARG, column >= _data.size());
return { *this, column };
}
std::wstring CharRow::GetText() const
{
std::wstring wstr;
@ -211,13 +185,9 @@ std::wstring CharRow::GetText() const
for (size_t i = 0; i < _data.size(); ++i)
{
const auto glyph = GlyphAt(i);
if (!DbcsAttrAt(i).IsTrailing())
if (!_data.at(i).DbcsAttr().IsTrailing())
{
for (const auto wch : glyph)
{
wstr.push_back(wch);
}
wstr.append(GlyphDataAt(i));
}
}
return wstr;
@ -235,7 +205,7 @@ const DelimiterClass CharRow::DelimiterClassAt(const size_t column, const std::w
{
THROW_HR_IF(E_INVALIDARG, column >= _data.size());
const auto glyph = *GlyphAt(column).begin();
const auto glyph = *GlyphDataAt(column).begin();
if (glyph <= UNICODE_SPACE)
{
return DelimiterClass::ControlChar;
@ -272,6 +242,40 @@ COORD CharRow::GetStorageKey(const size_t column) const noexcept
}
// 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

View file

@ -21,7 +21,6 @@ Revision History:
#pragma once
#include "DbcsAttribute.hpp"
#include "CharRowCellReference.hpp"
#include "CharRowCell.hpp"
#include "UnicodeStorage.hpp"
@ -52,7 +51,6 @@ public:
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;
using reference = typename CharRowCellReference;
CharRow(size_t rowWidth, ROW* const pParent) noexcept;
@ -68,8 +66,7 @@ public:
const DelimiterClass DelimiterClassAt(const size_t column, const std::wstring_view wordDelimiters) const;
// working with glyphs
const reference GlyphAt(const size_t column) const;
reference GlyphAt(const size_t column);
const std::wstring_view GlyphDataAt(const size_t column) const;
// iterators
iterator begin() noexcept;
@ -86,13 +83,13 @@ public:
void UpdateParent(ROW* const pParent);
friend CharRowCellReference;
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

View file

@ -1,136 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "UnicodeStorage.hpp"
#include "CharRow.hpp"
// Routine Description:
// - assignment operator. will store extended glyph data in a separate storage location
// Arguments:
// - chars - the glyph data to store
void CharRowCellReference::operator=(const std::wstring_view chars)
{
THROW_HR_IF(E_INVALIDARG, chars.empty());
if (chars.size() == 1)
{
_cellData().Char() = chars.front();
_cellData().DbcsAttr().SetGlyphStored(false);
}
else
{
auto& storage = _parent.GetUnicodeStorage();
const auto key = _parent.GetStorageKey(_index);
storage.StoreGlyph(key, { chars.cbegin(), chars.cend() });
_cellData().DbcsAttr().SetGlyphStored(true);
}
}
// Routine Description:
// - implicit conversion to vector<wchar_t> operator.
// Return Value:
// - std::vector<wchar_t> of the glyph data in the referenced cell
CharRowCellReference::operator std::wstring_view() const
{
return _glyphData();
}
// Routine Description:
// - The CharRowCell this object "references"
// Return Value:
// - ref to the CharRowCell
CharRowCell& CharRowCellReference::_cellData()
{
return _parent._data.at(_index);
}
// Routine Description:
// - The CharRowCell this object "references"
// Return Value:
// - ref to the CharRowCell
const CharRowCell& CharRowCellReference::_cellData() const
{
return _parent._data.at(_index);
}
// Routine Description:
// - the glyph data of the referenced cell
// Return Value:
// - the glyph data
std::wstring_view CharRowCellReference::_glyphData() const
{
if (_cellData().DbcsAttr().IsGlyphStored())
{
const auto& text = _parent.GetUnicodeStorage().GetText(_parent.GetStorageKey(_index));
return { text.data(), text.size() };
}
else
{
return { &_cellData().Char(), 1 };
}
}
// Routine Description:
// - gets read-only iterator to the beginning of the glyph data
// Return Value:
// - iterator of the glyph data
CharRowCellReference::const_iterator CharRowCellReference::begin() const
{
if (_cellData().DbcsAttr().IsGlyphStored())
{
return _parent.GetUnicodeStorage().GetText(_parent.GetStorageKey(_index)).data();
}
else
{
return &_cellData().Char();
}
}
// Routine Description:
// - get read-only iterator to the end of the glyph data
// Return Value:
// - end iterator of the glyph data
#pragma warning(push)
#pragma warning(disable : 26481)
// TODO GH 2672: eliminate using pointers raw as begin/end markers in this class
CharRowCellReference::const_iterator CharRowCellReference::end() const
{
if (_cellData().DbcsAttr().IsGlyphStored())
{
const auto& chars = _parent.GetUnicodeStorage().GetText(_parent.GetStorageKey(_index));
return chars.data() + chars.size();
}
else
{
return &_cellData().Char() + 1;
}
}
#pragma warning(pop)
bool operator==(const CharRowCellReference& ref, const std::vector<wchar_t>& glyph)
{
const DbcsAttribute& dbcsAttr = ref._cellData().DbcsAttr();
if (glyph.size() == 1 && dbcsAttr.IsGlyphStored())
{
return false;
}
else if (glyph.size() > 1 && !dbcsAttr.IsGlyphStored())
{
return false;
}
else if (glyph.size() == 1 && !dbcsAttr.IsGlyphStored())
{
return ref._cellData().Char() == glyph.front();
}
else
{
const auto& chars = ref._parent.GetUnicodeStorage().GetText(ref._parent.GetStorageKey(ref._index));
return chars == glyph;
}
}
bool operator==(const std::vector<wchar_t>& glyph, const CharRowCellReference& ref)
{
return ref == glyph;
}

View file

@ -1,63 +0,0 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- CharRowCellReference.hpp
Abstract:
- reference class for the glyph data of a char row cell
Author(s):
- Austin Diviness (AustDi) 02-May-2018
--*/
#pragma once
#include "DbcsAttribute.hpp"
#include "CharRowCell.hpp"
#include <utility>
class CharRow;
class CharRowCellReference final
{
public:
using const_iterator = const wchar_t*;
CharRowCellReference(CharRow& parent, const size_t index) noexcept :
_parent{ parent },
_index{ index }
{
}
~CharRowCellReference() = default;
CharRowCellReference(const CharRowCellReference&) noexcept = default;
CharRowCellReference(CharRowCellReference&&) noexcept = default;
void operator=(const CharRowCellReference&) = delete;
void operator=(CharRowCellReference&&) = delete;
void operator=(const std::wstring_view chars);
operator std::wstring_view() const;
const_iterator begin() const;
const_iterator end() const;
friend bool operator==(const CharRowCellReference& ref, const std::vector<wchar_t>& glyph);
friend bool operator==(const std::vector<wchar_t>& glyph, const CharRowCellReference& ref);
private:
// what char row the object belongs to
CharRow& _parent;
// the index of the cell in the parent char row
const size_t _index;
CharRowCell& _cellData();
const CharRowCell& _cellData() const;
std::wstring_view _glyphData() const;
};
bool operator==(const CharRowCellReference& ref, const std::vector<wchar_t>& glyph);
bool operator==(const std::vector<wchar_t>& glyph, const CharRowCellReference& ref);

View file

@ -4,6 +4,7 @@
#include "precomp.h"
#include "OutputCellIterator.hpp"
#include "TextBufferCellIterator.hpp"
#include "../../types/inc/convert.hpp"
#include "../../types/inc/Utf16Parser.hpp"
@ -153,6 +154,17 @@ OutputCellIterator::OutputCellIterator(const gsl::span<const OutputCell> cells)
{
}
OutputCellIterator::OutputCellIterator(TextBufferCellIterator start) :
_mode(Mode::BufferCopy),
_run(start),
_currentView(*start),
_attr(InvalidTextAttribute),
_distance(0),
_pos(0),
_fillLimit(0)
{
}
// Routine Description:
// - Specifies whether this iterator is valid for dereferencing (still valid underlying data)
// Return Value:
@ -190,6 +202,11 @@ OutputCellIterator::operator bool() const noexcept
{
return _pos < std::get<gsl::span<const WORD>>(_run).size();
}
case Mode::BufferCopy:
{
auto& it = std::get<TextBufferCellIterator>(_run);
return (bool)it;
}
default:
FAIL_FAST_HR(E_NOTIMPL);
}
@ -289,6 +306,16 @@ OutputCellIterator& OutputCellIterator::operator++()
}
break;
}
case Mode::BufferCopy:
{
auto& it = std::get<TextBufferCellIterator>(_run);
if (++it)
{
_pos++;
_currentView = *it;
}
break;
}
default:
FAIL_FAST_HR(E_NOTIMPL);
}

View file

@ -23,6 +23,7 @@ Revision History:
#include "OutputCell.hpp"
#include "OutputCellView.hpp"
#include "TextBufferCellIterator.hpp"
class OutputCellIterator final
{
@ -42,6 +43,7 @@ public:
OutputCellIterator(const gsl::span<const WORD> legacyAttributes) noexcept;
OutputCellIterator(const gsl::span<const CHAR_INFO> charInfos) noexcept;
OutputCellIterator(const gsl::span<const OutputCell> cells);
OutputCellIterator(TextBufferCellIterator start);
~OutputCellIterator() = default;
OutputCellIterator& operator=(const OutputCellIterator& it) = default;
@ -52,6 +54,8 @@ public:
ptrdiff_t GetInputDistance(OutputCellIterator other) const noexcept;
friend ptrdiff_t operator-(OutputCellIterator one, OutputCellIterator two) = delete;
void AddCellDistanceFault(ptrdiff_t fault) { _distance += fault; };
OutputCellIterator& operator++();
OutputCellIterator operator++(int);
@ -83,6 +87,10 @@ private:
// Cell mode is where we have an already fully structured cell data usually
// from accessing/copying data already put into the OutputBuffer.
Cell,
// BufferCopy mode is where we're starting from a TextBufferCellIterator,
// which produces fully-formed cells out of another buffer.
BufferCopy,
};
Mode _mode;
@ -93,6 +101,7 @@ private:
gsl::span<const WORD>,
gsl::span<const CHAR_INFO>,
gsl::span<const OutputCell>,
TextBufferCellIterator,
std::monostate>
_run;

View file

@ -24,6 +24,7 @@ ROW::ROW(const SHORT rowId, const unsigned short rowWidth, const TextAttribute f
_lineRendition{ LineRendition::SingleWidth },
_wrapForced{ false },
_doubleBytePadded{ false },
_highWaterMark{ 0 },
_pParent{ pParent }
{
}
@ -39,6 +40,7 @@ bool ROW::Reset(const TextAttribute Attr)
_lineRendition = LineRendition::SingleWidth;
_wrapForced = false;
_doubleBytePadded = false;
_highWaterMark = 0;
_charRow.Reset();
try
{
@ -152,19 +154,21 @@ OutputCellIterator ROW::WriteCells(OutputCellIterator it, const size_t index, co
if (currentIndex == 0 && it->DbcsAttr().IsTrailing())
{
_charRow.ClearCell(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);
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.GlyphAt(currentIndex) = it->Chars();
_charRow.WriteCharsIntoColumn(currentIndex, it->Chars());
++it;
}
@ -194,5 +198,13 @@ OutputCellIterator ROW::WriteCells(OutputCellIterator it, const size_t index, co
_attrRow.Replace(colorStarts, currentIndex, currentColor);
}
//_highWaterMark = std::max(currentIndex, _highWaterMark);
return it;
}
size_t ROW::MeasureRight() const noexcept
{
return _charRow.MeasureRight();
//return _highWaterMark;
}

View file

@ -42,8 +42,10 @@ public:
void SetDoubleBytePadded(const bool doubleBytePadded) noexcept { _doubleBytePadded = doubleBytePadded; }
bool WasDoubleBytePadded() const noexcept { return _doubleBytePadded; }
const CharRow& GetCharRow() const noexcept { return _charRow; }
size_t MeasureRight() const noexcept;
CharRow& GetCharRow() noexcept { return _charRow; }
const CharRow& GetCharRow() const noexcept { return _charRow; }
const ATTR_ROW& GetAttrRow() const noexcept { return _attrRow; }
ATTR_ROW& GetAttrRow() noexcept { return _attrRow; }
@ -76,6 +78,7 @@ private:
LineRendition _lineRendition;
SHORT _id;
unsigned short _rowWidth;
unsigned short _highWaterMark;
// 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

View file

@ -25,7 +25,6 @@
<ClCompile Include="..\textBufferTextIterator.cpp" />
<ClCompile Include="..\CharRow.cpp" />
<ClCompile Include="..\CharRowCell.cpp" />
<ClCompile Include="..\CharRowCellReference.cpp" />
<ClCompile Include="..\precomp.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
@ -50,7 +49,6 @@
<ClInclude Include="..\textBufferTextIterator.hpp" />
<ClInclude Include="..\CharRow.hpp" />
<ClInclude Include="..\CharRowCell.hpp" />
<ClInclude Include="..\CharRowCellReference.hpp" />
<ClInclude Include="..\precomp.h" />
<ClInclude Include="..\UnicodeStorage.hpp" />
</ItemGroup>

View file

@ -43,7 +43,6 @@ SOURCES= \
..\textBufferTextIterator.cpp \
..\CharRow.cpp \
..\CharRowCell.cpp \
..\CharRowCellReference.cpp \
..\UnicodeStorage.cpp \
..\search.cpp \

View file

@ -58,9 +58,14 @@ TextBuffer::TextBuffer(const COORD screenBufferSize,
// - OtherBuffer - The text buffer to copy properties from
// Return Value:
// - <none>
void TextBuffer::CopyProperties(const TextBuffer& OtherBuffer) noexcept
void TextBuffer::CopyProperties(const TextBuffer& other) noexcept
{
GetCursor().CopyProperties(OtherBuffer.GetCursor());
_cursor.CopyProperties(other._cursor);
_hyperlinkMap = other._hyperlinkMap;
_hyperlinkCustomIdMap = other._hyperlinkCustomIdMap;
_currentHyperlinkId = other._currentHyperlinkId;
_idsAndPatterns = other._idsAndPatterns;
_currentPatternId = other._currentPatternId;
}
// Routine Description:
@ -184,129 +189,6 @@ TextBufferCellIterator TextBuffer::GetCellDataAt(const COORD at, const Viewport
return TextBufferCellIterator(*this, at, limit);
}
//Routine Description:
// - Corrects and enforces consistent double byte character state (KAttrs line) within a row of the text buffer.
// - This will take the given double byte information and check that it will be consistent when inserted into the buffer
// at the current cursor position.
// - It will correct the buffer (by erasing the character prior to the cursor) if necessary to make a consistent state.
//Arguments:
// - dbcsAttribute - Double byte information associated with the character about to be inserted into the buffer
//Return Value:
// - True if it is valid to insert a character with the given double byte attributes. False otherwise.
bool TextBuffer::_AssertValidDoubleByteSequence(const DbcsAttribute dbcsAttribute)
{
// To figure out if the sequence is valid, we have to look at the character that comes before the current one
const COORD coordPrevPosition = _GetPreviousFromCursor();
ROW& prevRow = GetRowByOffset(coordPrevPosition.Y);
DbcsAttribute prevDbcsAttr;
try
{
prevDbcsAttr = prevRow.GetCharRow().DbcsAttrAt(coordPrevPosition.X);
}
catch (...)
{
LOG_HR(wil::ResultFromCaughtException());
return false;
}
bool fValidSequence = true; // Valid until proven otherwise
bool fCorrectableByErase = false; // Can't be corrected until proven otherwise
// Here's the matrix of valid items:
// N = None (single byte)
// L = Lead (leading byte of double byte sequence
// T = Trail (trailing byte of double byte sequence
// Prev Curr Result
// N N OK.
// N L OK.
// N T Fail, uncorrectable. Trailing byte must have had leading before it.
// L N Fail, OK with erase. Lead needs trailing pair. Can erase lead to correct.
// L L Fail, OK with erase. Lead needs trailing pair. Can erase prev lead to correct.
// L T OK.
// T N OK.
// T L OK.
// T T Fail, uncorrectable. New trailing byte must have had leading before it.
// Check for only failing portions of the matrix:
if (prevDbcsAttr.IsSingle() && dbcsAttribute.IsTrailing())
{
// N, T failing case (uncorrectable)
fValidSequence = false;
}
else if (prevDbcsAttr.IsLeading())
{
if (dbcsAttribute.IsSingle() || dbcsAttribute.IsLeading())
{
// L, N and L, L failing cases (correctable)
fValidSequence = false;
fCorrectableByErase = true;
}
}
else if (prevDbcsAttr.IsTrailing() && dbcsAttribute.IsTrailing())
{
// T, T failing case (uncorrectable)
fValidSequence = false;
}
// If it's correctable by erase, erase the previous character
if (fCorrectableByErase)
{
// Erase previous character into an N type.
try
{
prevRow.ClearColumn(coordPrevPosition.X);
}
catch (...)
{
LOG_HR(wil::ResultFromCaughtException());
return false;
}
// Sequence is now N N or N L, which are both okay. Set sequence back to valid.
fValidSequence = true;
}
return fValidSequence;
}
//Routine Description:
// - Call before inserting a character into the buffer.
// - This will ensure a consistent double byte state (KAttrs line) within the text buffer
// - It will attempt to correct the buffer if we're inserting an unexpected double byte character type
// and it will pad out the buffer if we're going to split a double byte sequence across two rows.
//Arguments:
// - dbcsAttribute - Double byte information associated with the character about to be inserted into the buffer
//Return Value:
// - true if we successfully prepared the buffer and moved the cursor
// - false otherwise (out of memory)
bool TextBuffer::_PrepareForDoubleByteSequence(const DbcsAttribute dbcsAttribute)
{
// This function corrects most errors. If this is false, we had an uncorrectable one which
// older versions of conhost simply let pass by unflinching.
LOG_HR_IF(E_NOT_VALID_STATE, !(_AssertValidDoubleByteSequence(dbcsAttribute))); // Shouldn't be uncorrectable sequences unless something is very wrong.
bool fSuccess = true;
// Now compensate if we don't have enough space for the upcoming double byte sequence
// We only need to compensate for leading bytes
if (dbcsAttribute.IsLeading())
{
const auto cursorPosition = GetCursor().GetPosition();
const auto lineWidth = GetLineWidth(cursorPosition.Y);
// If we're about to lead on the last column in the row, we need to add a padding space
if (cursorPosition.X == lineWidth - 1)
{
// set that we're wrapping for double byte reasons
auto& row = GetRowByOffset(cursorPosition.Y);
row.SetDoubleBytePadded(true);
// then move the cursor forward and onto the next row
fSuccess = IncrementCursor();
}
}
return fSuccess;
}
// Routine Description:
// - Writes cells to the output buffer. Writes at the cursor.
// Arguments:
@ -349,7 +231,10 @@ OutputCellIterator TextBuffer::Write(const OutputCellIterator givenIt,
{
// Attempt to write as much data as possible onto this line.
// NOTE: if wrap = true/false, we want to set the line's wrap to true/false (respectively) if we reach the end of the line
auto oldIt = it;
OutputDebugStringW(fmt::format(L"Write starting at {},{}\n", lineTarget.X, lineTarget.Y).c_str());
it = WriteLine(it, lineTarget, wrap);
OutputDebugStringW(fmt::format(L"--- done line {}; distance moved {} CEL {} INP\n", lineTarget.Y, it.GetCellDistance(oldIt), it.GetInputDistance(oldIt)).c_str());
// Move to the next line down.
lineTarget.X = 0;
@ -391,70 +276,6 @@ OutputCellIterator TextBuffer::WriteLine(const OutputCellIterator givenIt,
return newIt;
}
//Routine Description:
// - Inserts one codepoint into the buffer at the current cursor position and advances the cursor as appropriate.
//Arguments:
// - chars - The codepoint to insert
// - dbcsAttribute - Double byte information associated with the codepoint
// - bAttr - Color data associated with the character
//Return Value:
// - true if we successfully inserted the character
// - false otherwise (out of memory)
bool TextBuffer::InsertCharacter(const std::wstring_view chars,
const DbcsAttribute dbcsAttribute,
const TextAttribute attr)
{
// Ensure consistent buffer state for double byte characters based on the character type we're about to insert
bool fSuccess = _PrepareForDoubleByteSequence(dbcsAttribute);
if (fSuccess)
{
// Get the current cursor position
short const iRow = GetCursor().GetPosition().Y; // row stored as logical position, not array position
short const iCol = GetCursor().GetPosition().X; // column logical and array positions are equal.
// Get the row associated with the given logical position
ROW& Row = GetRowByOffset(iRow);
// Store character and double byte data
CharRow& charRow = Row.GetCharRow();
try
{
charRow.GlyphAt(iCol) = chars;
charRow.DbcsAttrAt(iCol) = dbcsAttribute;
}
catch (...)
{
LOG_HR(wil::ResultFromCaughtException());
return false;
}
// Store color data
fSuccess = Row.GetAttrRow().SetAttrToEnd(iCol, attr);
if (fSuccess)
{
// Advance the cursor
fSuccess = IncrementCursor();
}
}
return fSuccess;
}
//Routine Description:
// - Inserts one ucs2 codepoint into the buffer at the current cursor position and advances the cursor as appropriate.
//Arguments:
// - wch - The codepoint to insert
// - dbcsAttribute - Double byte information associated with the codepoint
// - bAttr - Color data associated with the character
//Return Value:
// - true if we successfully inserted the character
// - false otherwise (out of memory)
bool TextBuffer::InsertCharacter(const wchar_t wch, const DbcsAttribute dbcsAttribute, const TextAttribute attr)
{
return InsertCharacter({ &wch, 1 }, dbcsAttribute, attr);
}
//Routine Description:
// - Finds the current row in the buffer (as indicated by the cursor position)
// and specifies that we have forced a line wrap on that row
@ -606,7 +427,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();
@ -617,7 +438,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);
}
@ -974,7 +795,7 @@ void TextBuffer::_RefreshRowIDs(std::optional<SHORT> newRowWidth)
it.SetId(i++);
// Also update the char row parent pointers as they can get shuffled up in the rotates.
it.GetCharRow().UpdateParent(&it);
const_cast<CharRow&>(it.GetCharRow()).UpdateParent(&it);
// Resize the rows in the X dimension if we have a new width
if (newRowWidth.has_value())
@ -2138,8 +1959,13 @@ 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());
//const CharRow& charRow = row.GetCharRow();
short iRight = gsl::narrow_cast<short>(row.MeasureRight());
if (iRight == 0 && !row.WasWrapForced())
{
newBuffer.NewlineCursor();
continue;
}
// If we're starting a new row, try and preserve the line rendition
// from the row in the original buffer.
@ -2174,33 +2000,41 @@ HRESULT TextBuffer::Reflow(TextBuffer& oldBuffer,
}
}
// Loop through every character in the current row (up to
// the "right" boundary, which is one past the final valid
// character)
for (short iOldCol = 0; iOldCol < iRight; iOldCol++)
OutputCellIterator it{ oldBuffer.GetCellDataAt({ 0, iOldRow }, Viewport::FromDimensions({ 0, iOldRow }, iRight, 1)) };
const auto first = it;
auto preCurPos{ newBuffer.GetCursor().GetPosition() };
auto postCurPos{ preCurPos };
OutputDebugStringW(fmt::format(L"+++ CURSOR STARTED AT {},{}\n", preCurPos.X, preCurPos.Y).c_str());
//const auto last = newBuffer.Write(it, preCurPos, true); // true - wrap if we don't fit!
ptrdiff_t totalDistance{};
while (it)
{
if (iOldCol == cOldCursorPos.X && iOldRow == cOldCursorPos.Y)
auto last = it;
it = newBuffer.Write(it, postCurPos, true);
auto distance = it.GetCellDistance(last);
totalDistance += distance;
if (it)
{
cNewCursorPos = newCursor.GetPosition();
fFoundCursorPos = true;
// The iterator is still valid, but we stopped for some reason.
// Likely, we ran out of space.
newBuffer.IncrementCircularBuffer();
postCurPos.Y--; // adjust new write head location
preCurPos.Y--; // adjust original origin location (for future cursor repositioning)
}
try
{
// TODO: MSFT: 19446208 - this should just use an iterator and the inserter...
const auto glyph = row.GetCharRow().GlyphAt(iOldCol);
const auto dbcsAttr = row.GetCharRow().DbcsAttrAt(iOldCol);
const auto textAttr = row.GetAttrRow().GetAttrByColumn(iOldCol);
if (!newBuffer.InsertCharacter(glyph, dbcsAttr, textAttr))
{
hr = E_OUTOFMEMORY;
break;
}
}
CATCH_RETURN();
newBuffer.GetSize().MoveInBounds(distance, postCurPos);
}
if (iOldRow == cOldCursorPos.Y)
{
cNewCursorPos = preCurPos;
// we started at (0, NewY) -- walk forward by the old position's X to figure out where we land in the new line
newBuffer.GetSize().MoveInBounds(cOldCursorPos.X, cNewCursorPos);
fFoundCursorPos = true;
}
OutputDebugStringW(fmt::format(L"+++ AFTER MOVING {}, CURSOR ENDED AT {},{}\n", totalDistance, preCurPos.X, preCurPos.Y).c_str());
newBuffer.GetCursor().SetPosition(postCurPos);
// If we found the old row that the caller was interested in, set the
// out value of that parameter to the cursor's current Y position (the
// new location of the _end_ of that row in the buffer).
@ -2232,7 +2066,7 @@ HRESULT TextBuffer::Reflow(TextBuffer& oldBuffer,
// Only do so if we were not forced to wrap. If we did
// force a word wrap, then the existing line break was
// only because we ran out of space.
if (iRight < cOldColsTotal && !row.WasWrapForced())
if (totalDistance < cOldColsTotal && !row.WasWrapForced())
{
if (iRight == cOldCursorPos.X && iOldRow == cOldCursorPos.Y)
{
@ -2289,8 +2123,6 @@ HRESULT TextBuffer::Reflow(TextBuffer& oldBuffer,
{
// Finish copying remaining parameters from the old text buffer to the new one
newBuffer.CopyProperties(oldBuffer);
newBuffer.CopyHyperlinkMaps(oldBuffer);
newBuffer.CopyPatterns(oldBuffer);
// If we found where to put the cursor while placing characters into the buffer,
// just put the cursor there. Otherwise we have to advance manually.
@ -2354,6 +2186,8 @@ HRESULT TextBuffer::Reflow(TextBuffer& oldBuffer,
newCursor.SetSize(ulSize);
}
OutputDebugStringW(fmt::format(L"+++ REFLOW DONE\n\n\n").c_str());
return hr;
}
@ -2451,18 +2285,6 @@ std::wstring TextBuffer::GetCustomIdFromId(uint16_t id) const
return {};
}
// Method Description:
// - Copies the hyperlink/customID maps of the old buffer into this one,
// also copies currentHyperlinkId
// Arguments:
// - The other buffer
void TextBuffer::CopyHyperlinkMaps(const TextBuffer& other)
{
_hyperlinkMap = other._hyperlinkMap;
_hyperlinkCustomIdMap = other._hyperlinkCustomIdMap;
_currentHyperlinkId = other._currentHyperlinkId;
}
// Method Description:
// - Adds a regex pattern we should search for
// - The searching does not happen here, we only search when asked to by TerminalCore
@ -2485,16 +2307,6 @@ void TextBuffer::ClearPatternRecognizers() noexcept
_currentPatternId = 0;
}
// Method Description:
// - Copies the patterns the other buffer knows about into this one
// Arguments:
// - The other buffer
void TextBuffer::CopyPatterns(const TextBuffer& OtherBuffer)
{
_idsAndPatterns = OtherBuffer._idsAndPatterns;
_currentPatternId = OtherBuffer._currentPatternId;
}
// Method Description:
// - Finds patterns within the requested region of the text buffer
// Arguments:

View file

@ -97,8 +97,6 @@ public:
const std::optional<bool> setWrap = std::nullopt,
const std::optional<size_t> limitRight = std::nullopt);
bool InsertCharacter(const wchar_t wch, const DbcsAttribute dbcsAttribute, const TextAttribute attr);
bool InsertCharacter(const std::wstring_view chars, const DbcsAttribute dbcsAttribute, const TextAttribute attr);
bool IncrementCursor();
bool NewlineCursor();
@ -158,7 +156,6 @@ public:
uint16_t GetHyperlinkId(std::wstring_view uri, std::wstring_view id);
void RemoveHyperlinkFromMap(uint16_t id) noexcept;
std::wstring GetCustomIdFromId(uint16_t id) const;
void CopyHyperlinkMaps(const TextBuffer& OtherBuffer);
class TextAndColor
{
@ -197,7 +194,6 @@ public:
const size_t AddPatternRecognizer(const std::wstring_view regexString);
void ClearPatternRecognizers() noexcept;
void CopyPatterns(const TextBuffer& OtherBuffer);
interval_tree::IntervalTree<til::point, size_t> GetPatterns(const size_t firstRow, const size_t lastRow) const;
private:
@ -230,10 +226,6 @@ private:
void _NotifyPaint(const Microsoft::Console::Types::Viewport& viewport) const;
// Assist with maintaining proper buffer state for Double Byte character sequences
bool _PrepareForDoubleByteSequence(const DbcsAttribute dbcsAttribute);
bool _AssertValidDoubleByteSequence(const DbcsAttribute dbcsAttribute);
ROW& _GetFirstRow();
ROW& _GetPrevRowNoWrap(const ROW& row);

View file

@ -326,7 +326,7 @@ 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().GlyphAt(_pos.X),
_view = OutputCellView(_pRow->GetCharRow().GlyphDataAt(_pos.X),
_pRow->GetCharRow().DbcsAttrAt(_pos.X),
*_attrIter,
TextAttributeBehavior::Stored);

View file

@ -58,8 +58,8 @@ protected:
const ROW* _pRow;
ATTR_ROW::const_iterator _attrIter;
const TextBuffer& _buffer;
const Microsoft::Console::Types::Viewport _bounds;
std::reference_wrapper<const TextBuffer> _buffer;
Microsoft::Console::Types::Viewport _bounds;
bool _exceeded;
COORD _pos;

View file

@ -93,8 +93,6 @@ class TextBufferTests
TEST_METHOD(TestCopyProperties);
TEST_METHOD(TestInsertCharacter);
TEST_METHOD(TestIncrementCursor);
TEST_METHOD(TestNewlineCursor);
@ -386,50 +384,6 @@ void TextBufferTests::TestCopyProperties()
VERIFY_IS_TRUE(testTextBuffer->GetCursor().GetDelay());
}
void TextBufferTests::TestInsertCharacter()
{
TextBuffer& textBuffer = GetTbi();
// get starting cursor position
COORD const coordCursorBefore = textBuffer.GetCursor().GetPosition();
// Get current row from the buffer
ROW& Row = textBuffer.GetRowByOffset(coordCursorBefore.Y);
// create some sample test data
const auto wch = L'Z';
const std::wstring_view wchTest(&wch, 1);
DbcsAttribute dbcsAttribute;
dbcsAttribute.SetTrailing();
WORD const wAttrTest = BACKGROUND_INTENSITY | FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_BLUE;
TextAttribute TestAttributes = TextAttribute(wAttrTest);
CharRow& charRow = Row.GetCharRow();
charRow.DbcsAttrAt(coordCursorBefore.X).SetLeading();
// ensure that the buffer didn't start with these fields
VERIFY_ARE_NOT_EQUAL(charRow.GlyphAt(coordCursorBefore.X), wchTest);
VERIFY_ARE_NOT_EQUAL(charRow.DbcsAttrAt(coordCursorBefore.X), dbcsAttribute);
auto attr = Row.GetAttrRow().GetAttrByColumn(coordCursorBefore.X);
VERIFY_ARE_NOT_EQUAL(attr, TestAttributes);
// now apply the new data to the buffer
textBuffer.InsertCharacter(wchTest, dbcsAttribute, TestAttributes);
// ensure that the buffer position where the cursor WAS contains the test items
VERIFY_ARE_EQUAL(charRow.GlyphAt(coordCursorBefore.X), wchTest);
VERIFY_ARE_EQUAL(charRow.DbcsAttrAt(coordCursorBefore.X), dbcsAttribute);
attr = Row.GetAttrRow().GetAttrByColumn(coordCursorBefore.X);
VERIFY_ARE_EQUAL(attr, TestAttributes);
// ensure that the cursor moved to a new position (X or Y or both have changed)
VERIFY_IS_TRUE((coordCursorBefore.X != textBuffer.GetCursor().GetPosition().X) ||
(coordCursorBefore.Y != textBuffer.GetCursor().GetPosition().Y));
// the proper advancement of the cursor (e.g. which position it goes to) is validated in other tests
}
void TextBufferTests::TestIncrementCursor()
{
TextBuffer& textBuffer = GetTbi();

View file

@ -26,7 +26,7 @@ namespace Microsoft::Console::Types
~Viewport() {}
constexpr Viewport() noexcept :
_sr({ 0, 0, -1, -1 }){};
Viewport(const Viewport& other) noexcept;
Viewport(const Viewport& other) = default;
Viewport(Viewport&&) = default;
Viewport& operator=(const Viewport&) & = default;
Viewport& operator=(Viewport&&) & = default;

View file

@ -11,11 +11,6 @@ Viewport::Viewport(const SMALL_RECT sr) noexcept :
{
}
Viewport::Viewport(const Viewport& other) noexcept :
_sr(other._sr)
{
}
Viewport Viewport::Empty() noexcept
{
return Viewport();