[A11y] Treat last character as 'end of buffer' (#11122)
## Summary of the Pull Request Updates our `UiaTextRange` to no longer treat the end of the buffer as the "document end". Instead, we consider the "document end" to be the line beneath the cursor or last legible character (whichever is further down). In the event where the last legible character is on the last line of the buffer, we use the "end exclusive" position (left-most point on a line one past the end of the buffer). When movement of any kind occurs, we clamp each endpoint to the document end. Since the document end is an actual spot in the buffer (most of the time), this should improve stability because we shouldn't be pointing out-of-bounds anymore. The biggest benefit is that this significantly improves the performance of word navigation because screen readers no longer have to take into account the whitespace following the end of the prompt. Word navigation tests were added to the `TestTableWriter` (see #10886). 24 of the 85 tests were failing, however, they don't seem to interact with the document end, so I've marked them as skip and will fix them in a follow-up. This PR is large enough as-is, so I'm hoping I can take time in the follow-up to clean some things on the side (aka `preventBoundary` and `allowBottomExclusive` being used interchangeably). ## References #7000 - Epic Closes #6986 Closes #10925 ## Validation Steps Performed - [X] Tests pass - [X] @codeofdusk has been personally testing this build (and others)
This commit is contained in:
parent
4793541c90
commit
d08afc4e88
3
.github/actions/spelling/allow/allow.txt
vendored
3
.github/actions/spelling/allow/allow.txt
vendored
|
@ -35,6 +35,9 @@ liga
|
||||||
lje
|
lje
|
||||||
locl
|
locl
|
||||||
lorem
|
lorem
|
||||||
|
Llast
|
||||||
|
Lmid
|
||||||
|
Lorigin
|
||||||
maxed
|
maxed
|
||||||
mkmk
|
mkmk
|
||||||
mru
|
mru
|
||||||
|
|
|
@ -1057,9 +1057,10 @@ const DelimiterClass TextBuffer::_GetDelimiterClassAt(const COORD pos, const std
|
||||||
// - accessibilityMode - when enabled, we continue expanding left until we are at the beginning of a readable word.
|
// - accessibilityMode - when enabled, we continue expanding left until we are at the beginning of a readable word.
|
||||||
// Otherwise, expand left until a character of a new delimiter class is found
|
// Otherwise, expand left until a character of a new delimiter class is found
|
||||||
// (or a row boundary is encountered)
|
// (or a row boundary is encountered)
|
||||||
|
// - limitOptional - (optional) the last possible position in the buffer that can be explored. This can be used to improve performance.
|
||||||
// Return Value:
|
// Return Value:
|
||||||
// - The COORD for the first character on the "word" (inclusive)
|
// - The COORD for the first character on the "word" (inclusive)
|
||||||
const COORD TextBuffer::GetWordStart(const COORD target, const std::wstring_view wordDelimiters, bool accessibilityMode) const
|
const COORD TextBuffer::GetWordStart(const COORD target, const std::wstring_view wordDelimiters, bool accessibilityMode, std::optional<til::point> limitOptional) const
|
||||||
{
|
{
|
||||||
// Consider a buffer with this text in it:
|
// Consider a buffer with this text in it:
|
||||||
// " word other "
|
// " word other "
|
||||||
|
@ -1072,10 +1073,9 @@ const COORD TextBuffer::GetWordStart(const COORD target, const std::wstring_view
|
||||||
// NOTE: the start anchor (this one) is inclusive, whereas the end anchor (GetWordEnd) is exclusive
|
// NOTE: the start anchor (this one) is inclusive, whereas the end anchor (GetWordEnd) is exclusive
|
||||||
|
|
||||||
#pragma warning(suppress : 26496)
|
#pragma warning(suppress : 26496)
|
||||||
// GH#7664: Treat EndExclusive as EndInclusive so
|
|
||||||
// that it actually points to a space in the buffer
|
|
||||||
auto copy{ target };
|
auto copy{ target };
|
||||||
const auto bufferSize{ GetSize() };
|
const auto bufferSize{ GetSize() };
|
||||||
|
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
|
||||||
if (target == bufferSize.Origin())
|
if (target == bufferSize.Origin())
|
||||||
{
|
{
|
||||||
// can't expand left
|
// can't expand left
|
||||||
|
@ -1083,9 +1083,15 @@ const COORD TextBuffer::GetWordStart(const COORD target, const std::wstring_view
|
||||||
}
|
}
|
||||||
else if (target == bufferSize.EndExclusive())
|
else if (target == bufferSize.EndExclusive())
|
||||||
{
|
{
|
||||||
// treat EndExclusive as EndInclusive
|
// GH#7664: Treat EndExclusive as EndInclusive so
|
||||||
|
// that it actually points to a space in the buffer
|
||||||
copy = { bufferSize.RightInclusive(), bufferSize.BottomInclusive() };
|
copy = { bufferSize.RightInclusive(), bufferSize.BottomInclusive() };
|
||||||
}
|
}
|
||||||
|
else if (bufferSize.CompareInBounds(target, limit, true) >= 0)
|
||||||
|
{
|
||||||
|
// if at/past the limit --> clamp to limit
|
||||||
|
copy = *limitOptional;
|
||||||
|
}
|
||||||
|
|
||||||
if (accessibilityMode)
|
if (accessibilityMode)
|
||||||
{
|
{
|
||||||
|
@ -1179,9 +1185,10 @@ const COORD TextBuffer::_GetWordStartForSelection(const COORD target, const std:
|
||||||
// - accessibilityMode - when enabled, we continue expanding right until we are at the beginning of the next READABLE word
|
// - accessibilityMode - when enabled, we continue expanding right until we are at the beginning of the next READABLE word
|
||||||
// Otherwise, expand right until a character of a new delimiter class is found
|
// Otherwise, expand right until a character of a new delimiter class is found
|
||||||
// (or a row boundary is encountered)
|
// (or a row boundary is encountered)
|
||||||
|
// - limitOptional - (optional) the last possible position in the buffer that can be explored. This can be used to improve performance.
|
||||||
// Return Value:
|
// Return Value:
|
||||||
// - The COORD for the last character on the "word" (inclusive)
|
// - The COORD for the last character on the "word" (inclusive)
|
||||||
const COORD TextBuffer::GetWordEnd(const COORD target, const std::wstring_view wordDelimiters, bool accessibilityMode) const
|
const COORD TextBuffer::GetWordEnd(const COORD target, const std::wstring_view wordDelimiters, bool accessibilityMode, std::optional<til::point> limitOptional) const
|
||||||
{
|
{
|
||||||
// Consider a buffer with this text in it:
|
// Consider a buffer with this text in it:
|
||||||
// " word other "
|
// " word other "
|
||||||
|
@ -1193,16 +1200,17 @@ const COORD TextBuffer::GetWordEnd(const COORD target, const std::wstring_view w
|
||||||
// so the words in the example include ["word ", "other "]
|
// so the words in the example include ["word ", "other "]
|
||||||
// NOTE: the end anchor (this one) is exclusive, whereas the start anchor (GetWordStart) is inclusive
|
// NOTE: the end anchor (this one) is exclusive, whereas the start anchor (GetWordStart) is inclusive
|
||||||
|
|
||||||
// Already at the end. Can't move forward.
|
// Already at/past the limit. Can't move forward.
|
||||||
if (target == GetSize().EndExclusive())
|
const auto bufferSize{ GetSize() };
|
||||||
|
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
|
||||||
|
if (bufferSize.CompareInBounds(target, limit, true) >= 0)
|
||||||
{
|
{
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (accessibilityMode)
|
if (accessibilityMode)
|
||||||
{
|
{
|
||||||
const auto lastCharPos{ GetLastNonSpaceCharacter() };
|
return _GetWordEndForAccessibility(target, wordDelimiters, limit);
|
||||||
return _GetWordEndForAccessibility(target, wordDelimiters, lastCharPos);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1215,44 +1223,46 @@ const COORD TextBuffer::GetWordEnd(const COORD target, const std::wstring_view w
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// - target - a COORD on the word you are currently on
|
// - target - a COORD on the word you are currently on
|
||||||
// - wordDelimiters - what characters are we considering for the separation of words
|
// - wordDelimiters - what characters are we considering for the separation of words
|
||||||
// - lastCharPos - the position of the last nonspace character in the text buffer (to improve performance)
|
// - limit - the last "valid" position in the text buffer (to improve performance)
|
||||||
// Return Value:
|
// Return Value:
|
||||||
// - The COORD for the first character of the next readable "word". If no next word, return one past the end of the buffer
|
// - The COORD for the first character of the next readable "word". If no next word, return one past the end of the buffer
|
||||||
const COORD TextBuffer::_GetWordEndForAccessibility(const COORD target, const std::wstring_view wordDelimiters, const COORD lastCharPos) const
|
const COORD TextBuffer::_GetWordEndForAccessibility(const COORD target, const std::wstring_view wordDelimiters, const COORD limit) const
|
||||||
{
|
{
|
||||||
const auto bufferSize = GetSize();
|
const auto bufferSize{ GetSize() };
|
||||||
COORD result = target;
|
COORD result{ target };
|
||||||
|
|
||||||
// Check if we're already on/past the last RegularChar
|
if (bufferSize.CompareInBounds(target, limit, true) >= 0)
|
||||||
if (bufferSize.CompareInBounds(result, lastCharPos, true) >= 0)
|
|
||||||
{
|
{
|
||||||
return bufferSize.EndExclusive();
|
// if we're already on/past the last RegularChar,
|
||||||
|
// clamp result to that position
|
||||||
|
result = limit;
|
||||||
|
|
||||||
|
// make the result exclusive
|
||||||
|
bufferSize.IncrementInBounds(result, true);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
// ignore right boundary. Continue through readable text found
|
|
||||||
while (_GetDelimiterClassAt(result, wordDelimiters) == DelimiterClass::RegularChar)
|
|
||||||
{
|
{
|
||||||
if (!bufferSize.IncrementInBounds(result, true))
|
auto iter{ GetCellDataAt(result, bufferSize) };
|
||||||
|
while (iter && iter.Pos() != limit && _GetDelimiterClassAt(iter.Pos(), wordDelimiters) == DelimiterClass::RegularChar)
|
||||||
{
|
{
|
||||||
break;
|
// Iterate through readable text
|
||||||
|
++iter;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// we are already on/past the last RegularChar
|
while (iter && iter.Pos() != limit && _GetDelimiterClassAt(iter.Pos(), wordDelimiters) != DelimiterClass::RegularChar)
|
||||||
if (bufferSize.CompareInBounds(result, lastCharPos, true) >= 0)
|
|
||||||
{
|
|
||||||
return bufferSize.EndExclusive();
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure we expand to the beginning of the NEXT word
|
|
||||||
while (_GetDelimiterClassAt(result, wordDelimiters) != DelimiterClass::RegularChar)
|
|
||||||
{
|
|
||||||
if (!bufferSize.IncrementInBounds(result, true))
|
|
||||||
{
|
{
|
||||||
// we are at the EndInclusive COORD
|
// expand to the beginning of the NEXT word
|
||||||
// this signifies that we must include the last char in the buffer
|
++iter;
|
||||||
// but the position of the COORD points to nothing
|
}
|
||||||
break;
|
|
||||||
|
result = iter.Pos();
|
||||||
|
|
||||||
|
// Special case: we tried to move one past the end of the buffer,
|
||||||
|
// but iter prevented that (because that pos doesn't exist).
|
||||||
|
// Manually increment onto the EndExclusive point.
|
||||||
|
if (!iter)
|
||||||
|
{
|
||||||
|
bufferSize.IncrementInBounds(result, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1345,18 +1355,20 @@ void TextBuffer::_PruneHyperlinks()
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// - pos - a COORD on the word you are currently on
|
// - pos - a COORD on the word you are currently on
|
||||||
// - wordDelimiters - what characters are we considering for the separation of words
|
// - wordDelimiters - what characters are we considering for the separation of words
|
||||||
// - lastCharPos - the position of the last nonspace character in the text buffer (to improve performance)
|
// - limitOptional - (optional) the last possible position in the buffer that can be explored. This can be used to improve performance.
|
||||||
// Return Value:
|
// Return Value:
|
||||||
// - true, if successfully updated pos. False, if we are unable to move (usually due to a buffer boundary)
|
// - true, if successfully updated pos. False, if we are unable to move (usually due to a buffer boundary)
|
||||||
// - pos - The COORD for the first character on the "word" (inclusive)
|
// - pos - The COORD for the first character on the "word" (inclusive)
|
||||||
bool TextBuffer::MoveToNextWord(COORD& pos, const std::wstring_view wordDelimiters, COORD lastCharPos) const
|
bool TextBuffer::MoveToNextWord(COORD& pos, const std::wstring_view wordDelimiters, std::optional<til::point> limitOptional) const
|
||||||
{
|
{
|
||||||
// move to the beginning of the next word
|
// move to the beginning of the next word
|
||||||
// NOTE: _GetWordEnd...() returns the exclusive position of the "end of the word"
|
// NOTE: _GetWordEnd...() returns the exclusive position of the "end of the word"
|
||||||
// This is also the inclusive start of the next word.
|
// This is also the inclusive start of the next word.
|
||||||
auto copy{ _GetWordEndForAccessibility(pos, wordDelimiters, lastCharPos) };
|
const auto bufferSize{ GetSize() };
|
||||||
|
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
|
||||||
|
const auto copy{ _GetWordEndForAccessibility(pos, wordDelimiters, limit) };
|
||||||
|
|
||||||
if (copy == GetSize().EndExclusive())
|
if (bufferSize.CompareInBounds(copy, limit, true) >= 0)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1393,19 +1405,23 @@ bool TextBuffer::MoveToPreviousWord(COORD& pos, std::wstring_view wordDelimiters
|
||||||
// - Update pos to be the beginning of the current glyph/character. This is used for accessibility
|
// - Update pos to be the beginning of the current glyph/character. This is used for accessibility
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// - pos - a COORD on the word you are currently on
|
// - pos - a COORD on the word you are currently on
|
||||||
|
// - limitOptional - (optional) the last possible position in the buffer that can be explored. This can be used to improve performance.
|
||||||
// Return Value:
|
// Return Value:
|
||||||
// - pos - The COORD for the first cell of the current glyph (inclusive)
|
// - pos - The COORD for the first cell of the current glyph (inclusive)
|
||||||
const til::point TextBuffer::GetGlyphStart(const til::point pos) const
|
const til::point TextBuffer::GetGlyphStart(const til::point pos, std::optional<til::point> limitOptional) const
|
||||||
{
|
{
|
||||||
COORD resultPos = pos;
|
COORD resultPos = pos;
|
||||||
const auto bufferSize = GetSize();
|
const auto bufferSize = GetSize();
|
||||||
|
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
|
||||||
|
|
||||||
if (resultPos == bufferSize.EndExclusive())
|
// Clamp pos to limit
|
||||||
|
if (bufferSize.CompareInBounds(resultPos, limit, true) > 0)
|
||||||
{
|
{
|
||||||
bufferSize.DecrementInBounds(resultPos, true);
|
resultPos = limit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resultPos != bufferSize.EndExclusive() && GetCellDataAt(resultPos)->DbcsAttr().IsTrailing())
|
// limit is exclusive, so we need to move back to be within valid bounds
|
||||||
|
if (resultPos != limit && GetCellDataAt(resultPos)->DbcsAttr().IsTrailing())
|
||||||
{
|
{
|
||||||
bufferSize.DecrementInBounds(resultPos, true);
|
bufferSize.DecrementInBounds(resultPos, true);
|
||||||
}
|
}
|
||||||
|
@ -1419,12 +1435,19 @@ const til::point TextBuffer::GetGlyphStart(const til::point pos) const
|
||||||
// - pos - a COORD on the word you are currently on
|
// - pos - a COORD on the word you are currently on
|
||||||
// Return Value:
|
// Return Value:
|
||||||
// - pos - The COORD for the last cell of the current glyph (exclusive)
|
// - pos - The COORD for the last cell of the current glyph (exclusive)
|
||||||
const til::point TextBuffer::GetGlyphEnd(const til::point pos) const
|
const til::point TextBuffer::GetGlyphEnd(const til::point pos, std::optional<til::point> limitOptional) const
|
||||||
{
|
{
|
||||||
COORD resultPos = pos;
|
COORD resultPos = pos;
|
||||||
|
|
||||||
const auto bufferSize = GetSize();
|
const auto bufferSize = GetSize();
|
||||||
if (resultPos != bufferSize.EndExclusive() && GetCellDataAt(resultPos)->DbcsAttr().IsLeading())
|
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
|
||||||
|
|
||||||
|
// Clamp pos to limit
|
||||||
|
if (bufferSize.CompareInBounds(resultPos, limit, true) > 0)
|
||||||
|
{
|
||||||
|
resultPos = limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resultPos != limit && GetCellDataAt(resultPos)->DbcsAttr().IsLeading())
|
||||||
{
|
{
|
||||||
bufferSize.IncrementInBounds(resultPos, true);
|
bufferSize.IncrementInBounds(resultPos, true);
|
||||||
}
|
}
|
||||||
|
@ -1438,29 +1461,43 @@ const til::point TextBuffer::GetGlyphEnd(const til::point pos) const
|
||||||
// - Update pos to be the beginning of the next glyph/character. This is used for accessibility
|
// - Update pos to be the beginning of the next glyph/character. This is used for accessibility
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// - pos - a COORD on the word you are currently on
|
// - pos - a COORD on the word you are currently on
|
||||||
// - allowBottomExclusive - allow the nonexistent end-of-buffer cell to be encountered
|
// - allowExclusiveEnd - allow result to be the exclusive limit (one past limit)
|
||||||
|
// - limit - boundaries for the iterator to operate within
|
||||||
// Return Value:
|
// Return Value:
|
||||||
// - true, if successfully updated pos. False, if we are unable to move (usually due to a buffer boundary)
|
// - true, if successfully updated pos. False, if we are unable to move (usually due to a buffer boundary)
|
||||||
// - pos - The COORD for the first cell of the current glyph (inclusive)
|
// - pos - The COORD for the first cell of the current glyph (inclusive)
|
||||||
bool TextBuffer::MoveToNextGlyph(til::point& pos, bool allowBottomExclusive) const
|
bool TextBuffer::MoveToNextGlyph(til::point& pos, bool allowExclusiveEnd, std::optional<til::point> limitOptional) const
|
||||||
{
|
{
|
||||||
COORD resultPos = pos;
|
|
||||||
const auto bufferSize = GetSize();
|
const auto bufferSize = GetSize();
|
||||||
|
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
|
||||||
|
|
||||||
if (resultPos == GetSize().EndExclusive())
|
const auto distanceToLimit{ bufferSize.CompareInBounds(pos, limit, true) };
|
||||||
|
if (distanceToLimit >= 0)
|
||||||
{
|
{
|
||||||
// we're already at the end
|
// Corner Case: we're on/past the limit
|
||||||
|
// Clamp us to the limit
|
||||||
|
pos = limit;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (!allowExclusiveEnd && distanceToLimit == -1)
|
||||||
|
{
|
||||||
|
// Corner Case: we're just before the limit
|
||||||
|
// and we are not allowed onto the exclusive end.
|
||||||
|
// Fail to move.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// try to move. If we can't, we're done.
|
// Try to move forward, but if we hit the buffer boundary, we fail to move.
|
||||||
const bool success = bufferSize.IncrementInBounds(resultPos, allowBottomExclusive);
|
auto iter{ GetCellDataAt(pos, bufferSize) };
|
||||||
if (resultPos != bufferSize.EndExclusive() && GetCellDataAt(resultPos)->DbcsAttr().IsTrailing())
|
const bool success{ ++iter };
|
||||||
|
|
||||||
|
// Move again if we're on a wide glyph
|
||||||
|
if (success && iter->DbcsAttr().IsTrailing())
|
||||||
{
|
{
|
||||||
bufferSize.IncrementInBounds(resultPos, allowBottomExclusive);
|
++iter;
|
||||||
}
|
}
|
||||||
|
|
||||||
pos = resultPos;
|
pos = iter.Pos();
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1471,12 +1508,21 @@ bool TextBuffer::MoveToNextGlyph(til::point& pos, bool allowBottomExclusive) con
|
||||||
// Return Value:
|
// Return Value:
|
||||||
// - true, if successfully updated pos. False, if we are unable to move (usually due to a buffer boundary)
|
// - true, if successfully updated pos. False, if we are unable to move (usually due to a buffer boundary)
|
||||||
// - pos - The COORD for the first cell of the previous glyph (inclusive)
|
// - pos - The COORD for the first cell of the previous glyph (inclusive)
|
||||||
bool TextBuffer::MoveToPreviousGlyph(til::point& pos) const
|
bool TextBuffer::MoveToPreviousGlyph(til::point& pos, std::optional<til::point> limitOptional) const
|
||||||
{
|
{
|
||||||
COORD resultPos = pos;
|
COORD resultPos = pos;
|
||||||
|
const auto bufferSize = GetSize();
|
||||||
|
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
|
||||||
|
|
||||||
|
if (bufferSize.CompareInBounds(pos, limit, true) > 0)
|
||||||
|
{
|
||||||
|
// we're past the end
|
||||||
|
// clamp us to the limit
|
||||||
|
pos = limit;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// try to move. If we can't, we're done.
|
// try to move. If we can't, we're done.
|
||||||
const auto bufferSize = GetSize();
|
|
||||||
const bool success = bufferSize.DecrementInBounds(resultPos, true);
|
const bool success = bufferSize.DecrementInBounds(resultPos, true);
|
||||||
if (resultPos != bufferSize.EndExclusive() && GetCellDataAt(resultPos)->DbcsAttr().IsLeading())
|
if (resultPos != bufferSize.EndExclusive() && GetCellDataAt(resultPos)->DbcsAttr().IsLeading())
|
||||||
{
|
{
|
||||||
|
|
|
@ -141,15 +141,15 @@ public:
|
||||||
|
|
||||||
Microsoft::Console::Render::IRenderTarget& GetRenderTarget() noexcept;
|
Microsoft::Console::Render::IRenderTarget& GetRenderTarget() noexcept;
|
||||||
|
|
||||||
const COORD GetWordStart(const COORD target, const std::wstring_view wordDelimiters, bool accessibilityMode = false) const;
|
const COORD GetWordStart(const COORD target, const std::wstring_view wordDelimiters, bool accessibilityMode = false, std::optional<til::point> limitOptional = std::nullopt) const;
|
||||||
const COORD GetWordEnd(const COORD target, const std::wstring_view wordDelimiters, bool accessibilityMode = false) const;
|
const COORD GetWordEnd(const COORD target, const std::wstring_view wordDelimiters, bool accessibilityMode = false, std::optional<til::point> limitOptional = std::nullopt) const;
|
||||||
bool MoveToNextWord(COORD& pos, const std::wstring_view wordDelimiters, COORD lastCharPos) const;
|
bool MoveToNextWord(COORD& pos, const std::wstring_view wordDelimiters, std::optional<til::point> limitOptional = std::nullopt) const;
|
||||||
bool MoveToPreviousWord(COORD& pos, const std::wstring_view wordDelimiters) const;
|
bool MoveToPreviousWord(COORD& pos, const std::wstring_view wordDelimiters) const;
|
||||||
|
|
||||||
const til::point GetGlyphStart(const til::point pos) const;
|
const til::point GetGlyphStart(const til::point pos, std::optional<til::point> limitOptional = std::nullopt) const;
|
||||||
const til::point GetGlyphEnd(const til::point pos) const;
|
const til::point GetGlyphEnd(const til::point pos, std::optional<til::point> limitOptional = std::nullopt) const;
|
||||||
bool MoveToNextGlyph(til::point& pos, bool allowBottomExclusive = false) const;
|
bool MoveToNextGlyph(til::point& pos, bool allowBottomExclusive = false, std::optional<til::point> limitOptional = std::nullopt) const;
|
||||||
bool MoveToPreviousGlyph(til::point& pos) const;
|
bool MoveToPreviousGlyph(til::point& pos, std::optional<til::point> limitOptional = std::nullopt) const;
|
||||||
|
|
||||||
const std::vector<SMALL_RECT> GetTextRects(COORD start, COORD end, bool blockSelection, bool bufferCoordinates) const;
|
const std::vector<SMALL_RECT> GetTextRects(COORD start, COORD end, bool blockSelection, bool bufferCoordinates) const;
|
||||||
|
|
||||||
|
@ -242,7 +242,7 @@ private:
|
||||||
const DelimiterClass _GetDelimiterClassAt(const COORD pos, const std::wstring_view wordDelimiters) const;
|
const DelimiterClass _GetDelimiterClassAt(const COORD pos, const std::wstring_view wordDelimiters) const;
|
||||||
const COORD _GetWordStartForAccessibility(const COORD target, const std::wstring_view wordDelimiters) const;
|
const COORD _GetWordStartForAccessibility(const COORD target, const std::wstring_view wordDelimiters) const;
|
||||||
const COORD _GetWordStartForSelection(const COORD target, const std::wstring_view wordDelimiters) const;
|
const COORD _GetWordStartForSelection(const COORD target, const std::wstring_view wordDelimiters) const;
|
||||||
const COORD _GetWordEndForAccessibility(const COORD target, const std::wstring_view wordDelimiters, const COORD lastCharPos) const;
|
const COORD _GetWordEndForAccessibility(const COORD target, const std::wstring_view wordDelimiters, const COORD limit) const;
|
||||||
const COORD _GetWordEndForSelection(const COORD target, const std::wstring_view wordDelimiters) const;
|
const COORD _GetWordEndForSelection(const COORD target, const std::wstring_view wordDelimiters) const;
|
||||||
|
|
||||||
void _PruneHyperlinks();
|
void _PruneHyperlinks();
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -349,14 +349,29 @@ class UiaTextRangeTests
|
||||||
_pTextBuffer = &_pScreenInfo->GetTextBuffer();
|
_pTextBuffer = &_pScreenInfo->GetTextBuffer();
|
||||||
_pUiaData = &gci.renderData;
|
_pUiaData = &gci.renderData;
|
||||||
|
|
||||||
// fill text buffer with text
|
// GH#6986: document end now limits the navigation to be
|
||||||
for (UINT i = 0; i < _pTextBuffer->TotalRowCount(); ++i)
|
// within the document end bounds _as opposed to_ the buffer bounds.
|
||||||
|
// As a result, let's populate the buffer partially to define a document end.
|
||||||
|
// Additionally, add spaces to create "words" in the buffer.
|
||||||
|
|
||||||
|
// LOAD BEARING: make sure we fill it halfway so that we can reuse most of
|
||||||
|
// the variables from the generated tests.
|
||||||
|
|
||||||
|
// fill first half of text buffer with text
|
||||||
|
for (UINT i = 0; i < _pTextBuffer->TotalRowCount() / 2; ++i)
|
||||||
{
|
{
|
||||||
ROW& row = _pTextBuffer->GetRowByOffset(i);
|
ROW& row = _pTextBuffer->GetRowByOffset(i);
|
||||||
auto& charRow = row.GetCharRow();
|
auto& charRow = row.GetCharRow();
|
||||||
for (auto& cell : charRow)
|
for (auto& cell : charRow)
|
||||||
{
|
{
|
||||||
cell.Char() = L' ';
|
if (i % 2 == 0)
|
||||||
|
{
|
||||||
|
cell.Char() = L' ';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cell.Char() = L'X';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -719,9 +734,13 @@ class UiaTextRangeTests
|
||||||
|
|
||||||
TEST_METHOD(CanMoveByCharacter)
|
TEST_METHOD(CanMoveByCharacter)
|
||||||
{
|
{
|
||||||
const SHORT lastColumnIndex = _pScreenInfo->GetBufferSize().Width() - 1;
|
const SHORT lastColumnIndex = _pScreenInfo->GetBufferSize().RightInclusive();
|
||||||
const SHORT bottomRow = gsl::narrow<SHORT>(_pTextBuffer->TotalRowCount() - 1);
|
const SHORT bottomRow = gsl::narrow<SHORT>(_pTextBuffer->TotalRowCount() - 1);
|
||||||
|
|
||||||
|
// GH#6986: This is used as the "end of the buffer" to help screen readers run faster
|
||||||
|
// instead of parsing through thousands of empty lines of text.
|
||||||
|
const COORD documentEnd{ _pTextBuffer->GetSize().Left(), _pTextBuffer->GetLastNonSpaceCharacter().Y + 1 };
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
const std::vector<MoveTest> testData
|
const std::vector<MoveTest> testData
|
||||||
{
|
{
|
||||||
|
@ -749,6 +768,18 @@ class UiaTextRangeTests
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
MoveTest{
|
||||||
|
L"can't move past the end of the 'document'",
|
||||||
|
documentEnd,
|
||||||
|
documentEnd,
|
||||||
|
5,
|
||||||
|
{
|
||||||
|
0,
|
||||||
|
documentEnd,
|
||||||
|
documentEnd,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
MoveTest{
|
MoveTest{
|
||||||
L"can move to a new row when necessary when moving forward",
|
L"can move to a new row when necessary when moving forward",
|
||||||
{ lastColumnIndex, 0 },
|
{ lastColumnIndex, 0 },
|
||||||
|
@ -782,7 +813,7 @@ class UiaTextRangeTests
|
||||||
int amountMoved;
|
int amountMoved;
|
||||||
|
|
||||||
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr, _pUiaData, &_dummyProvider, test.start, test.end));
|
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr, _pUiaData, &_dummyProvider, test.start, test.end));
|
||||||
utr->Move(TextUnit::TextUnit_Character, test.moveAmt, &amountMoved);
|
THROW_IF_FAILED(utr->Move(TextUnit::TextUnit_Character, test.moveAmt, &amountMoved));
|
||||||
|
|
||||||
VERIFY_ARE_EQUAL(test.expected.moveAmt, amountMoved);
|
VERIFY_ARE_EQUAL(test.expected.moveAmt, amountMoved);
|
||||||
VERIFY_ARE_EQUAL(test.expected.start, utr->_start);
|
VERIFY_ARE_EQUAL(test.expected.start, utr->_start);
|
||||||
|
@ -795,6 +826,10 @@ class UiaTextRangeTests
|
||||||
const SHORT lastColumnIndex = _pScreenInfo->GetBufferSize().Width() - 1;
|
const SHORT lastColumnIndex = _pScreenInfo->GetBufferSize().Width() - 1;
|
||||||
const SHORT bottomRow = gsl::narrow<SHORT>(_pTextBuffer->TotalRowCount() - 1);
|
const SHORT bottomRow = gsl::narrow<SHORT>(_pTextBuffer->TotalRowCount() - 1);
|
||||||
|
|
||||||
|
// GH#6986: This is used as the "end of the buffer" to help screen readers run faster
|
||||||
|
// instead of parsing through thousands of empty lines of text.
|
||||||
|
const COORD documentEnd{ _pTextBuffer->GetSize().Left(), _pTextBuffer->GetLastNonSpaceCharacter().Y + 1 };
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
const std::vector<MoveTest> testData
|
const std::vector<MoveTest> testData
|
||||||
{
|
{
|
||||||
|
@ -810,15 +845,27 @@ class UiaTextRangeTests
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
MoveTest{
|
||||||
|
L"can't move past the end of the 'document'",
|
||||||
|
documentEnd,
|
||||||
|
documentEnd,
|
||||||
|
5,
|
||||||
|
{
|
||||||
|
0,
|
||||||
|
documentEnd,
|
||||||
|
documentEnd,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
MoveTest{
|
MoveTest{
|
||||||
L"can move backward from bottom row",
|
L"can move backward from bottom row",
|
||||||
{0, bottomRow},
|
{0, documentEnd.Y},
|
||||||
{lastColumnIndex, bottomRow},
|
{lastColumnIndex, documentEnd.Y},
|
||||||
-3,
|
-3,
|
||||||
{
|
{
|
||||||
-3,
|
-3,
|
||||||
{0, bottomRow - 3},
|
{0, base::ClampSub(documentEnd.Y, 3)},
|
||||||
{0, bottomRow - 2}
|
{0, base::ClampSub(documentEnd.Y, 3)}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -868,6 +915,10 @@ class UiaTextRangeTests
|
||||||
const SHORT lastColumnIndex = _pScreenInfo->GetBufferSize().Width() - 1;
|
const SHORT lastColumnIndex = _pScreenInfo->GetBufferSize().Width() - 1;
|
||||||
const SHORT bottomRow = static_cast<SHORT>(_pTextBuffer->TotalRowCount() - 1);
|
const SHORT bottomRow = static_cast<SHORT>(_pTextBuffer->TotalRowCount() - 1);
|
||||||
|
|
||||||
|
// GH#6986: This is used as the "end of the buffer" to help screen readers run faster
|
||||||
|
// instead of parsing through thousands of empty lines of text.
|
||||||
|
const COORD documentEnd{ _pTextBuffer->GetSize().RightInclusive(), _pTextBuffer->GetLastNonSpaceCharacter().Y };
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
const std::vector<MoveEndpointTest> testData
|
const std::vector<MoveEndpointTest> testData
|
||||||
{
|
{
|
||||||
|
@ -910,6 +961,19 @@ class UiaTextRangeTests
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
MoveEndpointTest{
|
||||||
|
L"can't move _end past the end of the document",
|
||||||
|
{0, 0},
|
||||||
|
documentEnd,
|
||||||
|
5,
|
||||||
|
TextPatternRangeEndpoint_End,
|
||||||
|
{
|
||||||
|
1,
|
||||||
|
{0,0},
|
||||||
|
{0, base::ClampAdd(documentEnd.Y,1)}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
MoveEndpointTest{
|
MoveEndpointTest{
|
||||||
L"_start follows _end when passed during movement",
|
L"_start follows _end when passed during movement",
|
||||||
{5, 0},
|
{5, 0},
|
||||||
|
@ -925,40 +989,40 @@ class UiaTextRangeTests
|
||||||
|
|
||||||
MoveEndpointTest{
|
MoveEndpointTest{
|
||||||
L"can't move _end past the beginning of the document when _end is positioned at the end",
|
L"can't move _end past the beginning of the document when _end is positioned at the end",
|
||||||
{0, bottomRow},
|
{0, documentEnd.Y},
|
||||||
{0, bottomRow+1},
|
{0, base::ClampAdd(documentEnd.Y,1)},
|
||||||
1,
|
1,
|
||||||
TextPatternRangeEndpoint_End,
|
TextPatternRangeEndpoint_End,
|
||||||
{
|
{
|
||||||
0,
|
0,
|
||||||
{0, bottomRow},
|
{0, documentEnd.Y},
|
||||||
{0, bottomRow+1},
|
{0, base::ClampAdd(documentEnd.Y,1)},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
MoveEndpointTest{
|
MoveEndpointTest{
|
||||||
L"can partially move _end to the end of the document when it is closer than the move count requested",
|
L"can partially move _end to the end of the document when it is closer than the move count requested",
|
||||||
{0, 0},
|
{0, 0},
|
||||||
{lastColumnIndex - 3, bottomRow},
|
{base::ClampSub(lastColumnIndex, 3), documentEnd.Y},
|
||||||
5,
|
5,
|
||||||
TextPatternRangeEndpoint_End,
|
TextPatternRangeEndpoint_End,
|
||||||
{
|
{
|
||||||
4,
|
4,
|
||||||
{0, 0},
|
{0, 0},
|
||||||
{0, bottomRow+1},
|
{0, base::ClampAdd(documentEnd.Y,1)},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
MoveEndpointTest{
|
MoveEndpointTest{
|
||||||
L"can't move _start past the end of the document",
|
L"can't move _start past the end of the document",
|
||||||
{lastColumnIndex - 4, bottomRow},
|
{base::ClampSub(lastColumnIndex, 4), documentEnd.Y},
|
||||||
{0, bottomRow+1},
|
{0, base::ClampAdd(documentEnd.Y,1)},
|
||||||
5,
|
5,
|
||||||
TextPatternRangeEndpoint_Start,
|
TextPatternRangeEndpoint_Start,
|
||||||
{
|
{
|
||||||
5,
|
5,
|
||||||
{0, bottomRow+1},
|
{0, base::ClampAdd(documentEnd.Y,1)},
|
||||||
{0, bottomRow+1},
|
{0, base::ClampAdd(documentEnd.Y,1)},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -997,6 +1061,10 @@ class UiaTextRangeTests
|
||||||
const SHORT lastColumnIndex = _pScreenInfo->GetBufferSize().Width() - 1;
|
const SHORT lastColumnIndex = _pScreenInfo->GetBufferSize().Width() - 1;
|
||||||
const SHORT bottomRow = gsl::narrow<SHORT>(_pTextBuffer->TotalRowCount() - 1);
|
const SHORT bottomRow = gsl::narrow<SHORT>(_pTextBuffer->TotalRowCount() - 1);
|
||||||
|
|
||||||
|
// GH#6986: This is used as the "end of the buffer" to help screen readers run faster
|
||||||
|
// instead of parsing through thousands of empty lines of text.
|
||||||
|
const COORD documentEnd{ _pTextBuffer->GetSize().Left(), _pTextBuffer->GetLastNonSpaceCharacter().Y + 1 };
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
const std::vector<MoveEndpointTest> testData
|
const std::vector<MoveEndpointTest> testData
|
||||||
{
|
{
|
||||||
|
@ -1067,36 +1135,36 @@ class UiaTextRangeTests
|
||||||
},
|
},
|
||||||
|
|
||||||
MoveEndpointTest{
|
MoveEndpointTest{
|
||||||
L"can move _end forwards when it's on the bottom row",
|
L"can't move _end forwards when it's on the bottom row (past doc end)",
|
||||||
{0, 0},
|
{0, 0},
|
||||||
{lastColumnIndex - 3, bottomRow},
|
{lastColumnIndex - 3, bottomRow},
|
||||||
1,
|
1,
|
||||||
TextPatternRangeEndpoint_End,
|
TextPatternRangeEndpoint_End,
|
||||||
1,
|
0,
|
||||||
{0, 0},
|
{0, 0},
|
||||||
{0, bottomRow+1}
|
documentEnd
|
||||||
},
|
},
|
||||||
|
|
||||||
MoveEndpointTest{
|
MoveEndpointTest{
|
||||||
L"can't move _end forwards when it's at the end of the document already",
|
L"can't move _end forwards when it's at the end of the buffer already (past doc end)",
|
||||||
{0, 0},
|
{0, 0},
|
||||||
{0, bottomRow+1},
|
{0, bottomRow+1},
|
||||||
1,
|
1,
|
||||||
TextPatternRangeEndpoint_End,
|
TextPatternRangeEndpoint_End,
|
||||||
0,
|
0,
|
||||||
{0, 0},
|
{0, 0},
|
||||||
{0, bottomRow+1}
|
documentEnd
|
||||||
},
|
},
|
||||||
|
|
||||||
MoveEndpointTest{
|
MoveEndpointTest{
|
||||||
L"moving _start forward when it's already on the bottom row creates a degenerate range at the document end",
|
L"moving _start forward when it's already on the bottom row (past doc end) creates a degenerate range at the document end",
|
||||||
{0, bottomRow},
|
{0, bottomRow},
|
||||||
{lastColumnIndex, bottomRow},
|
{lastColumnIndex, bottomRow},
|
||||||
1,
|
1,
|
||||||
TextPatternRangeEndpoint_Start,
|
TextPatternRangeEndpoint_Start,
|
||||||
1,
|
0,
|
||||||
{0, bottomRow+1},
|
documentEnd,
|
||||||
{0, bottomRow+1}
|
documentEnd
|
||||||
},
|
},
|
||||||
|
|
||||||
MoveEndpointTest{
|
MoveEndpointTest{
|
||||||
|
@ -1132,6 +1200,10 @@ class UiaTextRangeTests
|
||||||
const SHORT lastColumnIndex = _pScreenInfo->GetBufferSize().Width() - 1;
|
const SHORT lastColumnIndex = _pScreenInfo->GetBufferSize().Width() - 1;
|
||||||
const SHORT bottomRow = gsl::narrow<SHORT>(_pTextBuffer->TotalRowCount() - 1);
|
const SHORT bottomRow = gsl::narrow<SHORT>(_pTextBuffer->TotalRowCount() - 1);
|
||||||
|
|
||||||
|
// GH#6986: This is used as the "end of the buffer" to help screen readers run faster
|
||||||
|
// instead of parsing through thousands of empty lines of text.
|
||||||
|
const COORD documentEnd{ _pTextBuffer->GetSize().Left(), _pTextBuffer->GetLastNonSpaceCharacter().Y + 1 };
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
const std::vector<MoveEndpointTest> testData =
|
const std::vector<MoveEndpointTest> testData =
|
||||||
{
|
{
|
||||||
|
@ -1144,7 +1216,7 @@ class UiaTextRangeTests
|
||||||
{
|
{
|
||||||
1,
|
1,
|
||||||
{0, 4},
|
{0, 4},
|
||||||
{0, bottomRow+1}
|
documentEnd
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1162,7 +1234,7 @@ class UiaTextRangeTests
|
||||||
},
|
},
|
||||||
|
|
||||||
MoveEndpointTest{
|
MoveEndpointTest{
|
||||||
L"can't move _end forward when it's already at the end of the document",
|
L"can't move _end forward when it's already at the end of the buffer (past doc end)",
|
||||||
{3, 2},
|
{3, 2},
|
||||||
{0, bottomRow+1},
|
{0, bottomRow+1},
|
||||||
1,
|
1,
|
||||||
|
@ -1170,7 +1242,7 @@ class UiaTextRangeTests
|
||||||
{
|
{
|
||||||
0,
|
0,
|
||||||
{3, 2},
|
{3, 2},
|
||||||
{0, bottomRow+1}
|
documentEnd
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1208,8 +1280,8 @@ class UiaTextRangeTests
|
||||||
TextPatternRangeEndpoint_Start,
|
TextPatternRangeEndpoint_Start,
|
||||||
{
|
{
|
||||||
1,
|
1,
|
||||||
{0, bottomRow+1},
|
documentEnd,
|
||||||
{0, bottomRow+1}
|
documentEnd
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1235,51 +1307,64 @@ class UiaTextRangeTests
|
||||||
// GH#7664: When attempting to expand to an enclosing unit
|
// GH#7664: When attempting to expand to an enclosing unit
|
||||||
// at the end exclusive, the UTR should refuse to move past
|
// at the end exclusive, the UTR should refuse to move past
|
||||||
// the end.
|
// the end.
|
||||||
|
const auto lastNonspaceCharPos{ _pTextBuffer->GetLastNonSpaceCharacter() };
|
||||||
|
const COORD documentEnd{ 0, lastNonspaceCharPos.Y + 1 };
|
||||||
|
|
||||||
const til::point endInclusive{ bufferEnd };
|
// Iterate over each TextUnit. If we don't support
|
||||||
|
|
||||||
// Iterate over each TextUnit. If the we don't support
|
|
||||||
// the given TextUnit, we're supposed to fallback
|
// the given TextUnit, we're supposed to fallback
|
||||||
// to the last one that was defined anyways.
|
// to the last one that was defined anyways.
|
||||||
|
BEGIN_TEST_METHOD_PROPERTIES()
|
||||||
|
TEST_METHOD_PROPERTY(L"Data:textUnit", L"{0, 1, 2, 3, 4, 5, 6}")
|
||||||
|
END_TEST_METHOD_PROPERTIES();
|
||||||
|
|
||||||
|
int textUnit;
|
||||||
|
VERIFY_SUCCEEDED(TestData::TryGetValue(L"textUnit", textUnit), L"Get textUnit variant");
|
||||||
|
|
||||||
Microsoft::WRL::ComPtr<UiaTextRange> utr;
|
Microsoft::WRL::ComPtr<UiaTextRange> utr;
|
||||||
for (int unit = TextUnit::TextUnit_Character; unit != TextUnit::TextUnit_Document; ++unit)
|
Log::Comment(NoThrowString().Format(L"%s", toString(static_cast<TextUnit>(textUnit))));
|
||||||
{
|
|
||||||
Log::Comment(NoThrowString().Format(L"%s", toString(static_cast<TextUnit>(unit))));
|
|
||||||
|
|
||||||
// Create a degenerate UTR at EndExclusive
|
// Create a degenerate UTR at EndExclusive
|
||||||
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr, _pUiaData, &_dummyProvider, endInclusive, endExclusive));
|
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr, _pUiaData, &_dummyProvider, bufferEnd, endExclusive));
|
||||||
THROW_IF_FAILED(utr->ExpandToEnclosingUnit(static_cast<TextUnit>(unit)));
|
THROW_IF_FAILED(utr->ExpandToEnclosingUnit(static_cast<TextUnit>(textUnit)));
|
||||||
|
|
||||||
VERIFY_ARE_EQUAL(endExclusive, til::point{ utr->_end });
|
VERIFY_ARE_EQUAL(documentEnd, til::point{ utr->_end });
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_METHOD(MovementAtExclusiveEnd)
|
TEST_METHOD(MovementAtExclusiveEnd)
|
||||||
{
|
{
|
||||||
// GH#7663: When attempting to move from end exclusive,
|
// GH#7663: When attempting to move from end exclusive,
|
||||||
// the UTR should refuse to move past the end.
|
// the UTR should refuse to move past the end.
|
||||||
|
|
||||||
const auto lastLineStart{ bufferEndLeft };
|
|
||||||
const auto secondToLastLinePos{ point_offset_by_line(lastLineStart, bufferSize, -1) };
|
|
||||||
const auto secondToLastCharacterPos{ point_offset_by_char(bufferEnd, bufferSize, -1) };
|
|
||||||
const auto endInclusive{ bufferEnd };
|
const auto endInclusive{ bufferEnd };
|
||||||
|
|
||||||
// write "temp" at (2,2)
|
// write "temp" at (2,2)
|
||||||
|
_pTextBuffer->Reset();
|
||||||
const til::point writeTarget{ 2, 2 };
|
const til::point writeTarget{ 2, 2 };
|
||||||
_pTextBuffer->Write({ L"temp" }, writeTarget);
|
_pTextBuffer->Write({ L"temp" }, writeTarget);
|
||||||
|
|
||||||
|
// GH#6986: This is used as the "end of the buffer" to help screen readers run faster
|
||||||
|
// instead of parsing through thousands of empty lines of text.
|
||||||
|
const COORD documentEndInclusive{ base::ClampSub<short, short>(static_cast<short>(bufferSize.right()), 1), _pTextBuffer->GetLastNonSpaceCharacter().Y };
|
||||||
|
const COORD documentEndExclusive{ static_cast<short>(bufferSize.left()), base::ClampAdd(documentEndInclusive.Y, 1) };
|
||||||
|
|
||||||
|
const COORD lastLineStart{ static_cast<short>(bufferSize.left()), documentEndInclusive.Y };
|
||||||
|
const auto secondToLastLinePos{ point_offset_by_line(lastLineStart, bufferSize, -1) };
|
||||||
|
const COORD secondToLastCharacterPos{ documentEndInclusive.X - 1, documentEndInclusive.Y };
|
||||||
|
|
||||||
// Iterate over each TextUnit. If we don't support
|
// Iterate over each TextUnit. If we don't support
|
||||||
// the given TextUnit, we're supposed to fallback
|
// the given TextUnit, we're supposed to fallback
|
||||||
// to the last one that was defined anyways.
|
// to the last one that was defined anyways.
|
||||||
BEGIN_TEST_METHOD_PROPERTIES()
|
BEGIN_TEST_METHOD_PROPERTIES()
|
||||||
TEST_METHOD_PROPERTY(L"Data:textUnit", L"{0, 1, 2, 3, 4, 5, 6}")
|
TEST_METHOD_PROPERTY(L"Data:textUnit", L"{0, 1, 2, 3, 4, 5, 6}")
|
||||||
TEST_METHOD_PROPERTY(L"Data:degenerate", L"{false, true}")
|
TEST_METHOD_PROPERTY(L"Data:degenerate", L"{false, true}")
|
||||||
|
TEST_METHOD_PROPERTY(L"Data:atDocumentEnd", L"{false, true}")
|
||||||
END_TEST_METHOD_PROPERTIES();
|
END_TEST_METHOD_PROPERTIES();
|
||||||
|
|
||||||
int unit;
|
int unit;
|
||||||
bool degenerate;
|
bool degenerate;
|
||||||
|
bool atDocumentEnd;
|
||||||
VERIFY_SUCCEEDED(TestData::TryGetValue(L"textUnit", unit), L"Get TextUnit variant");
|
VERIFY_SUCCEEDED(TestData::TryGetValue(L"textUnit", unit), L"Get TextUnit variant");
|
||||||
VERIFY_SUCCEEDED(TestData::TryGetValue(L"degenerate", degenerate), L"Get degenerate variant");
|
VERIFY_SUCCEEDED(TestData::TryGetValue(L"degenerate", degenerate), L"Get degenerate variant");
|
||||||
|
VERIFY_SUCCEEDED(TestData::TryGetValue(L"atDocumentEnd", atDocumentEnd), L"Get atDocumentEnd variant");
|
||||||
TextUnit textUnit{ static_cast<TextUnit>(unit) };
|
TextUnit textUnit{ static_cast<TextUnit>(unit) };
|
||||||
|
|
||||||
Microsoft::WRL::ComPtr<UiaTextRange> utr;
|
Microsoft::WRL::ComPtr<UiaTextRange> utr;
|
||||||
|
@ -1287,17 +1372,22 @@ class UiaTextRangeTests
|
||||||
Log::Comment(NoThrowString().Format(L"Forward by %s", toString(textUnit)));
|
Log::Comment(NoThrowString().Format(L"Forward by %s", toString(textUnit)));
|
||||||
|
|
||||||
// Create an UTR at EndExclusive
|
// Create an UTR at EndExclusive
|
||||||
|
const auto utrEnd{ atDocumentEnd ? documentEndExclusive : endExclusive };
|
||||||
if (degenerate)
|
if (degenerate)
|
||||||
{
|
{
|
||||||
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr, _pUiaData, &_dummyProvider, endExclusive, endExclusive));
|
// UTR: (exclusive, exclusive) range
|
||||||
|
const auto utrStart{ atDocumentEnd ? documentEndExclusive : endExclusive };
|
||||||
|
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr, _pUiaData, &_dummyProvider, utrStart, utrEnd));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr, _pUiaData, &_dummyProvider, endInclusive, endExclusive));
|
// UTR: (inclusive, exclusive) range
|
||||||
|
const auto utrStart{ atDocumentEnd ? documentEndInclusive : endInclusive };
|
||||||
|
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr, _pUiaData, &_dummyProvider, utrStart, utrEnd));
|
||||||
}
|
}
|
||||||
THROW_IF_FAILED(utr->Move(textUnit, 1, &moveAmt));
|
THROW_IF_FAILED(utr->Move(textUnit, 1, &moveAmt));
|
||||||
|
|
||||||
VERIFY_ARE_EQUAL(endExclusive, til::point{ utr->_end });
|
VERIFY_ARE_EQUAL(documentEndExclusive, utr->_end);
|
||||||
VERIFY_ARE_EQUAL(0, moveAmt);
|
VERIFY_ARE_EQUAL(0, moveAmt);
|
||||||
|
|
||||||
// Verify expansion works properly
|
// Verify expansion works properly
|
||||||
|
@ -1305,33 +1395,35 @@ class UiaTextRangeTests
|
||||||
THROW_IF_FAILED(utr->ExpandToEnclosingUnit(textUnit));
|
THROW_IF_FAILED(utr->ExpandToEnclosingUnit(textUnit));
|
||||||
if (textUnit <= TextUnit::TextUnit_Character)
|
if (textUnit <= TextUnit::TextUnit_Character)
|
||||||
{
|
{
|
||||||
VERIFY_ARE_EQUAL(endInclusive, til::point{ utr->_start });
|
VERIFY_ARE_EQUAL(documentEndInclusive, utr->_start);
|
||||||
VERIFY_ARE_EQUAL(endExclusive, til::point{ utr->_end });
|
VERIFY_ARE_EQUAL(documentEndExclusive, utr->_end);
|
||||||
}
|
}
|
||||||
else if (textUnit <= TextUnit::TextUnit_Word)
|
else if (textUnit <= TextUnit::TextUnit_Word)
|
||||||
{
|
{
|
||||||
VERIFY_ARE_EQUAL(writeTarget, til::point{ utr->_start });
|
VERIFY_ARE_EQUAL(writeTarget, til::point{ utr->_start });
|
||||||
VERIFY_ARE_EQUAL(endExclusive, til::point{ utr->_end });
|
VERIFY_ARE_EQUAL(documentEndExclusive, utr->_end);
|
||||||
}
|
}
|
||||||
else if (textUnit <= TextUnit::TextUnit_Line)
|
else if (textUnit <= TextUnit::TextUnit_Line)
|
||||||
{
|
{
|
||||||
VERIFY_ARE_EQUAL(lastLineStart, til::point{ utr->_start });
|
VERIFY_ARE_EQUAL(lastLineStart, utr->_start);
|
||||||
VERIFY_ARE_EQUAL(endExclusive, til::point{ utr->_end });
|
VERIFY_ARE_EQUAL(documentEndExclusive, utr->_end);
|
||||||
}
|
}
|
||||||
else // textUnit <= TextUnit::TextUnit_Document:
|
else // textUnit <= TextUnit::TextUnit_Document:
|
||||||
{
|
{
|
||||||
VERIFY_ARE_EQUAL(origin, til::point{ utr->_start });
|
VERIFY_ARE_EQUAL(origin, til::point{ utr->_start });
|
||||||
VERIFY_ARE_EQUAL(endExclusive, til::point{ utr->_end });
|
VERIFY_ARE_EQUAL(documentEndExclusive, utr->_end);
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset the UTR
|
// reset the UTR
|
||||||
if (degenerate)
|
if (degenerate)
|
||||||
{
|
{
|
||||||
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr, _pUiaData, &_dummyProvider, endExclusive, endExclusive));
|
const auto utrStart{ atDocumentEnd ? documentEndExclusive : endExclusive };
|
||||||
|
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr, _pUiaData, &_dummyProvider, utrStart, utrEnd));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr, _pUiaData, &_dummyProvider, endInclusive, endExclusive));
|
const auto utrStart{ atDocumentEnd ? documentEndInclusive : endInclusive };
|
||||||
|
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr, _pUiaData, &_dummyProvider, utrStart, utrEnd));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify that moving backwards still works properly
|
// Verify that moving backwards still works properly
|
||||||
|
@ -1345,26 +1437,26 @@ class UiaTextRangeTests
|
||||||
// - degenerate --> it moves with _start to stay degenerate
|
// - degenerate --> it moves with _start to stay degenerate
|
||||||
// - !degenerate --> it excludes the last char, to select the second to last char
|
// - !degenerate --> it excludes the last char, to select the second to last char
|
||||||
VERIFY_ARE_EQUAL(-1, moveAmt);
|
VERIFY_ARE_EQUAL(-1, moveAmt);
|
||||||
VERIFY_ARE_EQUAL(degenerate ? endInclusive : secondToLastCharacterPos, til::point{ utr->_start });
|
VERIFY_ARE_EQUAL(degenerate || !atDocumentEnd ? documentEndInclusive : secondToLastCharacterPos, utr->_start);
|
||||||
VERIFY_ARE_EQUAL(endInclusive, til::point{ utr->_end });
|
VERIFY_ARE_EQUAL(documentEndInclusive, utr->_end);
|
||||||
}
|
}
|
||||||
else if (textUnit <= TextUnit::TextUnit_Word)
|
else if (textUnit <= TextUnit::TextUnit_Word)
|
||||||
{
|
{
|
||||||
VERIFY_ARE_EQUAL(-1, moveAmt);
|
VERIFY_ARE_EQUAL(-1, moveAmt);
|
||||||
VERIFY_ARE_EQUAL(origin, til::point{ utr->_start });
|
VERIFY_ARE_EQUAL(origin, til::point{ utr->_start });
|
||||||
VERIFY_ARE_EQUAL(degenerate ? origin : writeTarget, til::point{ utr->_end });
|
VERIFY_ARE_EQUAL(degenerate || !atDocumentEnd ? origin : writeTarget, til::point{ utr->_end });
|
||||||
}
|
}
|
||||||
else if (textUnit <= TextUnit::TextUnit_Line)
|
else if (textUnit <= TextUnit::TextUnit_Line)
|
||||||
{
|
{
|
||||||
VERIFY_ARE_EQUAL(-1, moveAmt);
|
VERIFY_ARE_EQUAL(-1, moveAmt);
|
||||||
VERIFY_ARE_EQUAL(degenerate ? lastLineStart : secondToLastLinePos, til::point{ utr->_start });
|
VERIFY_ARE_EQUAL(degenerate || !atDocumentEnd ? lastLineStart : secondToLastLinePos, til::point{ utr->_start });
|
||||||
VERIFY_ARE_EQUAL(lastLineStart, til::point{ utr->_end });
|
VERIFY_ARE_EQUAL(lastLineStart, til::point{ utr->_end });
|
||||||
}
|
}
|
||||||
else // textUnit <= TextUnit::TextUnit_Document:
|
else // textUnit <= TextUnit::TextUnit_Document:
|
||||||
{
|
{
|
||||||
VERIFY_ARE_EQUAL(degenerate ? -1 : 0, moveAmt);
|
VERIFY_ARE_EQUAL(degenerate || !atDocumentEnd ? -1 : 0, moveAmt);
|
||||||
VERIFY_ARE_EQUAL(origin, til::point{ utr->_start });
|
VERIFY_ARE_EQUAL(origin, til::point{ utr->_start });
|
||||||
VERIFY_ARE_EQUAL(degenerate ? origin : endExclusive, til::point{ utr->_end });
|
VERIFY_ARE_EQUAL(degenerate || !atDocumentEnd ? origin : documentEndExclusive, til::point{ utr->_end });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1829,37 +1921,38 @@ class UiaTextRangeTests
|
||||||
TEST_METHOD(GeneratedMovementTests)
|
TEST_METHOD(GeneratedMovementTests)
|
||||||
{
|
{
|
||||||
// Populate the buffer with...
|
// Populate the buffer with...
|
||||||
// - 9 segments of alternating text
|
// - 10 segments of alternating text
|
||||||
// - up to half of the buffer (vertically)
|
// - up to half of the buffer (vertically)
|
||||||
// It'll look something like this
|
// It'll look something like this
|
||||||
// +---------------------------+
|
// +------------------------------+
|
||||||
// |XXX XXX XXX XXX XXX|
|
// |XXX XXX XXX XXX XXX |
|
||||||
// |XXX XXX XXX XXX XXX|
|
// |XXX XXX XXX XXX XXX |
|
||||||
// |XXX XXX XXX XXX XXX|
|
// |XXX XXX XXX XXX XXX |
|
||||||
// |XXX XXX XXX XXX XXX|
|
// |XXX XXX XXX XXX XXX |
|
||||||
// |XXX XXX XXX XXX XXX|
|
// |XXX XXX XXX XXX XXX |
|
||||||
// | |
|
// | |
|
||||||
// | |
|
// | |
|
||||||
// | |
|
// | |
|
||||||
// | |
|
// | |
|
||||||
// | |
|
// | |
|
||||||
// +---------------------------+
|
// +------------------------------+
|
||||||
{
|
{
|
||||||
short i = 0;
|
short i = 0;
|
||||||
auto iter{ _pTextBuffer->GetCellDataAt(bufferSize.origin()) };
|
auto iter{ _pTextBuffer->GetCellDataAt(bufferSize.origin()) };
|
||||||
const auto segment{ bufferSize.width() / 9 };
|
const auto segment{ bufferSize.width() / 10 };
|
||||||
|
bool fill{ true };
|
||||||
while (iter.Pos() != docEnd)
|
while (iter.Pos() != docEnd)
|
||||||
{
|
{
|
||||||
bool fill{ true };
|
if (iter.Pos().X == bufferSize.left())
|
||||||
if (i % segment == 0)
|
{
|
||||||
|
fill = true;
|
||||||
|
}
|
||||||
|
else if (i % segment == 0)
|
||||||
{
|
{
|
||||||
fill = !fill;
|
fill = !fill;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fill)
|
_pTextBuffer->Write({ fill ? L"X" : L" " }, iter.Pos());
|
||||||
{
|
|
||||||
_pTextBuffer->Write({ L"X" }, iter.Pos());
|
|
||||||
}
|
|
||||||
|
|
||||||
++i;
|
++i;
|
||||||
++iter;
|
++iter;
|
||||||
|
|
|
@ -280,52 +280,50 @@ IFACEMETHODIMP UiaTextRangeBase::ExpandToEnclosingUnit(_In_ TextUnit unit) noexc
|
||||||
void UiaTextRangeBase::_expandToEnclosingUnit(TextUnit unit)
|
void UiaTextRangeBase::_expandToEnclosingUnit(TextUnit unit)
|
||||||
{
|
{
|
||||||
const auto& buffer = _pData->GetTextBuffer();
|
const auto& buffer = _pData->GetTextBuffer();
|
||||||
const auto bufferSize = _getBufferSize();
|
const auto bufferSize{ buffer.GetSize() };
|
||||||
const auto bufferEnd = bufferSize.EndExclusive();
|
const auto documentEnd{ _getDocumentEnd() };
|
||||||
|
|
||||||
|
// If we're past document end,
|
||||||
|
// set us to ONE BEFORE the document end.
|
||||||
|
// This allows us to expand properly.
|
||||||
|
if (bufferSize.CompareInBounds(_start, documentEnd, true) >= 0)
|
||||||
|
{
|
||||||
|
_start = documentEnd;
|
||||||
|
bufferSize.DecrementInBounds(_start, true);
|
||||||
|
}
|
||||||
|
|
||||||
if (unit == TextUnit_Character)
|
if (unit == TextUnit_Character)
|
||||||
{
|
{
|
||||||
_start = buffer.GetGlyphStart(_start);
|
_start = buffer.GetGlyphStart(_start, documentEnd);
|
||||||
_end = buffer.GetGlyphEnd(_start);
|
_end = buffer.GetGlyphEnd(_start, documentEnd);
|
||||||
}
|
}
|
||||||
else if (unit <= TextUnit_Word)
|
else if (unit <= TextUnit_Word)
|
||||||
{
|
{
|
||||||
// expand to word
|
// expand to word
|
||||||
_start = buffer.GetWordStart(_start, _wordDelimiters, true);
|
_start = buffer.GetWordStart(_start, _wordDelimiters, true, documentEnd);
|
||||||
_end = buffer.GetWordEnd(_start, _wordDelimiters, true);
|
_end = buffer.GetWordEnd(_start, _wordDelimiters, true, documentEnd);
|
||||||
|
|
||||||
// GetWordEnd may return the actual end of the TextBuffer.
|
|
||||||
// If so, just set it to this value of bufferEnd
|
|
||||||
if (!bufferSize.IsInBounds(_end))
|
|
||||||
{
|
|
||||||
_end = bufferEnd;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (unit <= TextUnit_Line)
|
else if (unit <= TextUnit_Line)
|
||||||
{
|
{
|
||||||
if (_start == bufferEnd)
|
// expand to line
|
||||||
|
_start.X = 0;
|
||||||
|
if (_start.Y == documentEnd.y())
|
||||||
{
|
{
|
||||||
// Special case: if we are at the bufferEnd,
|
// we're on the last line
|
||||||
// move _start back one, instead of _end forward
|
_end = documentEnd;
|
||||||
_start.X = 0;
|
bufferSize.IncrementInBounds(_end, true);
|
||||||
_start.Y = base::ClampSub(_start.Y, 1);
|
|
||||||
_end = bufferEnd;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// expand to line
|
|
||||||
_start.X = 0;
|
|
||||||
_end.X = 0;
|
_end.X = 0;
|
||||||
_end.Y = base::ClampAdd(_start.Y, 1);
|
_end.Y = base::ClampAdd(_start.Y, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// TODO GH#6986: properly handle "end of buffer" as last character
|
|
||||||
// instead of last cell
|
|
||||||
// expand to document
|
// expand to document
|
||||||
_start = bufferSize.Origin();
|
_start = bufferSize.Origin();
|
||||||
_end = bufferSize.EndExclusive();
|
_end = documentEnd;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -608,7 +606,7 @@ try
|
||||||
*ppRetVal = nullptr;
|
*ppRetVal = nullptr;
|
||||||
|
|
||||||
const std::wstring queryText{ text, SysStringLen(text) };
|
const std::wstring queryText{ text, SysStringLen(text) };
|
||||||
const auto bufferSize = _getBufferSize();
|
const auto bufferSize = _getOptimizedBufferSize();
|
||||||
const auto sensitivity = ignoreCase ? Search::Sensitivity::CaseInsensitive : Search::Sensitivity::CaseSensitive;
|
const auto sensitivity = ignoreCase ? Search::Sensitivity::CaseInsensitive : Search::Sensitivity::CaseSensitive;
|
||||||
|
|
||||||
auto searchDirection = Search::Direction::Forward;
|
auto searchDirection = Search::Direction::Forward;
|
||||||
|
@ -1016,11 +1014,24 @@ try
|
||||||
_pData->UnlockConsole();
|
_pData->UnlockConsole();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// We can abstract this movement by moving _start
|
||||||
|
// GH#7342: check if we're past the documentEnd
|
||||||
|
// If so, clamp each endpoint to the end of the document.
|
||||||
|
constexpr auto endpoint = TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start;
|
||||||
|
const auto bufferSize{ _pData->GetTextBuffer().GetSize() };
|
||||||
|
const COORD documentEnd = _getDocumentEnd();
|
||||||
|
if (bufferSize.CompareInBounds(_start, documentEnd, true) > 0)
|
||||||
|
{
|
||||||
|
_start = documentEnd;
|
||||||
|
}
|
||||||
|
if (bufferSize.CompareInBounds(_end, documentEnd, true) > 0)
|
||||||
|
{
|
||||||
|
_end = documentEnd;
|
||||||
|
}
|
||||||
|
|
||||||
const auto wasDegenerate = IsDegenerate();
|
const auto wasDegenerate = IsDegenerate();
|
||||||
if (count != 0)
|
if (count != 0)
|
||||||
{
|
{
|
||||||
// We can abstract this movement by moving _start
|
|
||||||
constexpr auto endpoint = TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start;
|
|
||||||
const auto preventBoundary = !wasDegenerate;
|
const auto preventBoundary = !wasDegenerate;
|
||||||
if (unit == TextUnit::TextUnit_Character)
|
if (unit == TextUnit::TextUnit_Character)
|
||||||
{
|
{
|
||||||
|
@ -1028,13 +1039,7 @@ try
|
||||||
}
|
}
|
||||||
else if (unit <= TextUnit::TextUnit_Word)
|
else if (unit <= TextUnit::TextUnit_Word)
|
||||||
{
|
{
|
||||||
// TODO GH#10925: passing in "true" instead of "preventBoundary"
|
_moveEndpointByUnitWord(count, endpoint, pRetVal, preventBoundary);
|
||||||
// We still need to go through the process of writing
|
|
||||||
// tests, finding failing cases, and fixing them.
|
|
||||||
// For now, just use true because we've been doing that so far.
|
|
||||||
// The tests at the time of writing don't report any failures
|
|
||||||
// if we use one over the other.
|
|
||||||
_moveEndpointByUnitWord(count, endpoint, pRetVal, true);
|
|
||||||
}
|
}
|
||||||
else if (unit <= TextUnit::TextUnit_Line)
|
else if (unit <= TextUnit::TextUnit_Line)
|
||||||
{
|
{
|
||||||
|
@ -1080,6 +1085,26 @@ IFACEMETHODIMP UiaTextRangeBase::MoveEndpointByUnit(_In_ TextPatternRangeEndpoin
|
||||||
_pData->UnlockConsole();
|
_pData->UnlockConsole();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// GH#7342: check if we're past the documentEnd
|
||||||
|
// If so, clamp each endpoint to the end of the document.
|
||||||
|
const auto bufferSize{ _pData->GetTextBuffer().GetSize() };
|
||||||
|
|
||||||
|
auto documentEnd = bufferSize.EndExclusive();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
documentEnd = _getDocumentEnd();
|
||||||
|
}
|
||||||
|
CATCH_LOG();
|
||||||
|
|
||||||
|
if (bufferSize.CompareInBounds(_start, documentEnd, true) > 0)
|
||||||
|
{
|
||||||
|
_start = documentEnd;
|
||||||
|
}
|
||||||
|
if (bufferSize.CompareInBounds(_end, documentEnd, true) > 0)
|
||||||
|
{
|
||||||
|
_end = documentEnd;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (unit == TextUnit::TextUnit_Character)
|
if (unit == TextUnit::TextUnit_Character)
|
||||||
|
@ -1307,7 +1332,7 @@ const unsigned int UiaTextRangeBase::_getViewportHeight(const SMALL_RECT viewpor
|
||||||
// - <none>
|
// - <none>
|
||||||
// Return Value:
|
// Return Value:
|
||||||
// - A viewport representing the portion of the TextBuffer that has valid text
|
// - A viewport representing the portion of the TextBuffer that has valid text
|
||||||
const Viewport UiaTextRangeBase::_getBufferSize() const noexcept
|
const Viewport UiaTextRangeBase::_getOptimizedBufferSize() const noexcept
|
||||||
{
|
{
|
||||||
// we need to add 1 to the X/Y of textBufferEnd
|
// we need to add 1 to the X/Y of textBufferEnd
|
||||||
// because we want the returned viewport to include this COORD
|
// because we want the returned viewport to include this COORD
|
||||||
|
@ -1318,6 +1343,20 @@ const Viewport UiaTextRangeBase::_getBufferSize() const noexcept
|
||||||
return Viewport::FromDimensions({ 0, 0 }, width, height);
|
return Viewport::FromDimensions({ 0, 0 }, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We consider the "document end" to be the line beneath the cursor or
|
||||||
|
// last legible character (whichever is further down). In the event where
|
||||||
|
// the last legible character is on the last line of the buffer,
|
||||||
|
// we use the "end exclusive" position (left-most point on a line one past the end of the buffer).
|
||||||
|
// NOTE: "end exclusive" is naturally computed using the heuristic above.
|
||||||
|
const til::point UiaTextRangeBase::_getDocumentEnd() const
|
||||||
|
{
|
||||||
|
const auto optimizedBufferSize{ _getOptimizedBufferSize() };
|
||||||
|
const auto& buffer{ _pData->GetTextBuffer() };
|
||||||
|
const auto lastCharPos{ buffer.GetLastNonSpaceCharacter(optimizedBufferSize) };
|
||||||
|
const auto cursorPos{ buffer.GetCursor().GetPosition() };
|
||||||
|
return { optimizedBufferSize.Left(), std::max(lastCharPos.Y, cursorPos.Y) + 1 };
|
||||||
|
}
|
||||||
|
|
||||||
// Routine Description:
|
// Routine Description:
|
||||||
// - adds the relevant coordinate points from the row to coords.
|
// - adds the relevant coordinate points from the row to coords.
|
||||||
// - it is assumed that startAnchor and endAnchor are within the same row
|
// - it is assumed that startAnchor and endAnchor are within the same row
|
||||||
|
@ -1388,19 +1427,20 @@ void UiaTextRangeBase::_moveEndpointByUnitCharacter(_In_ const int moveCount,
|
||||||
|
|
||||||
bool success = true;
|
bool success = true;
|
||||||
til::point target = GetEndpoint(endpoint);
|
til::point target = GetEndpoint(endpoint);
|
||||||
|
const auto documentEnd{ _getDocumentEnd() };
|
||||||
while (std::abs(*pAmountMoved) < std::abs(moveCount) && success)
|
while (std::abs(*pAmountMoved) < std::abs(moveCount) && success)
|
||||||
{
|
{
|
||||||
switch (moveDirection)
|
switch (moveDirection)
|
||||||
{
|
{
|
||||||
case MovementDirection::Forward:
|
case MovementDirection::Forward:
|
||||||
success = buffer.MoveToNextGlyph(target, allowBottomExclusive);
|
success = buffer.MoveToNextGlyph(target, allowBottomExclusive, documentEnd);
|
||||||
if (success)
|
if (success)
|
||||||
{
|
{
|
||||||
(*pAmountMoved)++;
|
(*pAmountMoved)++;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MovementDirection::Backward:
|
case MovementDirection::Backward:
|
||||||
success = buffer.MoveToPreviousGlyph(target);
|
success = buffer.MoveToPreviousGlyph(target, documentEnd);
|
||||||
if (success)
|
if (success)
|
||||||
{
|
{
|
||||||
(*pAmountMoved)--;
|
(*pAmountMoved)--;
|
||||||
|
@ -1441,10 +1481,9 @@ void UiaTextRangeBase::_moveEndpointByUnitWord(_In_ const int moveCount,
|
||||||
const bool allowBottomExclusive = !preventBufferEnd;
|
const bool allowBottomExclusive = !preventBufferEnd;
|
||||||
const MovementDirection moveDirection = (moveCount > 0) ? MovementDirection::Forward : MovementDirection::Backward;
|
const MovementDirection moveDirection = (moveCount > 0) ? MovementDirection::Forward : MovementDirection::Backward;
|
||||||
const auto& buffer = _pData->GetTextBuffer();
|
const auto& buffer = _pData->GetTextBuffer();
|
||||||
const auto bufferSize = _getBufferSize();
|
const auto bufferSize = buffer.GetSize();
|
||||||
const auto bufferOrigin = bufferSize.Origin();
|
const auto bufferOrigin = bufferSize.Origin();
|
||||||
const auto bufferEnd = bufferSize.EndExclusive();
|
const auto documentEnd = _getDocumentEnd();
|
||||||
const auto lastCharPos = buffer.GetLastNonSpaceCharacter(bufferSize);
|
|
||||||
|
|
||||||
auto resultPos = GetEndpoint(endpoint);
|
auto resultPos = GetEndpoint(endpoint);
|
||||||
auto nextPos = resultPos;
|
auto nextPos = resultPos;
|
||||||
|
@ -1457,18 +1496,18 @@ void UiaTextRangeBase::_moveEndpointByUnitWord(_In_ const int moveCount,
|
||||||
{
|
{
|
||||||
case MovementDirection::Forward:
|
case MovementDirection::Forward:
|
||||||
{
|
{
|
||||||
if (nextPos == bufferEnd)
|
if (bufferSize.CompareInBounds(nextPos, documentEnd, true) >= 0)
|
||||||
{
|
{
|
||||||
success = false;
|
success = false;
|
||||||
}
|
}
|
||||||
else if (buffer.MoveToNextWord(nextPos, _wordDelimiters, lastCharPos))
|
else if (buffer.MoveToNextWord(nextPos, _wordDelimiters, documentEnd))
|
||||||
{
|
{
|
||||||
resultPos = nextPos;
|
resultPos = nextPos;
|
||||||
(*pAmountMoved)++;
|
(*pAmountMoved)++;
|
||||||
}
|
}
|
||||||
else if (allowBottomExclusive)
|
else if (allowBottomExclusive)
|
||||||
{
|
{
|
||||||
resultPos = bufferEnd;
|
resultPos = documentEnd;
|
||||||
(*pAmountMoved)++;
|
(*pAmountMoved)++;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1529,10 +1568,18 @@ void UiaTextRangeBase::_moveEndpointByUnitLine(_In_ const int moveCount,
|
||||||
|
|
||||||
const bool allowBottomExclusive = !preventBoundary;
|
const bool allowBottomExclusive = !preventBoundary;
|
||||||
const MovementDirection moveDirection = (moveCount > 0) ? MovementDirection::Forward : MovementDirection::Backward;
|
const MovementDirection moveDirection = (moveCount > 0) ? MovementDirection::Forward : MovementDirection::Backward;
|
||||||
const auto bufferSize = _getBufferSize();
|
const auto bufferSize = _getOptimizedBufferSize();
|
||||||
|
|
||||||
|
auto documentEnd{ bufferSize.EndExclusive() };
|
||||||
|
try
|
||||||
|
{
|
||||||
|
documentEnd = _getDocumentEnd();
|
||||||
|
}
|
||||||
|
CATCH_LOG();
|
||||||
|
|
||||||
bool success = true;
|
bool success = true;
|
||||||
auto resultPos = GetEndpoint(endpoint);
|
auto resultPos = GetEndpoint(endpoint);
|
||||||
|
|
||||||
while (std::abs(*pAmountMoved) < std::abs(moveCount) && success)
|
while (std::abs(*pAmountMoved) < std::abs(moveCount) && success)
|
||||||
{
|
{
|
||||||
auto nextPos = resultPos;
|
auto nextPos = resultPos;
|
||||||
|
@ -1540,22 +1587,29 @@ void UiaTextRangeBase::_moveEndpointByUnitLine(_In_ const int moveCount,
|
||||||
{
|
{
|
||||||
case MovementDirection::Forward:
|
case MovementDirection::Forward:
|
||||||
{
|
{
|
||||||
// can't move past end
|
if (nextPos.Y >= documentEnd.Y)
|
||||||
if (nextPos.Y >= bufferSize.BottomInclusive())
|
|
||||||
{
|
{
|
||||||
if (preventBoundary || nextPos == bufferSize.EndExclusive())
|
// Corner Case: we're past the limit
|
||||||
{
|
// Clamp us to the limit
|
||||||
success = false;
|
resultPos = documentEnd;
|
||||||
break;
|
success = false;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else if (preventBoundary && nextPos.Y == base::ClampSub(documentEnd.Y, 1))
|
||||||
nextPos.X = bufferSize.RightInclusive();
|
|
||||||
success = bufferSize.IncrementInBounds(nextPos, allowBottomExclusive);
|
|
||||||
if (success)
|
|
||||||
{
|
{
|
||||||
resultPos = nextPos;
|
// Corner Case: we're just before the limit
|
||||||
(*pAmountMoved)++;
|
// and we're not allowed onto the exclusive end.
|
||||||
|
// Fail to move.
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nextPos.X = bufferSize.RightInclusive();
|
||||||
|
success = bufferSize.IncrementInBounds(nextPos, allowBottomExclusive);
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
resultPos = nextPos;
|
||||||
|
(*pAmountMoved)++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1621,15 +1675,21 @@ void UiaTextRangeBase::_moveEndpointByUnitDocument(_In_ const int moveCount,
|
||||||
}
|
}
|
||||||
|
|
||||||
const MovementDirection moveDirection = (moveCount > 0) ? MovementDirection::Forward : MovementDirection::Backward;
|
const MovementDirection moveDirection = (moveCount > 0) ? MovementDirection::Forward : MovementDirection::Backward;
|
||||||
const auto bufferSize = _getBufferSize();
|
const auto bufferSize = _getOptimizedBufferSize();
|
||||||
|
|
||||||
const auto target = GetEndpoint(endpoint);
|
const auto target = GetEndpoint(endpoint);
|
||||||
switch (moveDirection)
|
switch (moveDirection)
|
||||||
{
|
{
|
||||||
case MovementDirection::Forward:
|
case MovementDirection::Forward:
|
||||||
{
|
{
|
||||||
const auto documentEnd = bufferSize.EndExclusive();
|
auto documentEnd{ bufferSize.EndExclusive() };
|
||||||
if (preventBoundary || target == documentEnd)
|
try
|
||||||
|
{
|
||||||
|
documentEnd = _getDocumentEnd();
|
||||||
|
}
|
||||||
|
CATCH_LOG();
|
||||||
|
|
||||||
|
if (preventBoundary || bufferSize.CompareInBounds(target, documentEnd, true) >= 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -149,7 +149,8 @@ namespace Microsoft::Console::Types
|
||||||
virtual const COORD _getScreenFontSize() const;
|
virtual const COORD _getScreenFontSize() const;
|
||||||
|
|
||||||
const unsigned int _getViewportHeight(const SMALL_RECT viewport) const noexcept;
|
const unsigned int _getViewportHeight(const SMALL_RECT viewport) const noexcept;
|
||||||
const Viewport _getBufferSize() const noexcept;
|
const Viewport _getOptimizedBufferSize() const noexcept;
|
||||||
|
const til::point _getDocumentEnd() const;
|
||||||
|
|
||||||
void _getBoundingRect(const til::rectangle textRect, _Inout_ std::vector<double>& coords) const;
|
void _getBoundingRect(const til::rectangle textRect, _Inout_ std::vector<double>& coords) const;
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,6 @@ $result = "// Copyright (c) Microsoft Corporation.
|
||||||
// These were generated by tools\TestTableWriter\GenerateTests.ps1
|
// These were generated by tools\TestTableWriter\GenerateTests.ps1
|
||||||
// Read tools\TestTableWriter\README.md for more details"
|
// Read tools\TestTableWriter\README.md for more details"
|
||||||
|
|
||||||
# TODO: THIS IS PROBABLY WRONG. Bottom/Right are exclusive (I think?)
|
|
||||||
# 1. Define a few helpful variables to make life easier.
|
# 1. Define a few helpful variables to make life easier.
|
||||||
$result += "
|
$result += "
|
||||||
// Define a few helpful variables
|
// Define a few helpful variables
|
||||||
|
@ -29,11 +28,16 @@ constexpr til::rectangle bufferSize{ 0, 0, 80, 300 };
|
||||||
constexpr short midX{ 40 };
|
constexpr short midX{ 40 };
|
||||||
constexpr short midY{ 150 };
|
constexpr short midY{ 150 };
|
||||||
constexpr short midPopulatedY{ 75 };
|
constexpr short midPopulatedY{ 75 };
|
||||||
|
constexpr short segment0{ 0 };
|
||||||
|
constexpr short segment1{ 16 };
|
||||||
|
constexpr short segment2{ 32 };
|
||||||
|
constexpr short segment3{ 48 };
|
||||||
|
constexpr short segment4{ 64 };
|
||||||
constexpr til::point origin{ 0, 0 };
|
constexpr til::point origin{ 0, 0 };
|
||||||
constexpr til::point midTop{ midX, 0 };
|
constexpr til::point midTop{ midX, 0 };
|
||||||
constexpr til::point midHistory{ midX, midPopulatedY };
|
constexpr til::point midHistory{ midX, midPopulatedY };
|
||||||
constexpr til::point midDocEnd{ midX, midY };
|
constexpr til::point midDocEnd{ midX, midY };
|
||||||
constexpr til::point lastCharPos{ 79, midY };
|
constexpr til::point lastCharPos{ 72, midY };
|
||||||
constexpr til::point docEnd{ 0, midY + 1 };
|
constexpr til::point docEnd{ 0, midY + 1 };
|
||||||
constexpr til::point midEmptySpace{ midX, midY + midPopulatedY };
|
constexpr til::point midEmptySpace{ midX, midY + midPopulatedY };
|
||||||
constexpr til::point bufferEnd{ 79, 299 };
|
constexpr til::point bufferEnd{ 79, 299 };
|
||||||
|
@ -55,17 +59,33 @@ foreach ($test in $tests)
|
||||||
$vars.Remove("origin") > $null;
|
$vars.Remove("origin") > $null;
|
||||||
$vars.Remove("midTop") > $null;
|
$vars.Remove("midTop") > $null;
|
||||||
$vars.Remove("midHistory") > $null;
|
$vars.Remove("midHistory") > $null;
|
||||||
|
$vars.Remove("midDocEnd") > $null;
|
||||||
|
$vars.Remove("lastCharPos") > $null;
|
||||||
$vars.Remove("docEnd") > $null;
|
$vars.Remove("docEnd") > $null;
|
||||||
|
$vars.Remove("midEmptySpace") > $null;
|
||||||
|
$vars.Remove("bufferEnd") > $null;
|
||||||
|
$vars.Remove("endExclusive") > $null;
|
||||||
|
|
||||||
# 3.b. Now all of the remaining vars can be deduced from standard vars
|
# 3.b. Now all of the remaining vars can be deduced from standard vars
|
||||||
foreach ($var in $vars)
|
foreach ($var in $vars)
|
||||||
{
|
{
|
||||||
# Extract the standard var from the name
|
# Figure out what heuristic to use
|
||||||
$standardVar = $var.Contains("Left") ? $var.Split("Left") : $var.Substring(0, $var.length - 3);
|
$segmentHeuristic = $var.Contains("segment");
|
||||||
|
$leftHeuristic = $var.Contains("Left");
|
||||||
|
$movementHeuristic = $var -match ".*\d+.*";
|
||||||
|
|
||||||
# i. Contains number --> requires movement
|
# i. Contains "segment" --> define point at the beginning of a text segment
|
||||||
if ($var -match ".*\d+.*")
|
if ($segmentHeuristic)
|
||||||
{
|
{
|
||||||
|
$result += "constexpr til::point {0}{{ {1}, {2}.y() }};" -f $var, $var.Substring(0, 8), $var.Substring(9, $var.Length - $var.IndexOf("L") - 1);
|
||||||
|
}
|
||||||
|
# ii. Contains number --> requires movement
|
||||||
|
elseif ($movementHeuristic)
|
||||||
|
{
|
||||||
|
# everything excluding last 3 characters denotes the standard variable
|
||||||
|
# we're based on.
|
||||||
|
$standardVar = $var.Substring(0, $var.length - 3);
|
||||||
|
|
||||||
# 3rd to last character denotes the movement direction
|
# 3rd to last character denotes the movement direction
|
||||||
# P --> plus/forwards
|
# P --> plus/forwards
|
||||||
# M --> minus/backwards
|
# M --> minus/backwards
|
||||||
|
@ -101,10 +121,11 @@ foreach ($var in $vars)
|
||||||
Default { Write-Host "Error: unknown variable movement type" -ForegroundColor Red }
|
Default { Write-Host "Error: unknown variable movement type" -ForegroundColor Red }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
# ii. Contains "Left" --> set X to left
|
# iii. Contains "Left" --> set X to left
|
||||||
elseif ($var.Contains("Left"))
|
elseif ($leftHeuristic)
|
||||||
{
|
{
|
||||||
$result += "constexpr til::point " + $var + "{ bufferSize.left(), " + $standardVar + ".y() };";
|
$standardVar = $var.Split("Left")[0]
|
||||||
|
$result += "constexpr til::point {0}{{ bufferSize.left(), {1}.y() }};" -f $var, $standardVar;
|
||||||
}
|
}
|
||||||
$result += "`n";
|
$result += "`n";
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,21 +44,21 @@ The Test Table Writer was written as a method to generate UI Automation tests fo
|
||||||
- "Command Arguments" --> `$(TargetPath) /name:*uiatextrange*generated* /inproc`
|
- "Command Arguments" --> `$(TargetPath) /name:*uiatextrange*generated* /inproc`
|
||||||
|
|
||||||
# Position chart
|
# Position chart
|
||||||
The text buffer is assumed to be partially filled. Specifically, the top half of the text buffer contains text, and each row is filled with 9 segments of alternating text. For visualization,
|
The text buffer is assumed to be partially filled. Specifically, the top half of the text buffer contains text, and each row is filled with 10 segments of alternating text. For visualization,
|
||||||
the ascii diagram below shows what the text buffer may look like.
|
the ascii diagram below shows what the text buffer may look like.
|
||||||
```
|
```
|
||||||
+---------------------------+
|
+------------------------------+
|
||||||
|1XX XXX X2X XXX XXX|
|
|1XX XXX X2X XXX XXX |
|
||||||
|XXX XXX XXX XXX XXX|
|
|XXX XXX XXX XXX XXX |
|
||||||
|XXX XXX X3X XXX XXX|
|
|XXX XXX X3X XXX XXX |
|
||||||
|XXX XXX XXX XXX XXX|
|
|XXX XXX XXX XXX XXX |
|
||||||
|XXX XXX X4X XXX XX5|
|
|XXX XXX X4X XXX XX5 |
|
||||||
|6 |
|
|6 |
|
||||||
| |
|
| |
|
||||||
| 7 |
|
| 7 |
|
||||||
| |
|
| |
|
||||||
| 8|
|
| 8|
|
||||||
+---------------------------+
|
+------------------------------+
|
||||||
9
|
9
|
||||||
```
|
```
|
||||||
The following positions are being tested:
|
The following positions are being tested:
|
||||||
|
@ -84,6 +84,11 @@ Each position above already has a predefined variable name. However, a few heuri
|
||||||
- `C`: move by character. For simplicity, assumes that each character is one-cell wide.
|
- `C`: move by character. For simplicity, assumes that each character is one-cell wide.
|
||||||
- `<name>P<number>L`, `<name>M<number>L`:
|
- `<name>P<number>L`, `<name>M<number>L`:
|
||||||
- same as above, except move by line. For simplicity, assumes that you won't hit a buffer boundary.
|
- same as above, except move by line. For simplicity, assumes that you won't hit a buffer boundary.
|
||||||
|
- `segment#L<name>`
|
||||||
|
- This is mainly used for word navigation to target a segment of text in the buffer.
|
||||||
|
- `segment#` refers to the beginning of a segment of text in a row. The leftmost run of text is `segment0`, whereas the rightmost run of text is `segment4`.
|
||||||
|
- `L<name>` refers to the line number we're targeting, relative to the `<name>` variable. For example, `Lorigin` means that the y-position should be that of `origin`.
|
||||||
|
- Overall, this allows us to target the beginning of segments of text. `segment0Lorigin` and `segment0LmidTop` both refer to the beginning of the first segment of text on the top line (aka `origin`).
|
||||||
|
|
||||||
# Helpful terms and concepts
|
# Helpful terms and concepts
|
||||||
- *degenerate*: the text range encompasses no text. Also, means the start and end endpoints are the same.
|
- *degenerate*: the text range encompasses no text. Also, means the start and end endpoints are the same.
|
||||||
|
|
|
@ -62,195 +62,280 @@ FALSE,3,TextUnit_Line,5,midHistory,midHistoryP1C,5,midHistoryP5L,midHistoryP6L,F
|
||||||
TRUE,1,TextUnit_Document,-5,origin,origin,0,origin,origin,FALSE
|
TRUE,1,TextUnit_Document,-5,origin,origin,0,origin,origin,FALSE
|
||||||
TRUE,1,TextUnit_Document,-1,origin,origin,0,origin,origin,FALSE
|
TRUE,1,TextUnit_Document,-1,origin,origin,0,origin,origin,FALSE
|
||||||
TRUE,1,TextUnit_Document,0,origin,origin,0,origin,origin,FALSE
|
TRUE,1,TextUnit_Document,0,origin,origin,0,origin,origin,FALSE
|
||||||
TRUE,1,TextUnit_Document,1,origin,origin,1,endExclusive,endExclusive,FALSE
|
TRUE,1,TextUnit_Document,1,origin,origin,1,docEnd,docEnd,FALSE
|
||||||
TRUE,1,TextUnit_Document,5,origin,origin,1,endExclusive,endExclusive,FALSE
|
TRUE,1,TextUnit_Document,5,origin,origin,1,docEnd,docEnd,FALSE
|
||||||
FALSE,1,TextUnit_Document,-5,origin,originP1C,0,origin,endExclusive,FALSE
|
FALSE,1,TextUnit_Document,-5,origin,originP1C,0,origin,docEnd,FALSE
|
||||||
FALSE,1,TextUnit_Document,-1,origin,originP1C,0,origin,endExclusive,FALSE
|
FALSE,1,TextUnit_Document,-1,origin,originP1C,0,origin,docEnd,FALSE
|
||||||
FALSE,1,TextUnit_Document,0,origin,originP1C,0,origin,endExclusive,FALSE
|
FALSE,1,TextUnit_Document,0,origin,originP1C,0,origin,docEnd,FALSE
|
||||||
FALSE,1,TextUnit_Document,1,origin,originP1C,0,origin,endExclusive,FALSE
|
FALSE,1,TextUnit_Document,1,origin,originP1C,0,origin,docEnd,FALSE
|
||||||
FALSE,1,TextUnit_Document,5,origin,originP1C,0,origin,endExclusive,FALSE
|
FALSE,1,TextUnit_Document,5,origin,originP1C,0,origin,docEnd,FALSE
|
||||||
TRUE,2,TextUnit_Document,-5,midTop,midTop,-1,origin,origin,FALSE
|
TRUE,2,TextUnit_Document,-5,midTop,midTop,-1,origin,origin,FALSE
|
||||||
TRUE,2,TextUnit_Document,-1,midTop,midTop,-1,origin,origin,FALSE
|
TRUE,2,TextUnit_Document,-1,midTop,midTop,-1,origin,origin,FALSE
|
||||||
TRUE,2,TextUnit_Document,0,midTop,midTop,0,midTop,midTop,FALSE
|
TRUE,2,TextUnit_Document,0,midTop,midTop,0,midTop,midTop,FALSE
|
||||||
TRUE,2,TextUnit_Document,1,midTop,midTop,1,endExclusive,endExclusive,FALSE
|
TRUE,2,TextUnit_Document,1,midTop,midTop,1,docEnd,docEnd,FALSE
|
||||||
TRUE,2,TextUnit_Document,5,midTop,midTop,1,endExclusive,endExclusive,FALSE
|
TRUE,2,TextUnit_Document,5,midTop,midTop,1,docEnd,docEnd,FALSE
|
||||||
FALSE,2,TextUnit_Document,-5,midTop,midTopP1C,0,origin,endExclusive,FALSE
|
FALSE,2,TextUnit_Document,-5,midTop,midTopP1C,0,origin,docEnd,FALSE
|
||||||
FALSE,2,TextUnit_Document,-1,midTop,midTopP1C,0,origin,endExclusive,FALSE
|
FALSE,2,TextUnit_Document,-1,midTop,midTopP1C,0,origin,docEnd,FALSE
|
||||||
FALSE,2,TextUnit_Document,0,midTop,midTopP1C,0,origin,endExclusive,FALSE
|
FALSE,2,TextUnit_Document,0,midTop,midTopP1C,0,origin,docEnd,FALSE
|
||||||
FALSE,2,TextUnit_Document,1,midTop,midTopP1C,0,origin,endExclusive,FALSE
|
FALSE,2,TextUnit_Document,1,midTop,midTopP1C,0,origin,docEnd,FALSE
|
||||||
FALSE,2,TextUnit_Document,5,midTop,midTopP1C,0,origin,endExclusive,FALSE
|
FALSE,2,TextUnit_Document,5,midTop,midTopP1C,0,origin,docEnd,FALSE
|
||||||
TRUE,3,TextUnit_Document,-5,midHistory,midHistory,-1,origin,origin,FALSE
|
TRUE,3,TextUnit_Document,-5,midHistory,midHistory,-1,origin,origin,FALSE
|
||||||
TRUE,3,TextUnit_Document,-1,midHistory,midHistory,-1,origin,origin,FALSE
|
TRUE,3,TextUnit_Document,-1,midHistory,midHistory,-1,origin,origin,FALSE
|
||||||
TRUE,3,TextUnit_Document,0,midHistory,midHistory,0,midHistory,midHistory,FALSE
|
TRUE,3,TextUnit_Document,0,midHistory,midHistory,0,midHistory,midHistory,FALSE
|
||||||
TRUE,3,TextUnit_Document,1,midHistory,midHistory,1,endExclusive,endExclusive,FALSE
|
TRUE,3,TextUnit_Document,1,midHistory,midHistory,1,docEnd,docEnd,FALSE
|
||||||
TRUE,3,TextUnit_Document,5,midHistory,midHistory,1,endExclusive,endExclusive,FALSE
|
TRUE,3,TextUnit_Document,5,midHistory,midHistory,1,docEnd,docEnd,FALSE
|
||||||
FALSE,3,TextUnit_Document,-5,midHistory,midHistoryP1C,0,origin,endExclusive,FALSE
|
FALSE,3,TextUnit_Document,-5,midHistory,midHistoryP1C,0,origin,docEnd,FALSE
|
||||||
FALSE,3,TextUnit_Document,-1,midHistory,midHistoryP1C,0,origin,endExclusive,FALSE
|
FALSE,3,TextUnit_Document,-1,midHistory,midHistoryP1C,0,origin,docEnd,FALSE
|
||||||
FALSE,3,TextUnit_Document,0,midHistory,midHistoryP1C,0,origin,endExclusive,FALSE
|
FALSE,3,TextUnit_Document,0,midHistory,midHistoryP1C,0,origin,docEnd,FALSE
|
||||||
FALSE,3,TextUnit_Document,1,midHistory,midHistoryP1C,0,origin,endExclusive,FALSE
|
FALSE,3,TextUnit_Document,1,midHistory,midHistoryP1C,0,origin,docEnd,FALSE
|
||||||
FALSE,3,TextUnit_Document,5,midHistory,midHistoryP1C,0,origin,endExclusive,FALSE
|
FALSE,3,TextUnit_Document,5,midHistory,midHistoryP1C,0,origin,docEnd,FALSE
|
||||||
TRUE,8,TextUnit_Character,-5,bufferEnd,bufferEnd,-5,bufferEndM5C,bufferEndM5C,FALSE
|
TRUE,8,TextUnit_Character,-5,bufferEnd,bufferEnd,-5,docEndM5C,docEndM5C,FALSE
|
||||||
TRUE,8,TextUnit_Character,-1,bufferEnd,bufferEnd,-1,bufferEndM1C,bufferEndM1C,FALSE
|
TRUE,8,TextUnit_Character,-1,bufferEnd,bufferEnd,-1,docEndM1C,docEndM1C,FALSE
|
||||||
TRUE,8,TextUnit_Character,0,bufferEnd,bufferEnd,0,bufferEnd,bufferEnd,FALSE
|
TRUE,8,TextUnit_Character,0,bufferEnd,bufferEnd,0,docEnd,docEnd,FALSE
|
||||||
TRUE,8,TextUnit_Character,1,bufferEnd,bufferEnd,1,endExclusive,endExclusive,TRUE
|
TRUE,8,TextUnit_Character,1,bufferEnd,bufferEnd,0,docEnd,docEnd,FALSE
|
||||||
TRUE,8,TextUnit_Character,5,bufferEnd,bufferEnd,1,endExclusive,endExclusive,TRUE
|
TRUE,8,TextUnit_Character,5,bufferEnd,bufferEnd,0,docEnd,docEnd,FALSE
|
||||||
FALSE,8,TextUnit_Character,-5,bufferEnd,endExclusive,-5,bufferEndM5C,bufferEndM4C,FALSE
|
FALSE,8,TextUnit_Character,-5,bufferEnd,endExclusive,-5,docEndM5C,docEndM5C,FALSE
|
||||||
FALSE,8,TextUnit_Character,-1,bufferEnd,endExclusive,-1,bufferEndM1C,bufferEnd,FALSE
|
FALSE,8,TextUnit_Character,-1,bufferEnd,endExclusive,-1,docEndM1C,docEndM1C,FALSE
|
||||||
FALSE,8,TextUnit_Character,0,bufferEnd,endExclusive,0,bufferEnd,endExclusive,FALSE
|
FALSE,8,TextUnit_Character,0,bufferEnd,endExclusive,0,docEnd,docEnd,FALSE
|
||||||
FALSE,8,TextUnit_Character,1,bufferEnd,endExclusive,0,bufferEnd,endExclusive,FALSE
|
FALSE,8,TextUnit_Character,1,bufferEnd,endExclusive,0,docEnd,docEnd,FALSE
|
||||||
FALSE,8,TextUnit_Character,5,bufferEnd,endExclusive,0,bufferEnd,endExclusive,FALSE
|
FALSE,8,TextUnit_Character,5,bufferEnd,endExclusive,0,docEnd,docEnd,FALSE
|
||||||
TRUE,8,TextUnit_Line,-5,bufferEnd,bufferEnd,-5,bufferEndM4L,bufferEndM4L,FALSE
|
TRUE,8,TextUnit_Line,-5,bufferEnd,bufferEnd,-5,docEndM5L,docEndM5L,FALSE
|
||||||
TRUE,8,TextUnit_Line,-1,bufferEnd,bufferEnd,-1,bufferEndLeft,bufferEndLeft,FALSE
|
TRUE,8,TextUnit_Line,-1,bufferEnd,bufferEnd,-1,docEndM1L,docEndM1L,FALSE
|
||||||
TRUE,8,TextUnit_Line,0,bufferEnd,bufferEnd,0,bufferEnd,bufferEnd,FALSE
|
TRUE,8,TextUnit_Line,0,bufferEnd,bufferEnd,0,docEnd,docEnd,FALSE
|
||||||
TRUE,8,TextUnit_Line,1,bufferEnd,bufferEnd,1,endExclusive,endExclusive,TRUE
|
TRUE,8,TextUnit_Line,1,bufferEnd,bufferEnd,0,docEnd,docEnd,FALSE
|
||||||
TRUE,8,TextUnit_Line,5,bufferEnd,bufferEnd,1,endExclusive,endExclusive,TRUE
|
TRUE,8,TextUnit_Line,5,bufferEnd,bufferEnd,0,docEnd,docEnd,FALSE
|
||||||
FALSE,8,TextUnit_Line,-5,bufferEnd,endExclusive,-5,bufferEndM5L,bufferEndM4L,TRUE
|
FALSE,8,TextUnit_Line,-5,bufferEnd,endExclusive,-5,docEndM5L,docEndM5L,FALSE
|
||||||
FALSE,8,TextUnit_Line,-1,bufferEnd,endExclusive,-1,bufferEndM1L,bufferEndLeft,TRUE
|
FALSE,8,TextUnit_Line,-1,bufferEnd,endExclusive,-1,docEndM1L,docEndM1L,FALSE
|
||||||
FALSE,8,TextUnit_Line,0,bufferEnd,endExclusive,0,bufferEndLeft,endExclusive,TRUE
|
FALSE,8,TextUnit_Line,0,bufferEnd,endExclusive,0,docEnd,docEnd,FALSE
|
||||||
FALSE,8,TextUnit_Line,1,bufferEnd,endExclusive,0,bufferEndLeft,endExclusive,TRUE
|
FALSE,8,TextUnit_Line,1,bufferEnd,endExclusive,0,docEnd,docEnd,FALSE
|
||||||
FALSE,8,TextUnit_Line,5,bufferEnd,endExclusive,0,bufferEndLeft,endExclusive,TRUE
|
FALSE,8,TextUnit_Line,5,bufferEnd,endExclusive,0,docEnd,docEnd,FALSE
|
||||||
TRUE,8,TextUnit_Document,-5,bufferEnd,bufferEnd,-1,origin,origin,FALSE
|
TRUE,8,TextUnit_Document,-5,bufferEnd,bufferEnd,-1,origin,origin,FALSE
|
||||||
TRUE,8,TextUnit_Document,-1,bufferEnd,bufferEnd,-1,origin,origin,FALSE
|
TRUE,8,TextUnit_Document,-1,bufferEnd,bufferEnd,-1,origin,origin,FALSE
|
||||||
TRUE,8,TextUnit_Document,0,bufferEnd,bufferEnd,0,bufferEnd,bufferEnd,FALSE
|
TRUE,8,TextUnit_Document,0,bufferEnd,bufferEnd,0,docEnd,docEnd,FALSE
|
||||||
TRUE,8,TextUnit_Document,1,bufferEnd,bufferEnd,1,endExclusive,endExclusive,TRUE
|
TRUE,8,TextUnit_Document,1,bufferEnd,bufferEnd,0,docEnd,docEnd,FALSE
|
||||||
TRUE,8,TextUnit_Document,5,bufferEnd,bufferEnd,1,endExclusive,endExclusive,TRUE
|
TRUE,8,TextUnit_Document,5,bufferEnd,bufferEnd,0,docEnd,docEnd,FALSE
|
||||||
FALSE,8,TextUnit_Document,-5,bufferEnd,endExclusive,0,origin,endExclusive,TRUE
|
FALSE,8,TextUnit_Document,-5,bufferEnd,endExclusive,-1,origin,origin,FALSE
|
||||||
FALSE,8,TextUnit_Document,-1,bufferEnd,endExclusive,0,origin,endExclusive,TRUE
|
FALSE,8,TextUnit_Document,-1,bufferEnd,endExclusive,-1,origin,origin,FALSE
|
||||||
FALSE,8,TextUnit_Document,0,bufferEnd,endExclusive,0,origin,endExclusive,TRUE
|
FALSE,8,TextUnit_Document,0,bufferEnd,endExclusive,0,docEnd,docEnd,FALSE
|
||||||
FALSE,8,TextUnit_Document,1,bufferEnd,endExclusive,0,origin,endExclusive,TRUE
|
FALSE,8,TextUnit_Document,1,bufferEnd,endExclusive,0,docEnd,docEnd,FALSE
|
||||||
FALSE,8,TextUnit_Document,5,bufferEnd,endExclusive,0,origin,endExclusive,TRUE
|
FALSE,8,TextUnit_Document,5,bufferEnd,endExclusive,0,docEnd,docEnd,FALSE
|
||||||
TRUE,9,TextUnit_Character,-5,endExclusive,endExclusive,-5,bufferEndM4C,bufferEndM4C,FALSE
|
TRUE,9,TextUnit_Character,-5,endExclusive,endExclusive,-5,docEndM5C,docEndM5C,FALSE
|
||||||
TRUE,9,TextUnit_Character,-1,endExclusive,endExclusive,-1,bufferEnd,bufferEnd,FALSE
|
TRUE,9,TextUnit_Character,-1,endExclusive,endExclusive,-1,docEndM1C,docEndM1C,FALSE
|
||||||
TRUE,9,TextUnit_Character,0,endExclusive,endExclusive,0,endExclusive,endExclusive,FALSE
|
TRUE,9,TextUnit_Character,0,endExclusive,endExclusive,0,docEnd,docEnd,FALSE
|
||||||
TRUE,9,TextUnit_Character,1,endExclusive,endExclusive,0,endExclusive,endExclusive,FALSE
|
TRUE,9,TextUnit_Character,1,endExclusive,endExclusive,0,docEnd,docEnd,FALSE
|
||||||
TRUE,9,TextUnit_Character,5,endExclusive,endExclusive,0,endExclusive,endExclusive,FALSE
|
TRUE,9,TextUnit_Character,5,endExclusive,endExclusive,0,docEnd,docEnd,FALSE
|
||||||
TRUE,9,TextUnit_Line,-5,endExclusive,endExclusive,-5,bufferEndM4L,bufferEndM4L,FALSE
|
TRUE,9,TextUnit_Line,-5,endExclusive,endExclusive,-5,docEndM5L,docEndM5L,FALSE
|
||||||
TRUE,9,TextUnit_Line,-1,endExclusive,endExclusive,-1,bufferEndLeft,bufferEndLeft,FALSE
|
TRUE,9,TextUnit_Line,-1,endExclusive,endExclusive,-1,docEndM1L,docEndM1L,FALSE
|
||||||
TRUE,9,TextUnit_Line,0,endExclusive,endExclusive,0,endExclusive,endExclusive,FALSE
|
TRUE,9,TextUnit_Line,0,endExclusive,endExclusive,0,docEnd,docEnd,FALSE
|
||||||
TRUE,9,TextUnit_Line,1,endExclusive,endExclusive,0,endExclusive,endExclusive,FALSE
|
TRUE,9,TextUnit_Line,1,endExclusive,endExclusive,0,docEnd,docEnd,FALSE
|
||||||
TRUE,9,TextUnit_Line,5,endExclusive,endExclusive,0,endExclusive,endExclusive,FALSE
|
TRUE,9,TextUnit_Line,5,endExclusive,endExclusive,0,docEnd,docEnd,FALSE
|
||||||
TRUE,9,TextUnit_Document,-5,endExclusive,endExclusive,-1,origin,origin,FALSE
|
TRUE,9,TextUnit_Document,-5,endExclusive,endExclusive,-1,origin,origin,FALSE
|
||||||
TRUE,9,TextUnit_Document,-1,endExclusive,endExclusive,-1,origin,origin,FALSE
|
TRUE,9,TextUnit_Document,-1,endExclusive,endExclusive,-1,origin,origin,FALSE
|
||||||
TRUE,9,TextUnit_Document,0,endExclusive,endExclusive,0,endExclusive,endExclusive,FALSE
|
TRUE,9,TextUnit_Document,0,endExclusive,endExclusive,0,docEnd,docEnd,FALSE
|
||||||
TRUE,9,TextUnit_Document,1,endExclusive,endExclusive,0,endExclusive,endExclusive,FALSE
|
TRUE,9,TextUnit_Document,1,endExclusive,endExclusive,0,docEnd,docEnd,FALSE
|
||||||
TRUE,9,TextUnit_Document,5,endExclusive,endExclusive,0,endExclusive,endExclusive,FALSE
|
TRUE,9,TextUnit_Document,5,endExclusive,endExclusive,0,docEnd,docEnd,FALSE
|
||||||
TRUE,4,TextUnit_Character,-5,midDocEnd,midDocEnd,-5,midDocEndM5C,midDocEndM5C,TRUE
|
TRUE,4,TextUnit_Character,-5,midDocEnd,midDocEnd,-5,midDocEndM5C,midDocEndM5C,FALSE
|
||||||
TRUE,4,TextUnit_Character,-1,midDocEnd,midDocEnd,-1,midDocEndM1C,midDocEndM1C,TRUE
|
TRUE,4,TextUnit_Character,-1,midDocEnd,midDocEnd,-1,midDocEndM1C,midDocEndM1C,FALSE
|
||||||
TRUE,4,TextUnit_Character,0,midDocEnd,midDocEnd,0,midDocEnd,midDocEnd,TRUE
|
TRUE,4,TextUnit_Character,0,midDocEnd,midDocEnd,0,midDocEnd,midDocEnd,FALSE
|
||||||
TRUE,4,TextUnit_Character,1,midDocEnd,midDocEnd,1,midDocEndP1C,midDocEndP1C,TRUE
|
TRUE,4,TextUnit_Character,1,midDocEnd,midDocEnd,1,midDocEndP1C,midDocEndP1C,FALSE
|
||||||
TRUE,4,TextUnit_Character,5,midDocEnd,midDocEnd,5,midDocEndP5C,midDocEndP5C,TRUE
|
TRUE,4,TextUnit_Character,5,midDocEnd,midDocEnd,5,midDocEndP5C,midDocEndP5C,FALSE
|
||||||
FALSE,4,TextUnit_Character,-5,midDocEnd,midDocEndP1C,-5,midDocEndM5C,midDocEndM4C,TRUE
|
FALSE,4,TextUnit_Character,-5,midDocEnd,midDocEndP1C,-5,midDocEndM5C,midDocEndM4C,FALSE
|
||||||
FALSE,4,TextUnit_Character,-1,midDocEnd,midDocEndP1C,-1,midDocEndM1C,midDocEnd,TRUE
|
FALSE,4,TextUnit_Character,-1,midDocEnd,midDocEndP1C,-1,midDocEndM1C,midDocEnd,FALSE
|
||||||
FALSE,4,TextUnit_Character,0,midDocEnd,midDocEndP1C,0,midDocEnd,midDocEndP1C,TRUE
|
FALSE,4,TextUnit_Character,0,midDocEnd,midDocEndP1C,0,midDocEnd,midDocEndP1C,FALSE
|
||||||
FALSE,4,TextUnit_Character,1,midDocEnd,midDocEndP1C,1,midDocEndP1C,midDocEndP2C,TRUE
|
FALSE,4,TextUnit_Character,1,midDocEnd,midDocEndP1C,1,midDocEndP1C,midDocEndP2C,FALSE
|
||||||
FALSE,4,TextUnit_Character,5,midDocEnd,midDocEndP1C,5,midDocEndP5C,midDocEndP6C,TRUE
|
FALSE,4,TextUnit_Character,5,midDocEnd,midDocEndP1C,5,midDocEndP5C,midDocEndP6C,FALSE
|
||||||
TRUE,4,TextUnit_Line,-5,midDocEnd,midDocEnd,-5,midDocEndM4L,midDocEndM4L,TRUE
|
TRUE,4,TextUnit_Line,-5,midDocEnd,midDocEnd,-5,midDocEndM4L,midDocEndM4L,FALSE
|
||||||
TRUE,4,TextUnit_Line,-1,midDocEnd,midDocEnd,-1,midDocEndLeft,midDocEndLeft,TRUE
|
TRUE,4,TextUnit_Line,-1,midDocEnd,midDocEnd,-1,midDocEndLeft,midDocEndLeft,FALSE
|
||||||
TRUE,4,TextUnit_Line,0,midDocEnd,midDocEnd,0,midDocEnd,midDocEnd,TRUE
|
TRUE,4,TextUnit_Line,0,midDocEnd,midDocEnd,0,midDocEnd,midDocEnd,FALSE
|
||||||
TRUE,4,TextUnit_Line,1,midDocEnd,midDocEnd,1,docEnd,docEnd,TRUE
|
TRUE,4,TextUnit_Line,1,midDocEnd,midDocEnd,1,docEnd,docEnd,FALSE
|
||||||
TRUE,4,TextUnit_Line,5,midDocEnd,midDocEnd,1,docEnd,docEnd,TRUE
|
TRUE,4,TextUnit_Line,5,midDocEnd,midDocEnd,1,docEnd,docEnd,FALSE
|
||||||
FALSE,4,TextUnit_Line,-5,midDocEnd,midDocEndP1C,-5,midDocEndM5L,midDocEndM4L,TRUE
|
FALSE,4,TextUnit_Line,-5,midDocEnd,midDocEndP1C,-5,midDocEndM5L,midDocEndM4L,FALSE
|
||||||
FALSE,4,TextUnit_Line,-1,midDocEnd,midDocEndP1C,-1,midDocEndM1L,midDocEndLeft,TRUE
|
FALSE,4,TextUnit_Line,-1,midDocEnd,midDocEndP1C,-1,midDocEndM1L,midDocEndLeft,FALSE
|
||||||
FALSE,4,TextUnit_Line,0,midDocEnd,midDocEndP1C,0,midDocEndLeft,docEnd,TRUE
|
FALSE,4,TextUnit_Line,0,midDocEnd,midDocEndP1C,0,midDocEndLeft,docEnd,FALSE
|
||||||
FALSE,4,TextUnit_Line,1,midDocEnd,midDocEndP1C,0,midDocEndLeft,docEnd,TRUE
|
FALSE,4,TextUnit_Line,1,midDocEnd,midDocEndP1C,0,midDocEndLeft,docEnd,FALSE
|
||||||
FALSE,4,TextUnit_Line,5,midDocEnd,midDocEndP1C,0,midDocEndLeft,docEnd,TRUE
|
FALSE,4,TextUnit_Line,5,midDocEnd,midDocEndP1C,0,midDocEndLeft,docEnd,FALSE
|
||||||
TRUE,4,TextUnit_Document,-5,midDocEnd,midDocEnd,-1,origin,origin,TRUE
|
TRUE,4,TextUnit_Document,-5,midDocEnd,midDocEnd,-1,origin,origin,FALSE
|
||||||
TRUE,4,TextUnit_Document,-1,midDocEnd,midDocEnd,-1,origin,origin,TRUE
|
TRUE,4,TextUnit_Document,-1,midDocEnd,midDocEnd,-1,origin,origin,FALSE
|
||||||
TRUE,4,TextUnit_Document,0,midDocEnd,midDocEnd,0,midDocEnd,midDocEnd,TRUE
|
TRUE,4,TextUnit_Document,0,midDocEnd,midDocEnd,0,midDocEnd,midDocEnd,FALSE
|
||||||
TRUE,4,TextUnit_Document,1,midDocEnd,midDocEnd,1,docEnd,docEnd,TRUE
|
TRUE,4,TextUnit_Document,1,midDocEnd,midDocEnd,1,docEnd,docEnd,FALSE
|
||||||
TRUE,4,TextUnit_Document,5,midDocEnd,midDocEnd,1,docEnd,docEnd,TRUE
|
TRUE,4,TextUnit_Document,5,midDocEnd,midDocEnd,1,docEnd,docEnd,FALSE
|
||||||
FALSE,4,TextUnit_Document,-5,midDocEnd,midDocEndP1C,0,origin,docEnd,TRUE
|
FALSE,4,TextUnit_Document,-5,midDocEnd,midDocEndP1C,0,origin,docEnd,FALSE
|
||||||
FALSE,4,TextUnit_Document,-1,midDocEnd,midDocEndP1C,0,origin,docEnd,TRUE
|
FALSE,4,TextUnit_Document,-1,midDocEnd,midDocEndP1C,0,origin,docEnd,FALSE
|
||||||
FALSE,4,TextUnit_Document,0,midDocEnd,midDocEndP1C,0,origin,docEnd,TRUE
|
FALSE,4,TextUnit_Document,0,midDocEnd,midDocEndP1C,0,origin,docEnd,FALSE
|
||||||
FALSE,4,TextUnit_Document,1,midDocEnd,midDocEndP1C,0,origin,docEnd,TRUE
|
FALSE,4,TextUnit_Document,1,midDocEnd,midDocEndP1C,0,origin,docEnd,FALSE
|
||||||
FALSE,4,TextUnit_Document,5,midDocEnd,midDocEndP1C,0,origin,docEnd,TRUE
|
FALSE,4,TextUnit_Document,5,midDocEnd,midDocEndP1C,0,origin,docEnd,FALSE
|
||||||
TRUE,5,TextUnit_Character,-5,lastCharPos,lastCharPos,-5,lastCharPosM5C,lastCharPosM5C,TRUE
|
TRUE,5,TextUnit_Character,-5,lastCharPos,lastCharPos,-5,lastCharPosM5C,lastCharPosM5C,FALSE
|
||||||
TRUE,5,TextUnit_Character,-1,lastCharPos,lastCharPos,-1,lastCharPosM1C,lastCharPosM1C,TRUE
|
TRUE,5,TextUnit_Character,-1,lastCharPos,lastCharPos,-1,lastCharPosM1C,lastCharPosM1C,FALSE
|
||||||
TRUE,5,TextUnit_Character,0,lastCharPos,lastCharPos,0,lastCharPos,lastCharPos,TRUE
|
TRUE,5,TextUnit_Character,0,lastCharPos,lastCharPos,0,lastCharPos,lastCharPos,FALSE
|
||||||
TRUE,5,TextUnit_Character,1,lastCharPos,lastCharPos,1,docEnd,docEnd,TRUE
|
TRUE,5,TextUnit_Character,1,lastCharPos,lastCharPos,1,lastCharPosP1C,lastCharPosP1C,FALSE
|
||||||
TRUE,5,TextUnit_Character,5,lastCharPos,lastCharPos,1,docEnd,docEnd,TRUE
|
TRUE,5,TextUnit_Character,5,lastCharPos,lastCharPos,5,lastCharPosP5C,lastCharPosP5C,FALSE
|
||||||
FALSE,5,TextUnit_Character,-5,lastCharPos,lastCharPosP1C,-5,lastCharPosM5C,lastCharPosM4C,TRUE
|
FALSE,5,TextUnit_Character,-5,lastCharPos,lastCharPosP1C,-5,lastCharPosM5C,lastCharPosM4C,FALSE
|
||||||
FALSE,5,TextUnit_Character,-1,lastCharPos,lastCharPosP1C,-1,lastCharPosM1C,lastCharPos,TRUE
|
FALSE,5,TextUnit_Character,-1,lastCharPos,lastCharPosP1C,-1,lastCharPosM1C,lastCharPos,FALSE
|
||||||
FALSE,5,TextUnit_Character,0,lastCharPos,lastCharPosP1C,0,lastCharPos,docEnd,TRUE
|
FALSE,5,TextUnit_Character,0,lastCharPos,lastCharPosP1C,0,lastCharPos,lastCharPosP1C,FALSE
|
||||||
FALSE,5,TextUnit_Character,1,lastCharPos,lastCharPosP1C,0,lastCharPos,docEnd,TRUE
|
FALSE,5,TextUnit_Character,1,lastCharPos,lastCharPosP1C,1,lastCharPosP1C,lastCharPosP2C,FALSE
|
||||||
FALSE,5,TextUnit_Character,5,lastCharPos,lastCharPosP1C,0,lastCharPos,docEnd,TRUE
|
FALSE,5,TextUnit_Character,5,lastCharPos,lastCharPosP1C,5,lastCharPosP5C,lastCharPosP6C,FALSE
|
||||||
TRUE,5,TextUnit_Line,-5,lastCharPos,lastCharPos,-5,lastCharPosM4L,lastCharPosM4L,TRUE
|
TRUE,5,TextUnit_Line,-5,lastCharPos,lastCharPos,-5,lastCharPosM4L,lastCharPosM4L,FALSE
|
||||||
TRUE,5,TextUnit_Line,-1,lastCharPos,lastCharPos,-1,lastCharPosLeft,lastCharPosLeft,TRUE
|
TRUE,5,TextUnit_Line,-1,lastCharPos,lastCharPos,-1,lastCharPosLeft,lastCharPosLeft,FALSE
|
||||||
TRUE,5,TextUnit_Line,0,lastCharPos,lastCharPos,0,lastCharPos,lastCharPos,TRUE
|
TRUE,5,TextUnit_Line,0,lastCharPos,lastCharPos,0,lastCharPos,lastCharPos,FALSE
|
||||||
TRUE,5,TextUnit_Line,1,lastCharPos,lastCharPos,1,docEnd,docEnd,TRUE
|
TRUE,5,TextUnit_Line,1,lastCharPos,lastCharPos,1,docEnd,docEnd,FALSE
|
||||||
TRUE,5,TextUnit_Line,5,lastCharPos,lastCharPos,1,docEnd,docEnd,TRUE
|
TRUE,5,TextUnit_Line,5,lastCharPos,lastCharPos,1,docEnd,docEnd,FALSE
|
||||||
FALSE,5,TextUnit_Line,-5,lastCharPos,lastCharPosP1C,-5,lastCharPosM5L,lastCharPosM4L,TRUE
|
FALSE,5,TextUnit_Line,-5,lastCharPos,lastCharPosP1C,-5,lastCharPosM5L,lastCharPosM4L,FALSE
|
||||||
FALSE,5,TextUnit_Line,-1,lastCharPos,lastCharPosP1C,-1,lastCharPosM1L,lastCharPosLeft,TRUE
|
FALSE,5,TextUnit_Line,-1,lastCharPos,lastCharPosP1C,-1,lastCharPosM1L,lastCharPosLeft,FALSE
|
||||||
FALSE,5,TextUnit_Line,0,lastCharPos,lastCharPosP1C,0,lastCharPosLeft,docEnd,TRUE
|
FALSE,5,TextUnit_Line,0,lastCharPos,lastCharPosP1C,0,lastCharPosLeft,docEnd,FALSE
|
||||||
FALSE,5,TextUnit_Line,1,lastCharPos,lastCharPosP1C,0,lastCharPosLeft,docEnd,TRUE
|
FALSE,5,TextUnit_Line,1,lastCharPos,lastCharPosP1C,0,lastCharPosLeft,docEnd,FALSE
|
||||||
FALSE,5,TextUnit_Line,5,lastCharPos,lastCharPosP1C,0,lastCharPosLeft,docEnd,TRUE
|
FALSE,5,TextUnit_Line,5,lastCharPos,lastCharPosP1C,0,lastCharPosLeft,docEnd,FALSE
|
||||||
TRUE,5,TextUnit_Document,-5,lastCharPos,lastCharPos,-1,origin,origin,TRUE
|
TRUE,5,TextUnit_Document,-5,lastCharPos,lastCharPos,-1,origin,origin,FALSE
|
||||||
TRUE,5,TextUnit_Document,-1,lastCharPos,lastCharPos,-1,origin,origin,TRUE
|
TRUE,5,TextUnit_Document,-1,lastCharPos,lastCharPos,-1,origin,origin,FALSE
|
||||||
TRUE,5,TextUnit_Document,0,lastCharPos,lastCharPos,0,lastCharPos,lastCharPos,TRUE
|
TRUE,5,TextUnit_Document,0,lastCharPos,lastCharPos,0,lastCharPos,lastCharPos,FALSE
|
||||||
TRUE,5,TextUnit_Document,1,lastCharPos,lastCharPos,1,docEnd,docEnd,TRUE
|
TRUE,5,TextUnit_Document,1,lastCharPos,lastCharPos,1,docEnd,docEnd,FALSE
|
||||||
TRUE,5,TextUnit_Document,5,lastCharPos,lastCharPos,1,docEnd,docEnd,TRUE
|
TRUE,5,TextUnit_Document,5,lastCharPos,lastCharPos,1,docEnd,docEnd,FALSE
|
||||||
FALSE,5,TextUnit_Document,-5,lastCharPos,lastCharPosP1C,0,origin,docEnd,TRUE
|
FALSE,5,TextUnit_Document,-5,lastCharPos,lastCharPosP1C,0,origin,docEnd,FALSE
|
||||||
FALSE,5,TextUnit_Document,-1,lastCharPos,lastCharPosP1C,0,origin,docEnd,TRUE
|
FALSE,5,TextUnit_Document,-1,lastCharPos,lastCharPosP1C,0,origin,docEnd,FALSE
|
||||||
FALSE,5,TextUnit_Document,0,lastCharPos,lastCharPosP1C,0,origin,docEnd,TRUE
|
FALSE,5,TextUnit_Document,0,lastCharPos,lastCharPosP1C,0,origin,docEnd,FALSE
|
||||||
FALSE,5,TextUnit_Document,1,lastCharPos,lastCharPosP1C,0,origin,docEnd,TRUE
|
FALSE,5,TextUnit_Document,1,lastCharPos,lastCharPosP1C,0,origin,docEnd,FALSE
|
||||||
FALSE,5,TextUnit_Document,5,lastCharPos,lastCharPosP1C,0,origin,docEnd,TRUE
|
FALSE,5,TextUnit_Document,5,lastCharPos,lastCharPosP1C,0,origin,docEnd,FALSE
|
||||||
TRUE,6,TextUnit_Character,-5,docEnd,docEnd,-5,docEndM5C,docEndM5C,TRUE
|
TRUE,6,TextUnit_Character,-5,docEnd,docEnd,-5,docEndM5C,docEndM5C,FALSE
|
||||||
TRUE,6,TextUnit_Character,-1,docEnd,docEnd,-1,docEndM1C,docEndM1C,TRUE
|
TRUE,6,TextUnit_Character,-1,docEnd,docEnd,-1,docEndM1C,docEndM1C,FALSE
|
||||||
TRUE,6,TextUnit_Character,0,docEnd,docEnd,0,docEnd,docEnd,TRUE
|
TRUE,6,TextUnit_Character,0,docEnd,docEnd,0,docEnd,docEnd,FALSE
|
||||||
TRUE,6,TextUnit_Character,1,docEnd,docEnd,0,docEnd,docEnd,TRUE
|
TRUE,6,TextUnit_Character,1,docEnd,docEnd,0,docEnd,docEnd,FALSE
|
||||||
TRUE,6,TextUnit_Character,5,docEnd,docEnd,0,docEnd,docEnd,TRUE
|
TRUE,6,TextUnit_Character,5,docEnd,docEnd,0,docEnd,docEnd,FALSE
|
||||||
FALSE,6,TextUnit_Character,-5,docEnd,docEndP1C,-5,docEndM5C,docEndM5C,TRUE
|
FALSE,6,TextUnit_Character,-5,docEnd,docEndP1C,-5,docEndM5C,docEndM5C,FALSE
|
||||||
FALSE,6,TextUnit_Character,-1,docEnd,docEndP1C,-1,docEndM1C,docEndM1C,TRUE
|
FALSE,6,TextUnit_Character,-1,docEnd,docEndP1C,-1,docEndM1C,docEndM1C,FALSE
|
||||||
FALSE,6,TextUnit_Character,0,docEnd,docEndP1C,0,docEnd,docEnd,TRUE
|
FALSE,6,TextUnit_Character,0,docEnd,docEndP1C,0,docEnd,docEnd,FALSE
|
||||||
FALSE,6,TextUnit_Character,1,docEnd,docEndP1C,0,docEnd,docEnd,TRUE
|
FALSE,6,TextUnit_Character,1,docEnd,docEndP1C,0,docEnd,docEnd,FALSE
|
||||||
FALSE,6,TextUnit_Character,5,docEnd,docEndP1C,0,docEnd,docEnd,TRUE
|
FALSE,6,TextUnit_Character,5,docEnd,docEndP1C,0,docEnd,docEnd,FALSE
|
||||||
TRUE,6,TextUnit_Line,-5,docEnd,docEnd,-5,docEndM4L,docEndM4L,TRUE
|
TRUE,6,TextUnit_Line,-5,docEnd,docEnd,-5,docEndM5L,docEndM5L,FALSE
|
||||||
TRUE,6,TextUnit_Line,-1,docEnd,docEnd,-1,docEndLeft,docEndLeft,TRUE
|
TRUE,6,TextUnit_Line,-1,docEnd,docEnd,-1,docEndM1L,docEndM1L,FALSE
|
||||||
TRUE,6,TextUnit_Line,0,docEnd,docEnd,0,docEnd,docEnd,TRUE
|
TRUE,6,TextUnit_Line,0,docEnd,docEnd,0,docEnd,docEnd,FALSE
|
||||||
TRUE,6,TextUnit_Line,1,docEnd,docEnd,0,docEnd,docEnd,TRUE
|
TRUE,6,TextUnit_Line,1,docEnd,docEnd,0,docEnd,docEnd,FALSE
|
||||||
TRUE,6,TextUnit_Line,5,docEnd,docEnd,0,docEnd,docEnd,TRUE
|
TRUE,6,TextUnit_Line,5,docEnd,docEnd,0,docEnd,docEnd,FALSE
|
||||||
FALSE,6,TextUnit_Line,-5,docEnd,docEndP1C,-5,docEndM4L,docEndM4L,TRUE
|
FALSE,6,TextUnit_Line,-5,docEnd,docEndP1C,-5,docEndM5L,docEndM5L,FALSE
|
||||||
FALSE,6,TextUnit_Line,-1,docEnd,docEndP1C,-1,docEndLeft,docEndLeft,TRUE
|
FALSE,6,TextUnit_Line,-1,docEnd,docEndP1C,-1,docEndM1L,docEndM1L,FALSE
|
||||||
FALSE,6,TextUnit_Line,0,docEnd,docEndP1C,0,docEnd,docEnd,TRUE
|
FALSE,6,TextUnit_Line,0,docEnd,docEndP1C,0,docEnd,docEnd,FALSE
|
||||||
FALSE,6,TextUnit_Line,1,docEnd,docEndP1C,0,docEnd,docEnd,TRUE
|
FALSE,6,TextUnit_Line,1,docEnd,docEndP1C,0,docEnd,docEnd,FALSE
|
||||||
FALSE,6,TextUnit_Line,5,docEnd,docEndP1C,0,docEnd,docEnd,TRUE
|
FALSE,6,TextUnit_Line,5,docEnd,docEndP1C,0,docEnd,docEnd,FALSE
|
||||||
TRUE,6,TextUnit_Document,-5,docEnd,docEnd,-1,origin,origin,TRUE
|
TRUE,6,TextUnit_Document,-5,docEnd,docEnd,-1,origin,origin,FALSE
|
||||||
TRUE,6,TextUnit_Document,-1,docEnd,docEnd,-1,origin,origin,TRUE
|
TRUE,6,TextUnit_Document,-1,docEnd,docEnd,-1,origin,origin,FALSE
|
||||||
TRUE,6,TextUnit_Document,0,docEnd,docEnd,0,docEnd,docEnd,TRUE
|
TRUE,6,TextUnit_Document,0,docEnd,docEnd,0,docEnd,docEnd,FALSE
|
||||||
TRUE,6,TextUnit_Document,1,docEnd,docEnd,0,docEnd,docEnd,TRUE
|
TRUE,6,TextUnit_Document,1,docEnd,docEnd,0,docEnd,docEnd,FALSE
|
||||||
TRUE,6,TextUnit_Document,5,docEnd,docEnd,0,docEnd,docEnd,TRUE
|
TRUE,6,TextUnit_Document,5,docEnd,docEnd,0,docEnd,docEnd,FALSE
|
||||||
FALSE,6,TextUnit_Document,-5,docEnd,docEndP1C,-1,origin,origin,TRUE
|
FALSE,6,TextUnit_Document,-5,docEnd,docEndP1C,-1,origin,origin,FALSE
|
||||||
FALSE,6,TextUnit_Document,-1,docEnd,docEndP1C,-1,origin,origin,TRUE
|
FALSE,6,TextUnit_Document,-1,docEnd,docEndP1C,-1,origin,origin,FALSE
|
||||||
FALSE,6,TextUnit_Document,0,docEnd,docEndP1C,0,docEnd,docEnd,TRUE
|
FALSE,6,TextUnit_Document,0,docEnd,docEndP1C,0,docEnd,docEnd,FALSE
|
||||||
FALSE,6,TextUnit_Document,1,docEnd,docEndP1C,0,docEnd,docEnd,TRUE
|
FALSE,6,TextUnit_Document,1,docEnd,docEndP1C,0,docEnd,docEnd,FALSE
|
||||||
FALSE,6,TextUnit_Document,5,docEnd,docEndP1C,0,docEnd,docEnd,TRUE
|
FALSE,6,TextUnit_Document,5,docEnd,docEndP1C,0,docEnd,docEnd,FALSE
|
||||||
TRUE,7,TextUnit_Character,-5,midEmptySpace,midEmptySpace,-5,docEndM5C,docEndM5C,TRUE
|
TRUE,7,TextUnit_Character,-5,midEmptySpace,midEmptySpace,-5,docEndM5C,docEndM5C,FALSE
|
||||||
TRUE,7,TextUnit_Character,-1,midEmptySpace,midEmptySpace,-1,docEndM1C,docEndM1C,TRUE
|
TRUE,7,TextUnit_Character,-1,midEmptySpace,midEmptySpace,-1,docEndM1C,docEndM1C,FALSE
|
||||||
TRUE,7,TextUnit_Character,0,midEmptySpace,midEmptySpace,0,docEnd,docEnd,TRUE
|
TRUE,7,TextUnit_Character,0,midEmptySpace,midEmptySpace,0,docEnd,docEnd,FALSE
|
||||||
TRUE,7,TextUnit_Character,1,midEmptySpace,midEmptySpace,0,docEnd,docEnd,TRUE
|
TRUE,7,TextUnit_Character,1,midEmptySpace,midEmptySpace,0,docEnd,docEnd,FALSE
|
||||||
TRUE,7,TextUnit_Character,5,midEmptySpace,midEmptySpace,0,docEnd,docEnd,TRUE
|
TRUE,7,TextUnit_Character,5,midEmptySpace,midEmptySpace,0,docEnd,docEnd,FALSE
|
||||||
FALSE,7,TextUnit_Character,-5,midEmptySpace,midEmptySpaceP1C,-5,docEndM5C,docEndM5C,TRUE
|
FALSE,7,TextUnit_Character,-5,midEmptySpace,midEmptySpaceP1C,-5,docEndM5C,docEndM5C,FALSE
|
||||||
FALSE,7,TextUnit_Character,-1,midEmptySpace,midEmptySpaceP1C,-1,docEndM1C,docEndM1C,TRUE
|
FALSE,7,TextUnit_Character,-1,midEmptySpace,midEmptySpaceP1C,-1,docEndM1C,docEndM1C,FALSE
|
||||||
FALSE,7,TextUnit_Character,0,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,TRUE
|
FALSE,7,TextUnit_Character,0,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,FALSE
|
||||||
FALSE,7,TextUnit_Character,1,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,TRUE
|
FALSE,7,TextUnit_Character,1,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,FALSE
|
||||||
FALSE,7,TextUnit_Character,5,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,TRUE
|
FALSE,7,TextUnit_Character,5,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,FALSE
|
||||||
TRUE,7,TextUnit_Line,-5,midEmptySpace,midEmptySpace,-5,docEndM4L,docEndM4L,TRUE
|
TRUE,7,TextUnit_Line,-5,midEmptySpace,midEmptySpace,-5,docEndM5L,docEndM5L,FALSE
|
||||||
TRUE,7,TextUnit_Line,-1,midEmptySpace,midEmptySpace,-1,docEndLeft,docEndLeft,TRUE
|
TRUE,7,TextUnit_Line,-1,midEmptySpace,midEmptySpace,-1,docEndM1L,docEndM1L,FALSE
|
||||||
TRUE,7,TextUnit_Line,0,midEmptySpace,midEmptySpace,0,docEnd,docEnd,TRUE
|
TRUE,7,TextUnit_Line,0,midEmptySpace,midEmptySpace,0,docEnd,docEnd,FALSE
|
||||||
TRUE,7,TextUnit_Line,1,midEmptySpace,midEmptySpace,0,docEnd,docEnd,TRUE
|
TRUE,7,TextUnit_Line,1,midEmptySpace,midEmptySpace,0,docEnd,docEnd,FALSE
|
||||||
TRUE,7,TextUnit_Line,5,midEmptySpace,midEmptySpace,0,docEnd,docEnd,TRUE
|
TRUE,7,TextUnit_Line,5,midEmptySpace,midEmptySpace,0,docEnd,docEnd,FALSE
|
||||||
FALSE,7,TextUnit_Line,-5,midEmptySpace,midEmptySpaceP1C,-5,docEndM4L,docEndM4L,TRUE
|
FALSE,7,TextUnit_Line,-5,midEmptySpace,midEmptySpaceP1C,-5,docEndM5L,docEndM5L,FALSE
|
||||||
FALSE,7,TextUnit_Line,-1,midEmptySpace,midEmptySpaceP1C,-1,docEndLeft,docEndLeft,TRUE
|
FALSE,7,TextUnit_Line,-1,midEmptySpace,midEmptySpaceP1C,-1,docEndM1L,docEndM1L,FALSE
|
||||||
FALSE,7,TextUnit_Line,0,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,TRUE
|
FALSE,7,TextUnit_Line,0,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,FALSE
|
||||||
FALSE,7,TextUnit_Line,1,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,TRUE
|
FALSE,7,TextUnit_Line,1,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,FALSE
|
||||||
FALSE,7,TextUnit_Line,5,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,TRUE
|
FALSE,7,TextUnit_Line,5,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,FALSE
|
||||||
TRUE,7,TextUnit_Document,-5,midEmptySpace,midEmptySpace,-1,origin,origin,TRUE
|
TRUE,7,TextUnit_Document,-5,midEmptySpace,midEmptySpace,-1,origin,origin,FALSE
|
||||||
TRUE,7,TextUnit_Document,-1,midEmptySpace,midEmptySpace,-1,origin,origin,TRUE
|
TRUE,7,TextUnit_Document,-1,midEmptySpace,midEmptySpace,-1,origin,origin,FALSE
|
||||||
TRUE,7,TextUnit_Document,0,midEmptySpace,midEmptySpace,0,docEnd,docEnd,TRUE
|
TRUE,7,TextUnit_Document,0,midEmptySpace,midEmptySpace,0,docEnd,docEnd,FALSE
|
||||||
TRUE,7,TextUnit_Document,1,midEmptySpace,midEmptySpace,0,docEnd,docEnd,TRUE
|
TRUE,7,TextUnit_Document,1,midEmptySpace,midEmptySpace,0,docEnd,docEnd,FALSE
|
||||||
TRUE,7,TextUnit_Document,5,midEmptySpace,midEmptySpace,0,docEnd,docEnd,TRUE
|
TRUE,7,TextUnit_Document,5,midEmptySpace,midEmptySpace,0,docEnd,docEnd,FALSE
|
||||||
FALSE,7,TextUnit_Document,-5,midEmptySpace,midEmptySpaceP1C,-1,origin,origin,TRUE
|
FALSE,7,TextUnit_Document,-5,midEmptySpace,midEmptySpaceP1C,-1,origin,origin,FALSE
|
||||||
FALSE,7,TextUnit_Document,-1,midEmptySpace,midEmptySpaceP1C,-1,origin,origin,TRUE
|
FALSE,7,TextUnit_Document,-1,midEmptySpace,midEmptySpaceP1C,-1,origin,origin,FALSE
|
||||||
FALSE,7,TextUnit_Document,0,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,TRUE
|
FALSE,7,TextUnit_Document,0,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,FALSE
|
||||||
FALSE,7,TextUnit_Document,1,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,TRUE
|
FALSE,7,TextUnit_Document,1,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,FALSE
|
||||||
FALSE,7,TextUnit_Document,5,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,TRUE
|
FALSE,7,TextUnit_Document,5,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,FALSE
|
||||||
|
TRUE,1,TextUnit_Word,-5,origin,origin,0,origin,origin,FALSE
|
||||||
|
TRUE,1,TextUnit_Word,-1,origin,origin,0,origin,origin,FALSE
|
||||||
|
TRUE,1,TextUnit_Word,0,origin,origin,0,origin,origin,FALSE
|
||||||
|
TRUE,1,TextUnit_Word,1,origin,origin,1,segment1LmidTop,segment1LmidTop,FALSE
|
||||||
|
TRUE,1,TextUnit_Word,5,origin,origin,5,segment0LmidTopP1L,segment0LmidTopP1L,FALSE
|
||||||
|
FALSE,1,TextUnit_Word,-5,origin,originP1C,0,origin,segment1LmidTop,FALSE
|
||||||
|
FALSE,1,TextUnit_Word,-1,origin,originP1C,0,origin,segment1LmidTop,FALSE
|
||||||
|
FALSE,1,TextUnit_Word,0,origin,originP1C,0,origin,segment1LmidTop,FALSE
|
||||||
|
FALSE,1,TextUnit_Word,1,origin,originP1C,1,segment1LmidTop,segment2LmidTop,FALSE
|
||||||
|
FALSE,1,TextUnit_Word,5,origin,originP1C,5,segment0LmidTopP1L,segment1LmidTopP1L,FALSE
|
||||||
|
TRUE,2,TextUnit_Word,-5,midTop,midTop,-3,origin,origin,TRUE
|
||||||
|
TRUE,2,TextUnit_Word,-1,midTop,midTop,-1,segment2LmidTop,segment2LmidTop,TRUE
|
||||||
|
TRUE,2,TextUnit_Word,0,midTop,midTop,0,midTop,midTop,FALSE
|
||||||
|
TRUE,2,TextUnit_Word,1,midTop,midTop,1,segment3LmidTop,segment3LmidTop,FALSE
|
||||||
|
TRUE,2,TextUnit_Word,5,midTop,midTop,5,segment2LmidTopP1L,segment2LmidTopP1L,FALSE
|
||||||
|
FALSE,2,TextUnit_Word,-5,midTop,midTopP1C,-2,origin,segment1LmidTop,FALSE
|
||||||
|
FALSE,2,TextUnit_Word,-1,midTop,midTopP1C,-1,segment1LmidTop,segment2LmidTop,FALSE
|
||||||
|
FALSE,2,TextUnit_Word,0,midTop,midTopP1C,0,segment2LmidTop,segment3LmidTop,FALSE
|
||||||
|
FALSE,2,TextUnit_Word,1,midTop,midTopP1C,1,segment3LmidTop,segment4LmidTop,FALSE
|
||||||
|
FALSE,2,TextUnit_Word,5,midTop,midTopP1C,5,segment2LmidTopP1L,segment3LmidTopP1L,FALSE
|
||||||
|
TRUE,3,TextUnit_Word,-5,midHistory,midHistory,-5,segment3LmidHistoryM1L,segment3LmidHistoryM1L,TRUE
|
||||||
|
TRUE,3,TextUnit_Word,-1,midHistory,midHistory,-1,segment2LmidHistory,segment2LmidHistory,TRUE
|
||||||
|
TRUE,3,TextUnit_Word,0,midHistory,midHistory,0,midHistory,midHistory,FALSE
|
||||||
|
TRUE,3,TextUnit_Word,1,midHistory,midHistory,1,segment3LmidHistory,segment3LmidHistory,FALSE
|
||||||
|
TRUE,3,TextUnit_Word,5,midHistory,midHistory,5,segment2LmidHistoryP1L,segment2LmidHistoryP1L,FALSE
|
||||||
|
FALSE,3,TextUnit_Word,-5,midHistory,midHistoryP1C,-5,segment3LmidHistoryM1L,segment4LmidHistoryM1L,TRUE
|
||||||
|
FALSE,3,TextUnit_Word,-1,midHistory,midHistoryP1C,-1,segment1LmidHistory,segment2LmidHistory,FALSE
|
||||||
|
FALSE,3,TextUnit_Word,0,midHistory,midHistoryP1C,0,segment2LmidHistory,segment3LmidHistory,FALSE
|
||||||
|
FALSE,3,TextUnit_Word,1,midHistory,midHistoryP1C,1,segment3LmidHistory,segment4LmidHistory,FALSE
|
||||||
|
FALSE,3,TextUnit_Word,5,midHistory,midHistoryP1C,5,segment2LmidHistoryP1L,segment3LmidHistoryP1L,FALSE
|
||||||
|
TRUE,4,TextUnit_Word,-5,midDocEnd,midDocEnd,-5,segment3LmidDocEndM1L,segment3LmidDocEndM1L,TRUE
|
||||||
|
TRUE,4,TextUnit_Word,-1,midDocEnd,midDocEnd,-1,segment2LmidDocEnd,segment2LmidDocEnd,TRUE
|
||||||
|
TRUE,4,TextUnit_Word,0,midDocEnd,midDocEnd,0,midDocEnd,midDocEnd,FALSE
|
||||||
|
TRUE,4,TextUnit_Word,1,midDocEnd,midDocEnd,1,segment3LmidDocEnd,segment3LmidDocEnd,FALSE
|
||||||
|
TRUE,4,TextUnit_Word,5,midDocEnd,midDocEnd,3,docEnd,docEnd,FALSE
|
||||||
|
FALSE,4,TextUnit_Word,-5,midDocEnd,midDocEndP1C,-5,segment3LmidDocEndM1L,segment4LmidDocEndM1L,TRUE
|
||||||
|
FALSE,4,TextUnit_Word,-1,midDocEnd,midDocEndP1C,-1,segment1LmidDocEnd,segment2LmidDocEnd,FALSE
|
||||||
|
FALSE,4,TextUnit_Word,0,midDocEnd,midDocEndP1C,0,segment2LmidDocEnd,segment3LmidDocEnd,FALSE
|
||||||
|
FALSE,4,TextUnit_Word,1,midDocEnd,midDocEndP1C,1,segment3LmidDocEnd,segment4LmidDocEnd,FALSE
|
||||||
|
FALSE,4,TextUnit_Word,5,midDocEnd,midDocEndP1C,2,segment4LmidDocEnd,docEnd,FALSE
|
||||||
|
TRUE,5,TextUnit_Word,-5,lastCharPos,lastCharPos,-5,lastCharPosLeft,lastCharPosLeft,TRUE
|
||||||
|
TRUE,5,TextUnit_Word,-1,lastCharPos,lastCharPos,-1,segment4LmidDocEnd,segment4LmidDocEnd,TRUE
|
||||||
|
TRUE,5,TextUnit_Word,0,lastCharPos,lastCharPos,0,lastCharPos,lastCharPos,FALSE
|
||||||
|
TRUE,5,TextUnit_Word,1,lastCharPos,lastCharPos,1,docEnd,docEnd,FALSE
|
||||||
|
TRUE,5,TextUnit_Word,5,lastCharPos,lastCharPos,1,docEnd,docEnd,FALSE
|
||||||
|
FALSE,5,TextUnit_Word,-5,lastCharPos,lastCharPosP1C,-5,segment4LlastCharPosM1L,lastCharPosLeft,FALSE
|
||||||
|
FALSE,5,TextUnit_Word,-1,lastCharPos,lastCharPosP1C,-1,segment3LmidDocEnd,segment4LmidDocEnd,FALSE
|
||||||
|
FALSE,5,TextUnit_Word,0,lastCharPos,lastCharPosP1C,0,segment4LmidDocEnd,docEnd,FALSE
|
||||||
|
FALSE,5,TextUnit_Word,1,lastCharPos,lastCharPosP1C,0,segment4LmidDocEnd,docEnd,FALSE
|
||||||
|
FALSE,5,TextUnit_Word,5,lastCharPos,lastCharPosP1C,0,segment4LmidDocEnd,docEnd,FALSE
|
||||||
|
TRUE,6,TextUnit_Word,-5,docEnd,docEnd,-5,midDocEndLeft,midDocEndLeft,TRUE
|
||||||
|
TRUE,6,TextUnit_Word,-1,docEnd,docEnd,-1,segment4LmidDocEnd,segment4LmidDocEnd,TRUE
|
||||||
|
TRUE,6,TextUnit_Word,0,docEnd,docEnd,0,docEnd,docEnd,FALSE
|
||||||
|
TRUE,6,TextUnit_Word,1,docEnd,docEnd,0,docEnd,docEnd,FALSE
|
||||||
|
TRUE,6,TextUnit_Word,5,docEnd,docEnd,0,docEnd,docEnd,FALSE
|
||||||
|
FALSE,6,TextUnit_Word,-5,docEnd,docEndP1C,-5,midDocEndLeft,midDocEndLeft,TRUE
|
||||||
|
FALSE,6,TextUnit_Word,-1,docEnd,docEndP1C,-1,segment4LmidDocEnd,segment4LmidDocEnd,TRUE
|
||||||
|
FALSE,6,TextUnit_Word,0,docEnd,docEndP1C,0,docEnd,docEnd,FALSE
|
||||||
|
FALSE,6,TextUnit_Word,1,docEnd,docEndP1C,0,docEnd,docEnd,FALSE
|
||||||
|
FALSE,6,TextUnit_Word,5,docEnd,docEndP1C,0,docEnd,docEnd,FALSE
|
||||||
|
TRUE,7,TextUnit_Word,-5,midEmptySpace,midEmptySpace,-5,midDocEndLeft,midDocEndLeft,TRUE
|
||||||
|
TRUE,7,TextUnit_Word,-1,midEmptySpace,midEmptySpace,-1,segment4LmidDocEnd,segment4LmidDocEnd,TRUE
|
||||||
|
TRUE,7,TextUnit_Word,0,midEmptySpace,midEmptySpace,0,docEnd,docEnd,FALSE
|
||||||
|
TRUE,7,TextUnit_Word,1,midEmptySpace,midEmptySpace,0,docEnd,docEnd,FALSE
|
||||||
|
TRUE,7,TextUnit_Word,5,midEmptySpace,midEmptySpace,0,docEnd,docEnd,FALSE
|
||||||
|
FALSE,7,TextUnit_Word,-5,midEmptySpace,midEmptySpaceP1C,-5,midDocEndLeft,midDocEndLeft,TRUE
|
||||||
|
FALSE,7,TextUnit_Word,-1,midEmptySpace,midEmptySpaceP1C,-1,segment4LmidDocEnd,segment4LmidDocEnd,TRUE
|
||||||
|
FALSE,7,TextUnit_Word,0,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,FALSE
|
||||||
|
FALSE,7,TextUnit_Word,1,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,FALSE
|
||||||
|
FALSE,7,TextUnit_Word,5,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,FALSE
|
||||||
|
TRUE,8,TextUnit_Word,-5,bufferEnd,bufferEnd,-5,midDocEndLeft,midDocEndLeft,TRUE
|
||||||
|
TRUE,8,TextUnit_Word,-1,bufferEnd,bufferEnd,-1,segment4LmidDocEnd,segment4LmidDocEnd,TRUE
|
||||||
|
TRUE,8,TextUnit_Word,0,bufferEnd,bufferEnd,0,docEnd,docEnd,FALSE
|
||||||
|
TRUE,8,TextUnit_Word,1,bufferEnd,bufferEnd,0,docEnd,docEnd,FALSE
|
||||||
|
TRUE,8,TextUnit_Word,5,bufferEnd,bufferEnd,0,docEnd,docEnd,FALSE
|
||||||
|
FALSE,8,TextUnit_Word,-5,bufferEnd,endExclusive,-5,midDocEndLeft,midDocEndLeft,TRUE
|
||||||
|
FALSE,8,TextUnit_Word,-1,bufferEnd,endExclusive,-1,segment4LmidDocEnd,segment4LmidDocEnd,TRUE
|
||||||
|
FALSE,8,TextUnit_Word,0,bufferEnd,endExclusive,0,docEnd,docEnd,FALSE
|
||||||
|
FALSE,8,TextUnit_Word,1,bufferEnd,endExclusive,0,docEnd,docEnd,FALSE
|
||||||
|
FALSE,8,TextUnit_Word,5,bufferEnd,endExclusive,0,docEnd,docEnd,FALSE
|
||||||
|
TRUE,9,TextUnit_Word,-5,endExclusive,endExclusive,-5,midDocEndLeft,midDocEndLeft,TRUE
|
||||||
|
TRUE,9,TextUnit_Word,-1,endExclusive,endExclusive,-1,segment4LmidDocEnd,segment4LmidDocEnd,TRUE
|
||||||
|
TRUE,9,TextUnit_Word,0,endExclusive,endExclusive,0,docEnd,docEnd,FALSE
|
||||||
|
TRUE,9,TextUnit_Word,1,endExclusive,endExclusive,0,docEnd,docEnd,FALSE
|
||||||
|
TRUE,9,TextUnit_Word,5,endExclusive,endExclusive,0,docEnd,docEnd,FALSE
|
||||||
|
|
|
Loading…
Reference in a new issue