Compare commits
5 commits
main
...
dev/duhowe
Author | SHA1 | Date | |
---|---|---|---|
17068f4460 | |||
227ce8ff20 | |||
99d9bac51d | |||
97a2dc6878 | |||
abf66b2ff8 |
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -43,7 +43,6 @@ SOURCES= \
|
|||
..\textBufferTextIterator.cpp \
|
||||
..\CharRow.cpp \
|
||||
..\CharRowCell.cpp \
|
||||
..\CharRowCellReference.cpp \
|
||||
..\UnicodeStorage.cpp \
|
||||
..\search.cpp \
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in a new issue