[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:
Carlos Zamora 2021-09-16 13:44:29 -07:00 committed by GitHub
parent 4793541c90
commit d08afc4e88
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 2064 additions and 710 deletions

View File

@ -35,6 +35,9 @@ liga
lje
locl
lorem
Llast
Lmid
Lorigin
maxed
mkmk
mru

View File

@ -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.
// Otherwise, expand left until a character of a new delimiter class is found
// (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:
// - 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:
// " 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
#pragma warning(suppress : 26496)
// GH#7664: Treat EndExclusive as EndInclusive so
// that it actually points to a space in the buffer
auto copy{ target };
const auto bufferSize{ GetSize() };
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
if (target == bufferSize.Origin())
{
// can't expand left
@ -1083,9 +1083,15 @@ const COORD TextBuffer::GetWordStart(const COORD target, const std::wstring_view
}
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() };
}
else if (bufferSize.CompareInBounds(target, limit, true) >= 0)
{
// if at/past the limit --> clamp to limit
copy = *limitOptional;
}
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
// Otherwise, expand right until a character of a new delimiter class is found
// (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:
// - 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:
// " 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 "]
// NOTE: the end anchor (this one) is exclusive, whereas the start anchor (GetWordStart) is inclusive
// Already at the end. Can't move forward.
if (target == GetSize().EndExclusive())
// Already at/past the limit. Can't move forward.
const auto bufferSize{ GetSize() };
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
if (bufferSize.CompareInBounds(target, limit, true) >= 0)
{
return target;
}
if (accessibilityMode)
{
const auto lastCharPos{ GetLastNonSpaceCharacter() };
return _GetWordEndForAccessibility(target, wordDelimiters, lastCharPos);
return _GetWordEndForAccessibility(target, wordDelimiters, limit);
}
else
{
@ -1215,44 +1223,46 @@ const COORD TextBuffer::GetWordEnd(const COORD target, const std::wstring_view w
// Arguments:
// - target - a COORD on the word you are currently on
// - 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:
// - 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();
COORD result = target;
const auto bufferSize{ GetSize() };
COORD result{ target };
// Check if we're already on/past the last RegularChar
if (bufferSize.CompareInBounds(result, lastCharPos, true) >= 0)
if (bufferSize.CompareInBounds(target, limit, 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);
}
// ignore right boundary. Continue through readable text found
while (_GetDelimiterClassAt(result, wordDelimiters) == DelimiterClass::RegularChar)
else
{
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
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))
while (iter && iter.Pos() != limit && _GetDelimiterClassAt(iter.Pos(), wordDelimiters) != DelimiterClass::RegularChar)
{
// we are at the EndInclusive COORD
// this signifies that we must include the last char in the buffer
// but the position of the COORD points to nothing
break;
// expand to the beginning of the NEXT word
++iter;
}
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:
// - pos - a COORD on the word you are currently on
// - 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:
// - 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)
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
// NOTE: _GetWordEnd...() returns the exclusive position of the "end of the 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;
}
@ -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
// Arguments:
// - 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:
// - 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;
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);
}
@ -1419,12 +1435,19 @@ const til::point TextBuffer::GetGlyphStart(const til::point pos) const
// - pos - a COORD on the word you are currently on
// Return Value:
// - 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;
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);
}
@ -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
// Arguments:
// - 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:
// - 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)
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 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;
}
// try to move. If we can't, we're done.
const bool success = bufferSize.IncrementInBounds(resultPos, allowBottomExclusive);
if (resultPos != bufferSize.EndExclusive() && GetCellDataAt(resultPos)->DbcsAttr().IsTrailing())
// Try to move forward, but if we hit the buffer boundary, we fail to move.
auto iter{ GetCellDataAt(pos, bufferSize) };
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;
}
@ -1471,12 +1508,21 @@ bool TextBuffer::MoveToNextGlyph(til::point& pos, bool allowBottomExclusive) con
// Return Value:
// - 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)
bool TextBuffer::MoveToPreviousGlyph(til::point& pos) const
bool TextBuffer::MoveToPreviousGlyph(til::point& pos, std::optional<til::point> limitOptional) const
{
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.
const auto bufferSize = GetSize();
const bool success = bufferSize.DecrementInBounds(resultPos, true);
if (resultPos != bufferSize.EndExclusive() && GetCellDataAt(resultPos)->DbcsAttr().IsLeading())
{

View File

@ -141,15 +141,15 @@ public:
Microsoft::Console::Render::IRenderTarget& GetRenderTarget() noexcept;
const COORD GetWordStart(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) const;
bool MoveToNextWord(COORD& pos, const std::wstring_view wordDelimiters, COORD lastCharPos) 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, std::optional<til::point> limitOptional = std::nullopt) 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;
const til::point GetGlyphStart(const til::point pos) const;
const til::point GetGlyphEnd(const til::point pos) const;
bool MoveToNextGlyph(til::point& pos, bool allowBottomExclusive = false) const;
bool MoveToPreviousGlyph(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, std::optional<til::point> limitOptional = std::nullopt) const;
bool MoveToNextGlyph(til::point& pos, bool allowBottomExclusive = false, std::optional<til::point> limitOptional = std::nullopt) 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;
@ -242,7 +242,7 @@ private:
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 _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;
void _PruneHyperlinks();

View File

@ -349,14 +349,29 @@ class UiaTextRangeTests
_pTextBuffer = &_pScreenInfo->GetTextBuffer();
_pUiaData = &gci.renderData;
// fill text buffer with text
for (UINT i = 0; i < _pTextBuffer->TotalRowCount(); ++i)
// GH#6986: document end now limits the navigation to be
// 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);
auto& charRow = row.GetCharRow();
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)
{
const SHORT lastColumnIndex = _pScreenInfo->GetBufferSize().Width() - 1;
const SHORT lastColumnIndex = _pScreenInfo->GetBufferSize().RightInclusive();
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
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{
L"can move to a new row when necessary when moving forward",
{ lastColumnIndex, 0 },
@ -782,7 +813,7 @@ class UiaTextRangeTests
int amountMoved;
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.start, utr->_start);
@ -795,6 +826,10 @@ class UiaTextRangeTests
const SHORT lastColumnIndex = _pScreenInfo->GetBufferSize().Width() - 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
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{
L"can move backward from bottom row",
{0, bottomRow},
{lastColumnIndex, bottomRow},
{0, documentEnd.Y},
{lastColumnIndex, documentEnd.Y},
-3,
{
-3,
{0, bottomRow - 3},
{0, bottomRow - 2}
{0, base::ClampSub(documentEnd.Y, 3)},
{0, base::ClampSub(documentEnd.Y, 3)}
}
},
@ -868,6 +915,10 @@ class UiaTextRangeTests
const SHORT lastColumnIndex = _pScreenInfo->GetBufferSize().Width() - 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
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{
L"_start follows _end when passed during movement",
{5, 0},
@ -925,40 +989,40 @@ class UiaTextRangeTests
MoveEndpointTest{
L"can't move _end past the beginning of the document when _end is positioned at the end",
{0, bottomRow},
{0, bottomRow+1},
{0, documentEnd.Y},
{0, base::ClampAdd(documentEnd.Y,1)},
1,
TextPatternRangeEndpoint_End,
{
0,
{0, bottomRow},
{0, bottomRow+1},
{0, documentEnd.Y},
{0, base::ClampAdd(documentEnd.Y,1)},
}
},
MoveEndpointTest{
L"can partially move _end to the end of the document when it is closer than the move count requested",
{0, 0},
{lastColumnIndex - 3, bottomRow},
{base::ClampSub(lastColumnIndex, 3), documentEnd.Y},
5,
TextPatternRangeEndpoint_End,
{
4,
{0, 0},
{0, bottomRow+1},
{0, base::ClampAdd(documentEnd.Y,1)},
}
},
MoveEndpointTest{
L"can't move _start past the end of the document",
{lastColumnIndex - 4, bottomRow},
{0, bottomRow+1},
{base::ClampSub(lastColumnIndex, 4), documentEnd.Y},
{0, base::ClampAdd(documentEnd.Y,1)},
5,
TextPatternRangeEndpoint_Start,
{
5,
{0, bottomRow+1},
{0, bottomRow+1},
{0, base::ClampAdd(documentEnd.Y,1)},
{0, base::ClampAdd(documentEnd.Y,1)},
}
},
@ -997,6 +1061,10 @@ class UiaTextRangeTests
const SHORT lastColumnIndex = _pScreenInfo->GetBufferSize().Width() - 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
const std::vector<MoveEndpointTest> testData
{
@ -1067,36 +1135,36 @@ class UiaTextRangeTests
},
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},
{lastColumnIndex - 3, bottomRow},
1,
TextPatternRangeEndpoint_End,
1,
0,
{0, 0},
{0, bottomRow+1}
documentEnd
},
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, bottomRow+1},
1,
TextPatternRangeEndpoint_End,
0,
{0, 0},
{0, bottomRow+1}
documentEnd
},
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},
{lastColumnIndex, bottomRow},
1,
TextPatternRangeEndpoint_Start,
1,
{0, bottomRow+1},
{0, bottomRow+1}
0,
documentEnd,
documentEnd
},
MoveEndpointTest{
@ -1132,6 +1200,10 @@ class UiaTextRangeTests
const SHORT lastColumnIndex = _pScreenInfo->GetBufferSize().Width() - 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
const std::vector<MoveEndpointTest> testData =
{
@ -1144,7 +1216,7 @@ class UiaTextRangeTests
{
1,
{0, 4},
{0, bottomRow+1}
documentEnd
}
},
@ -1162,7 +1234,7 @@ class UiaTextRangeTests
},
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},
{0, bottomRow+1},
1,
@ -1170,7 +1242,7 @@ class UiaTextRangeTests
{
0,
{3, 2},
{0, bottomRow+1}
documentEnd
}
},
@ -1208,8 +1280,8 @@ class UiaTextRangeTests
TextPatternRangeEndpoint_Start,
{
1,
{0, bottomRow+1},
{0, bottomRow+1}
documentEnd,
documentEnd
}
}
};
@ -1235,51 +1307,64 @@ class UiaTextRangeTests
// GH#7664: When attempting to expand to an enclosing unit
// at the end exclusive, the UTR should refuse to move past
// the end.
const auto lastNonspaceCharPos{ _pTextBuffer->GetLastNonSpaceCharacter() };
const COORD documentEnd{ 0, lastNonspaceCharPos.Y + 1 };
const til::point endInclusive{ bufferEnd };
// Iterate over each TextUnit. If the we don't support
// Iterate over each TextUnit. If we don't support
// the given TextUnit, we're supposed to fallback
// 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;
for (int unit = TextUnit::TextUnit_Character; unit != TextUnit::TextUnit_Document; ++unit)
{
Log::Comment(NoThrowString().Format(L"%s", toString(static_cast<TextUnit>(unit))));
Log::Comment(NoThrowString().Format(L"%s", toString(static_cast<TextUnit>(textUnit))));
// Create a degenerate UTR at EndExclusive
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr, _pUiaData, &_dummyProvider, endInclusive, endExclusive));
THROW_IF_FAILED(utr->ExpandToEnclosingUnit(static_cast<TextUnit>(unit)));
// Create a degenerate UTR at EndExclusive
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr, _pUiaData, &_dummyProvider, bufferEnd, endExclusive));
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)
{
// GH#7663: When attempting to move from end exclusive,
// 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 };
// write "temp" at (2,2)
_pTextBuffer->Reset();
const til::point writeTarget{ 2, 2 };
_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
// the given TextUnit, we're supposed to fallback
// 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}")
TEST_METHOD_PROPERTY(L"Data:degenerate", L"{false, true}")
TEST_METHOD_PROPERTY(L"Data:atDocumentEnd", L"{false, true}")
END_TEST_METHOD_PROPERTIES();
int unit;
bool degenerate;
bool atDocumentEnd;
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"atDocumentEnd", atDocumentEnd), L"Get atDocumentEnd variant");
TextUnit textUnit{ static_cast<TextUnit>(unit) };
Microsoft::WRL::ComPtr<UiaTextRange> utr;
@ -1287,17 +1372,22 @@ class UiaTextRangeTests
Log::Comment(NoThrowString().Format(L"Forward by %s", toString(textUnit)));
// Create an UTR at EndExclusive
const auto utrEnd{ atDocumentEnd ? documentEndExclusive : endExclusive };
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
{
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));
VERIFY_ARE_EQUAL(endExclusive, til::point{ utr->_end });
VERIFY_ARE_EQUAL(documentEndExclusive, utr->_end);
VERIFY_ARE_EQUAL(0, moveAmt);
// Verify expansion works properly
@ -1305,33 +1395,35 @@ class UiaTextRangeTests
THROW_IF_FAILED(utr->ExpandToEnclosingUnit(textUnit));
if (textUnit <= TextUnit::TextUnit_Character)
{
VERIFY_ARE_EQUAL(endInclusive, til::point{ utr->_start });
VERIFY_ARE_EQUAL(endExclusive, til::point{ utr->_end });
VERIFY_ARE_EQUAL(documentEndInclusive, utr->_start);
VERIFY_ARE_EQUAL(documentEndExclusive, utr->_end);
}
else if (textUnit <= TextUnit::TextUnit_Word)
{
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)
{
VERIFY_ARE_EQUAL(lastLineStart, til::point{ utr->_start });
VERIFY_ARE_EQUAL(endExclusive, til::point{ utr->_end });
VERIFY_ARE_EQUAL(lastLineStart, utr->_start);
VERIFY_ARE_EQUAL(documentEndExclusive, utr->_end);
}
else // textUnit <= TextUnit::TextUnit_Document:
{
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
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
{
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
@ -1345,26 +1437,26 @@ class UiaTextRangeTests
// - degenerate --> it moves with _start to stay degenerate
// - !degenerate --> it excludes the last char, to select the second to last char
VERIFY_ARE_EQUAL(-1, moveAmt);
VERIFY_ARE_EQUAL(degenerate ? endInclusive : secondToLastCharacterPos, til::point{ utr->_start });
VERIFY_ARE_EQUAL(endInclusive, til::point{ utr->_end });
VERIFY_ARE_EQUAL(degenerate || !atDocumentEnd ? documentEndInclusive : secondToLastCharacterPos, utr->_start);
VERIFY_ARE_EQUAL(documentEndInclusive, utr->_end);
}
else if (textUnit <= TextUnit::TextUnit_Word)
{
VERIFY_ARE_EQUAL(-1, moveAmt);
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)
{
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 });
}
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(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)
{
// Populate the buffer with...
// - 9 segments of alternating text
// - 10 segments of alternating text
// - up to half of the buffer (vertically)
// 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;
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)
{
bool fill{ true };
if (i % segment == 0)
if (iter.Pos().X == bufferSize.left())
{
fill = true;
}
else if (i % segment == 0)
{
fill = !fill;
}
if (fill)
{
_pTextBuffer->Write({ L"X" }, iter.Pos());
}
_pTextBuffer->Write({ fill ? L"X" : L" " }, iter.Pos());
++i;
++iter;

View File

@ -280,52 +280,50 @@ IFACEMETHODIMP UiaTextRangeBase::ExpandToEnclosingUnit(_In_ TextUnit unit) noexc
void UiaTextRangeBase::_expandToEnclosingUnit(TextUnit unit)
{
const auto& buffer = _pData->GetTextBuffer();
const auto bufferSize = _getBufferSize();
const auto bufferEnd = bufferSize.EndExclusive();
const auto bufferSize{ buffer.GetSize() };
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)
{
_start = buffer.GetGlyphStart(_start);
_end = buffer.GetGlyphEnd(_start);
_start = buffer.GetGlyphStart(_start, documentEnd);
_end = buffer.GetGlyphEnd(_start, documentEnd);
}
else if (unit <= TextUnit_Word)
{
// expand to word
_start = buffer.GetWordStart(_start, _wordDelimiters, true);
_end = buffer.GetWordEnd(_start, _wordDelimiters, true);
// 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;
}
_start = buffer.GetWordStart(_start, _wordDelimiters, true, documentEnd);
_end = buffer.GetWordEnd(_start, _wordDelimiters, true, documentEnd);
}
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,
// move _start back one, instead of _end forward
_start.X = 0;
_start.Y = base::ClampSub(_start.Y, 1);
_end = bufferEnd;
// we're on the last line
_end = documentEnd;
bufferSize.IncrementInBounds(_end, true);
}
else
{
// expand to line
_start.X = 0;
_end.X = 0;
_end.Y = base::ClampAdd(_start.Y, 1);
}
}
else
{
// TODO GH#6986: properly handle "end of buffer" as last character
// instead of last cell
// expand to document
_start = bufferSize.Origin();
_end = bufferSize.EndExclusive();
_end = documentEnd;
}
}
@ -608,7 +606,7 @@ try
*ppRetVal = nullptr;
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;
auto searchDirection = Search::Direction::Forward;
@ -1016,11 +1014,24 @@ try
_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();
if (count != 0)
{
// We can abstract this movement by moving _start
constexpr auto endpoint = TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start;
const auto preventBoundary = !wasDegenerate;
if (unit == TextUnit::TextUnit_Character)
{
@ -1028,13 +1039,7 @@ try
}
else if (unit <= TextUnit::TextUnit_Word)
{
// TODO GH#10925: passing in "true" instead of "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);
_moveEndpointByUnitWord(count, endpoint, pRetVal, preventBoundary);
}
else if (unit <= TextUnit::TextUnit_Line)
{
@ -1080,6 +1085,26 @@ IFACEMETHODIMP UiaTextRangeBase::MoveEndpointByUnit(_In_ TextPatternRangeEndpoin
_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
{
if (unit == TextUnit::TextUnit_Character)
@ -1307,7 +1332,7 @@ const unsigned int UiaTextRangeBase::_getViewportHeight(const SMALL_RECT viewpor
// - <none>
// Return Value:
// - 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
// 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);
}
// 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:
// - adds the relevant coordinate points from the row to coords.
// - 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;
til::point target = GetEndpoint(endpoint);
const auto documentEnd{ _getDocumentEnd() };
while (std::abs(*pAmountMoved) < std::abs(moveCount) && success)
{
switch (moveDirection)
{
case MovementDirection::Forward:
success = buffer.MoveToNextGlyph(target, allowBottomExclusive);
success = buffer.MoveToNextGlyph(target, allowBottomExclusive, documentEnd);
if (success)
{
(*pAmountMoved)++;
}
break;
case MovementDirection::Backward:
success = buffer.MoveToPreviousGlyph(target);
success = buffer.MoveToPreviousGlyph(target, documentEnd);
if (success)
{
(*pAmountMoved)--;
@ -1441,10 +1481,9 @@ void UiaTextRangeBase::_moveEndpointByUnitWord(_In_ const int moveCount,
const bool allowBottomExclusive = !preventBufferEnd;
const MovementDirection moveDirection = (moveCount > 0) ? MovementDirection::Forward : MovementDirection::Backward;
const auto& buffer = _pData->GetTextBuffer();
const auto bufferSize = _getBufferSize();
const auto bufferSize = buffer.GetSize();
const auto bufferOrigin = bufferSize.Origin();
const auto bufferEnd = bufferSize.EndExclusive();
const auto lastCharPos = buffer.GetLastNonSpaceCharacter(bufferSize);
const auto documentEnd = _getDocumentEnd();
auto resultPos = GetEndpoint(endpoint);
auto nextPos = resultPos;
@ -1457,18 +1496,18 @@ void UiaTextRangeBase::_moveEndpointByUnitWord(_In_ const int moveCount,
{
case MovementDirection::Forward:
{
if (nextPos == bufferEnd)
if (bufferSize.CompareInBounds(nextPos, documentEnd, true) >= 0)
{
success = false;
}
else if (buffer.MoveToNextWord(nextPos, _wordDelimiters, lastCharPos))
else if (buffer.MoveToNextWord(nextPos, _wordDelimiters, documentEnd))
{
resultPos = nextPos;
(*pAmountMoved)++;
}
else if (allowBottomExclusive)
{
resultPos = bufferEnd;
resultPos = documentEnd;
(*pAmountMoved)++;
}
else
@ -1529,10 +1568,18 @@ void UiaTextRangeBase::_moveEndpointByUnitLine(_In_ const int moveCount,
const bool allowBottomExclusive = !preventBoundary;
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;
auto resultPos = GetEndpoint(endpoint);
while (std::abs(*pAmountMoved) < std::abs(moveCount) && success)
{
auto nextPos = resultPos;
@ -1540,22 +1587,29 @@ void UiaTextRangeBase::_moveEndpointByUnitLine(_In_ const int moveCount,
{
case MovementDirection::Forward:
{
// can't move past end
if (nextPos.Y >= bufferSize.BottomInclusive())
if (nextPos.Y >= documentEnd.Y)
{
if (preventBoundary || nextPos == bufferSize.EndExclusive())
{
success = false;
break;
}
// Corner Case: we're past the limit
// Clamp us to the limit
resultPos = documentEnd;
success = false;
}
nextPos.X = bufferSize.RightInclusive();
success = bufferSize.IncrementInBounds(nextPos, allowBottomExclusive);
if (success)
else if (preventBoundary && nextPos.Y == base::ClampSub(documentEnd.Y, 1))
{
resultPos = nextPos;
(*pAmountMoved)++;
// Corner Case: we're just before the limit
// 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;
}
@ -1621,15 +1675,21 @@ void UiaTextRangeBase::_moveEndpointByUnitDocument(_In_ const int moveCount,
}
const MovementDirection moveDirection = (moveCount > 0) ? MovementDirection::Forward : MovementDirection::Backward;
const auto bufferSize = _getBufferSize();
const auto bufferSize = _getOptimizedBufferSize();
const auto target = GetEndpoint(endpoint);
switch (moveDirection)
{
case MovementDirection::Forward:
{
const auto documentEnd = bufferSize.EndExclusive();
if (preventBoundary || target == documentEnd)
auto documentEnd{ bufferSize.EndExclusive() };
try
{
documentEnd = _getDocumentEnd();
}
CATCH_LOG();
if (preventBoundary || bufferSize.CompareInBounds(target, documentEnd, true) >= 0)
{
return;
}

View File

@ -149,7 +149,8 @@ namespace Microsoft::Console::Types
virtual const COORD _getScreenFontSize() const;
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;

View File

@ -21,7 +21,6 @@ $result = "// Copyright (c) Microsoft Corporation.
// These were generated by tools\TestTableWriter\GenerateTests.ps1
// 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.
$result += "
// Define a few helpful variables
@ -29,11 +28,16 @@ constexpr til::rectangle bufferSize{ 0, 0, 80, 300 };
constexpr short midX{ 40 };
constexpr short midY{ 150 };
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 midTop{ midX, 0 };
constexpr til::point midHistory{ midX, midPopulatedY };
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 midEmptySpace{ midX, midY + midPopulatedY };
constexpr til::point bufferEnd{ 79, 299 };
@ -55,17 +59,33 @@ foreach ($test in $tests)
$vars.Remove("origin") > $null;
$vars.Remove("midTop") > $null;
$vars.Remove("midHistory") > $null;
$vars.Remove("midDocEnd") > $null;
$vars.Remove("lastCharPos") > $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
foreach ($var in $vars)
{
# Extract the standard var from the name
$standardVar = $var.Contains("Left") ? $var.Split("Left") : $var.Substring(0, $var.length - 3);
# Figure out what heuristic to use
$segmentHeuristic = $var.Contains("segment");
$leftHeuristic = $var.Contains("Left");
$movementHeuristic = $var -match ".*\d+.*";
# i. Contains number --> requires movement
if ($var -match ".*\d+.*")
# i. Contains "segment" --> define point at the beginning of a text segment
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
# P --> plus/forwards
# M --> minus/backwards
@ -101,10 +121,11 @@ foreach ($var in $vars)
Default { Write-Host "Error: unknown variable movement type" -ForegroundColor Red }
}
}
# ii. Contains "Left" --> set X to left
elseif ($var.Contains("Left"))
# iii. Contains "Left" --> set X to 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";
}

View File

@ -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`
# 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.
```
+---------------------------+
|1XX XXX X2X XXX XXX|
|XXX XXX XXX XXX XXX|
|XXX XXX X3X XXX XXX|
|XXX XXX XXX XXX XXX|
|XXX XXX X4X XXX XX5|
|6 |
| |
| 7 |
| |
| 8|
+---------------------------+
+------------------------------+
|1XX XXX X2X XXX XXX |
|XXX XXX XXX XXX XXX |
|XXX XXX X3X XXX XXX |
|XXX XXX XXX XXX XXX |
|XXX XXX X4X XXX XX5 |
|6 |
| |
| 7 |
| |
| 8|
+------------------------------+
9
```
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.
- `<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.
- `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
- *degenerate*: the text range encompasses no text. Also, means the start and end endpoints are the same.

View File

@ -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,-1,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,5,origin,origin,1,endExclusive,endExclusive,FALSE
FALSE,1,TextUnit_Document,-5,origin,originP1C,0,origin,endExclusive,FALSE
FALSE,1,TextUnit_Document,-1,origin,originP1C,0,origin,endExclusive,FALSE
FALSE,1,TextUnit_Document,0,origin,originP1C,0,origin,endExclusive,FALSE
FALSE,1,TextUnit_Document,1,origin,originP1C,0,origin,endExclusive,FALSE
FALSE,1,TextUnit_Document,5,origin,originP1C,0,origin,endExclusive,FALSE
TRUE,1,TextUnit_Document,1,origin,origin,1,docEnd,docEnd,FALSE
TRUE,1,TextUnit_Document,5,origin,origin,1,docEnd,docEnd,FALSE
FALSE,1,TextUnit_Document,-5,origin,originP1C,0,origin,docEnd,FALSE
FALSE,1,TextUnit_Document,-1,origin,originP1C,0,origin,docEnd,FALSE
FALSE,1,TextUnit_Document,0,origin,originP1C,0,origin,docEnd,FALSE
FALSE,1,TextUnit_Document,1,origin,originP1C,0,origin,docEnd,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,-1,midTop,midTop,-1,origin,origin,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,5,midTop,midTop,1,endExclusive,endExclusive,FALSE
FALSE,2,TextUnit_Document,-5,midTop,midTopP1C,0,origin,endExclusive,FALSE
FALSE,2,TextUnit_Document,-1,midTop,midTopP1C,0,origin,endExclusive,FALSE
FALSE,2,TextUnit_Document,0,midTop,midTopP1C,0,origin,endExclusive,FALSE
FALSE,2,TextUnit_Document,1,midTop,midTopP1C,0,origin,endExclusive,FALSE
FALSE,2,TextUnit_Document,5,midTop,midTopP1C,0,origin,endExclusive,FALSE
TRUE,2,TextUnit_Document,1,midTop,midTop,1,docEnd,docEnd,FALSE
TRUE,2,TextUnit_Document,5,midTop,midTop,1,docEnd,docEnd,FALSE
FALSE,2,TextUnit_Document,-5,midTop,midTopP1C,0,origin,docEnd,FALSE
FALSE,2,TextUnit_Document,-1,midTop,midTopP1C,0,origin,docEnd,FALSE
FALSE,2,TextUnit_Document,0,midTop,midTopP1C,0,origin,docEnd,FALSE
FALSE,2,TextUnit_Document,1,midTop,midTopP1C,0,origin,docEnd,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,-1,midHistory,midHistory,-1,origin,origin,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,5,midHistory,midHistory,1,endExclusive,endExclusive,FALSE
FALSE,3,TextUnit_Document,-5,midHistory,midHistoryP1C,0,origin,endExclusive,FALSE
FALSE,3,TextUnit_Document,-1,midHistory,midHistoryP1C,0,origin,endExclusive,FALSE
FALSE,3,TextUnit_Document,0,midHistory,midHistoryP1C,0,origin,endExclusive,FALSE
FALSE,3,TextUnit_Document,1,midHistory,midHistoryP1C,0,origin,endExclusive,FALSE
FALSE,3,TextUnit_Document,5,midHistory,midHistoryP1C,0,origin,endExclusive,FALSE
TRUE,8,TextUnit_Character,-5,bufferEnd,bufferEnd,-5,bufferEndM5C,bufferEndM5C,FALSE
TRUE,8,TextUnit_Character,-1,bufferEnd,bufferEnd,-1,bufferEndM1C,bufferEndM1C,FALSE
TRUE,8,TextUnit_Character,0,bufferEnd,bufferEnd,0,bufferEnd,bufferEnd,FALSE
TRUE,8,TextUnit_Character,1,bufferEnd,bufferEnd,1,endExclusive,endExclusive,TRUE
TRUE,8,TextUnit_Character,5,bufferEnd,bufferEnd,1,endExclusive,endExclusive,TRUE
FALSE,8,TextUnit_Character,-5,bufferEnd,endExclusive,-5,bufferEndM5C,bufferEndM4C,FALSE
FALSE,8,TextUnit_Character,-1,bufferEnd,endExclusive,-1,bufferEndM1C,bufferEnd,FALSE
FALSE,8,TextUnit_Character,0,bufferEnd,endExclusive,0,bufferEnd,endExclusive,FALSE
FALSE,8,TextUnit_Character,1,bufferEnd,endExclusive,0,bufferEnd,endExclusive,FALSE
FALSE,8,TextUnit_Character,5,bufferEnd,endExclusive,0,bufferEnd,endExclusive,FALSE
TRUE,8,TextUnit_Line,-5,bufferEnd,bufferEnd,-5,bufferEndM4L,bufferEndM4L,FALSE
TRUE,8,TextUnit_Line,-1,bufferEnd,bufferEnd,-1,bufferEndLeft,bufferEndLeft,FALSE
TRUE,8,TextUnit_Line,0,bufferEnd,bufferEnd,0,bufferEnd,bufferEnd,FALSE
TRUE,8,TextUnit_Line,1,bufferEnd,bufferEnd,1,endExclusive,endExclusive,TRUE
TRUE,8,TextUnit_Line,5,bufferEnd,bufferEnd,1,endExclusive,endExclusive,TRUE
FALSE,8,TextUnit_Line,-5,bufferEnd,endExclusive,-5,bufferEndM5L,bufferEndM4L,TRUE
FALSE,8,TextUnit_Line,-1,bufferEnd,endExclusive,-1,bufferEndM1L,bufferEndLeft,TRUE
FALSE,8,TextUnit_Line,0,bufferEnd,endExclusive,0,bufferEndLeft,endExclusive,TRUE
FALSE,8,TextUnit_Line,1,bufferEnd,endExclusive,0,bufferEndLeft,endExclusive,TRUE
FALSE,8,TextUnit_Line,5,bufferEnd,endExclusive,0,bufferEndLeft,endExclusive,TRUE
TRUE,3,TextUnit_Document,1,midHistory,midHistory,1,docEnd,docEnd,FALSE
TRUE,3,TextUnit_Document,5,midHistory,midHistory,1,docEnd,docEnd,FALSE
FALSE,3,TextUnit_Document,-5,midHistory,midHistoryP1C,0,origin,docEnd,FALSE
FALSE,3,TextUnit_Document,-1,midHistory,midHistoryP1C,0,origin,docEnd,FALSE
FALSE,3,TextUnit_Document,0,midHistory,midHistoryP1C,0,origin,docEnd,FALSE
FALSE,3,TextUnit_Document,1,midHistory,midHistoryP1C,0,origin,docEnd,FALSE
FALSE,3,TextUnit_Document,5,midHistory,midHistoryP1C,0,origin,docEnd,FALSE
TRUE,8,TextUnit_Character,-5,bufferEnd,bufferEnd,-5,docEndM5C,docEndM5C,FALSE
TRUE,8,TextUnit_Character,-1,bufferEnd,bufferEnd,-1,docEndM1C,docEndM1C,FALSE
TRUE,8,TextUnit_Character,0,bufferEnd,bufferEnd,0,docEnd,docEnd,FALSE
TRUE,8,TextUnit_Character,1,bufferEnd,bufferEnd,0,docEnd,docEnd,FALSE
TRUE,8,TextUnit_Character,5,bufferEnd,bufferEnd,0,docEnd,docEnd,FALSE
FALSE,8,TextUnit_Character,-5,bufferEnd,endExclusive,-5,docEndM5C,docEndM5C,FALSE
FALSE,8,TextUnit_Character,-1,bufferEnd,endExclusive,-1,docEndM1C,docEndM1C,FALSE
FALSE,8,TextUnit_Character,0,bufferEnd,endExclusive,0,docEnd,docEnd,FALSE
FALSE,8,TextUnit_Character,1,bufferEnd,endExclusive,0,docEnd,docEnd,FALSE
FALSE,8,TextUnit_Character,5,bufferEnd,endExclusive,0,docEnd,docEnd,FALSE
TRUE,8,TextUnit_Line,-5,bufferEnd,bufferEnd,-5,docEndM5L,docEndM5L,FALSE
TRUE,8,TextUnit_Line,-1,bufferEnd,bufferEnd,-1,docEndM1L,docEndM1L,FALSE
TRUE,8,TextUnit_Line,0,bufferEnd,bufferEnd,0,docEnd,docEnd,FALSE
TRUE,8,TextUnit_Line,1,bufferEnd,bufferEnd,0,docEnd,docEnd,FALSE
TRUE,8,TextUnit_Line,5,bufferEnd,bufferEnd,0,docEnd,docEnd,FALSE
FALSE,8,TextUnit_Line,-5,bufferEnd,endExclusive,-5,docEndM5L,docEndM5L,FALSE
FALSE,8,TextUnit_Line,-1,bufferEnd,endExclusive,-1,docEndM1L,docEndM1L,FALSE
FALSE,8,TextUnit_Line,0,bufferEnd,endExclusive,0,docEnd,docEnd,FALSE
FALSE,8,TextUnit_Line,1,bufferEnd,endExclusive,0,docEnd,docEnd,FALSE
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,-1,bufferEnd,bufferEnd,-1,origin,origin,FALSE
TRUE,8,TextUnit_Document,0,bufferEnd,bufferEnd,0,bufferEnd,bufferEnd,FALSE
TRUE,8,TextUnit_Document,1,bufferEnd,bufferEnd,1,endExclusive,endExclusive,TRUE
TRUE,8,TextUnit_Document,5,bufferEnd,bufferEnd,1,endExclusive,endExclusive,TRUE
FALSE,8,TextUnit_Document,-5,bufferEnd,endExclusive,0,origin,endExclusive,TRUE
FALSE,8,TextUnit_Document,-1,bufferEnd,endExclusive,0,origin,endExclusive,TRUE
FALSE,8,TextUnit_Document,0,bufferEnd,endExclusive,0,origin,endExclusive,TRUE
FALSE,8,TextUnit_Document,1,bufferEnd,endExclusive,0,origin,endExclusive,TRUE
FALSE,8,TextUnit_Document,5,bufferEnd,endExclusive,0,origin,endExclusive,TRUE
TRUE,9,TextUnit_Character,-5,endExclusive,endExclusive,-5,bufferEndM4C,bufferEndM4C,FALSE
TRUE,9,TextUnit_Character,-1,endExclusive,endExclusive,-1,bufferEnd,bufferEnd,FALSE
TRUE,9,TextUnit_Character,0,endExclusive,endExclusive,0,endExclusive,endExclusive,FALSE
TRUE,9,TextUnit_Character,1,endExclusive,endExclusive,0,endExclusive,endExclusive,FALSE
TRUE,9,TextUnit_Character,5,endExclusive,endExclusive,0,endExclusive,endExclusive,FALSE
TRUE,9,TextUnit_Line,-5,endExclusive,endExclusive,-5,bufferEndM4L,bufferEndM4L,FALSE
TRUE,9,TextUnit_Line,-1,endExclusive,endExclusive,-1,bufferEndLeft,bufferEndLeft,FALSE
TRUE,9,TextUnit_Line,0,endExclusive,endExclusive,0,endExclusive,endExclusive,FALSE
TRUE,9,TextUnit_Line,1,endExclusive,endExclusive,0,endExclusive,endExclusive,FALSE
TRUE,9,TextUnit_Line,5,endExclusive,endExclusive,0,endExclusive,endExclusive,FALSE
TRUE,8,TextUnit_Document,0,bufferEnd,bufferEnd,0,docEnd,docEnd,FALSE
TRUE,8,TextUnit_Document,1,bufferEnd,bufferEnd,0,docEnd,docEnd,FALSE
TRUE,8,TextUnit_Document,5,bufferEnd,bufferEnd,0,docEnd,docEnd,FALSE
FALSE,8,TextUnit_Document,-5,bufferEnd,endExclusive,-1,origin,origin,FALSE
FALSE,8,TextUnit_Document,-1,bufferEnd,endExclusive,-1,origin,origin,FALSE
FALSE,8,TextUnit_Document,0,bufferEnd,endExclusive,0,docEnd,docEnd,FALSE
FALSE,8,TextUnit_Document,1,bufferEnd,endExclusive,0,docEnd,docEnd,FALSE
FALSE,8,TextUnit_Document,5,bufferEnd,endExclusive,0,docEnd,docEnd,FALSE
TRUE,9,TextUnit_Character,-5,endExclusive,endExclusive,-5,docEndM5C,docEndM5C,FALSE
TRUE,9,TextUnit_Character,-1,endExclusive,endExclusive,-1,docEndM1C,docEndM1C,FALSE
TRUE,9,TextUnit_Character,0,endExclusive,endExclusive,0,docEnd,docEnd,FALSE
TRUE,9,TextUnit_Character,1,endExclusive,endExclusive,0,docEnd,docEnd,FALSE
TRUE,9,TextUnit_Character,5,endExclusive,endExclusive,0,docEnd,docEnd,FALSE
TRUE,9,TextUnit_Line,-5,endExclusive,endExclusive,-5,docEndM5L,docEndM5L,FALSE
TRUE,9,TextUnit_Line,-1,endExclusive,endExclusive,-1,docEndM1L,docEndM1L,FALSE
TRUE,9,TextUnit_Line,0,endExclusive,endExclusive,0,docEnd,docEnd,FALSE
TRUE,9,TextUnit_Line,1,endExclusive,endExclusive,0,docEnd,docEnd,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,-1,endExclusive,endExclusive,-1,origin,origin,FALSE
TRUE,9,TextUnit_Document,0,endExclusive,endExclusive,0,endExclusive,endExclusive,FALSE
TRUE,9,TextUnit_Document,1,endExclusive,endExclusive,0,endExclusive,endExclusive,FALSE
TRUE,9,TextUnit_Document,5,endExclusive,endExclusive,0,endExclusive,endExclusive,FALSE
TRUE,4,TextUnit_Character,-5,midDocEnd,midDocEnd,-5,midDocEndM5C,midDocEndM5C,TRUE
TRUE,4,TextUnit_Character,-1,midDocEnd,midDocEnd,-1,midDocEndM1C,midDocEndM1C,TRUE
TRUE,4,TextUnit_Character,0,midDocEnd,midDocEnd,0,midDocEnd,midDocEnd,TRUE
TRUE,4,TextUnit_Character,1,midDocEnd,midDocEnd,1,midDocEndP1C,midDocEndP1C,TRUE
TRUE,4,TextUnit_Character,5,midDocEnd,midDocEnd,5,midDocEndP5C,midDocEndP5C,TRUE
FALSE,4,TextUnit_Character,-5,midDocEnd,midDocEndP1C,-5,midDocEndM5C,midDocEndM4C,TRUE
FALSE,4,TextUnit_Character,-1,midDocEnd,midDocEndP1C,-1,midDocEndM1C,midDocEnd,TRUE
FALSE,4,TextUnit_Character,0,midDocEnd,midDocEndP1C,0,midDocEnd,midDocEndP1C,TRUE
FALSE,4,TextUnit_Character,1,midDocEnd,midDocEndP1C,1,midDocEndP1C,midDocEndP2C,TRUE
FALSE,4,TextUnit_Character,5,midDocEnd,midDocEndP1C,5,midDocEndP5C,midDocEndP6C,TRUE
TRUE,4,TextUnit_Line,-5,midDocEnd,midDocEnd,-5,midDocEndM4L,midDocEndM4L,TRUE
TRUE,4,TextUnit_Line,-1,midDocEnd,midDocEnd,-1,midDocEndLeft,midDocEndLeft,TRUE
TRUE,4,TextUnit_Line,0,midDocEnd,midDocEnd,0,midDocEnd,midDocEnd,TRUE
TRUE,4,TextUnit_Line,1,midDocEnd,midDocEnd,1,docEnd,docEnd,TRUE
TRUE,4,TextUnit_Line,5,midDocEnd,midDocEnd,1,docEnd,docEnd,TRUE
FALSE,4,TextUnit_Line,-5,midDocEnd,midDocEndP1C,-5,midDocEndM5L,midDocEndM4L,TRUE
FALSE,4,TextUnit_Line,-1,midDocEnd,midDocEndP1C,-1,midDocEndM1L,midDocEndLeft,TRUE
FALSE,4,TextUnit_Line,0,midDocEnd,midDocEndP1C,0,midDocEndLeft,docEnd,TRUE
FALSE,4,TextUnit_Line,1,midDocEnd,midDocEndP1C,0,midDocEndLeft,docEnd,TRUE
FALSE,4,TextUnit_Line,5,midDocEnd,midDocEndP1C,0,midDocEndLeft,docEnd,TRUE
TRUE,4,TextUnit_Document,-5,midDocEnd,midDocEnd,-1,origin,origin,TRUE
TRUE,4,TextUnit_Document,-1,midDocEnd,midDocEnd,-1,origin,origin,TRUE
TRUE,4,TextUnit_Document,0,midDocEnd,midDocEnd,0,midDocEnd,midDocEnd,TRUE
TRUE,4,TextUnit_Document,1,midDocEnd,midDocEnd,1,docEnd,docEnd,TRUE
TRUE,4,TextUnit_Document,5,midDocEnd,midDocEnd,1,docEnd,docEnd,TRUE
FALSE,4,TextUnit_Document,-5,midDocEnd,midDocEndP1C,0,origin,docEnd,TRUE
FALSE,4,TextUnit_Document,-1,midDocEnd,midDocEndP1C,0,origin,docEnd,TRUE
FALSE,4,TextUnit_Document,0,midDocEnd,midDocEndP1C,0,origin,docEnd,TRUE
FALSE,4,TextUnit_Document,1,midDocEnd,midDocEndP1C,0,origin,docEnd,TRUE
FALSE,4,TextUnit_Document,5,midDocEnd,midDocEndP1C,0,origin,docEnd,TRUE
TRUE,5,TextUnit_Character,-5,lastCharPos,lastCharPos,-5,lastCharPosM5C,lastCharPosM5C,TRUE
TRUE,5,TextUnit_Character,-1,lastCharPos,lastCharPos,-1,lastCharPosM1C,lastCharPosM1C,TRUE
TRUE,5,TextUnit_Character,0,lastCharPos,lastCharPos,0,lastCharPos,lastCharPos,TRUE
TRUE,5,TextUnit_Character,1,lastCharPos,lastCharPos,1,docEnd,docEnd,TRUE
TRUE,5,TextUnit_Character,5,lastCharPos,lastCharPos,1,docEnd,docEnd,TRUE
FALSE,5,TextUnit_Character,-5,lastCharPos,lastCharPosP1C,-5,lastCharPosM5C,lastCharPosM4C,TRUE
FALSE,5,TextUnit_Character,-1,lastCharPos,lastCharPosP1C,-1,lastCharPosM1C,lastCharPos,TRUE
FALSE,5,TextUnit_Character,0,lastCharPos,lastCharPosP1C,0,lastCharPos,docEnd,TRUE
FALSE,5,TextUnit_Character,1,lastCharPos,lastCharPosP1C,0,lastCharPos,docEnd,TRUE
FALSE,5,TextUnit_Character,5,lastCharPos,lastCharPosP1C,0,lastCharPos,docEnd,TRUE
TRUE,5,TextUnit_Line,-5,lastCharPos,lastCharPos,-5,lastCharPosM4L,lastCharPosM4L,TRUE
TRUE,5,TextUnit_Line,-1,lastCharPos,lastCharPos,-1,lastCharPosLeft,lastCharPosLeft,TRUE
TRUE,5,TextUnit_Line,0,lastCharPos,lastCharPos,0,lastCharPos,lastCharPos,TRUE
TRUE,5,TextUnit_Line,1,lastCharPos,lastCharPos,1,docEnd,docEnd,TRUE
TRUE,5,TextUnit_Line,5,lastCharPos,lastCharPos,1,docEnd,docEnd,TRUE
FALSE,5,TextUnit_Line,-5,lastCharPos,lastCharPosP1C,-5,lastCharPosM5L,lastCharPosM4L,TRUE
FALSE,5,TextUnit_Line,-1,lastCharPos,lastCharPosP1C,-1,lastCharPosM1L,lastCharPosLeft,TRUE
FALSE,5,TextUnit_Line,0,lastCharPos,lastCharPosP1C,0,lastCharPosLeft,docEnd,TRUE
FALSE,5,TextUnit_Line,1,lastCharPos,lastCharPosP1C,0,lastCharPosLeft,docEnd,TRUE
FALSE,5,TextUnit_Line,5,lastCharPos,lastCharPosP1C,0,lastCharPosLeft,docEnd,TRUE
TRUE,5,TextUnit_Document,-5,lastCharPos,lastCharPos,-1,origin,origin,TRUE
TRUE,5,TextUnit_Document,-1,lastCharPos,lastCharPos,-1,origin,origin,TRUE
TRUE,5,TextUnit_Document,0,lastCharPos,lastCharPos,0,lastCharPos,lastCharPos,TRUE
TRUE,5,TextUnit_Document,1,lastCharPos,lastCharPos,1,docEnd,docEnd,TRUE
TRUE,5,TextUnit_Document,5,lastCharPos,lastCharPos,1,docEnd,docEnd,TRUE
FALSE,5,TextUnit_Document,-5,lastCharPos,lastCharPosP1C,0,origin,docEnd,TRUE
FALSE,5,TextUnit_Document,-1,lastCharPos,lastCharPosP1C,0,origin,docEnd,TRUE
FALSE,5,TextUnit_Document,0,lastCharPos,lastCharPosP1C,0,origin,docEnd,TRUE
FALSE,5,TextUnit_Document,1,lastCharPos,lastCharPosP1C,0,origin,docEnd,TRUE
FALSE,5,TextUnit_Document,5,lastCharPos,lastCharPosP1C,0,origin,docEnd,TRUE
TRUE,6,TextUnit_Character,-5,docEnd,docEnd,-5,docEndM5C,docEndM5C,TRUE
TRUE,6,TextUnit_Character,-1,docEnd,docEnd,-1,docEndM1C,docEndM1C,TRUE
TRUE,6,TextUnit_Character,0,docEnd,docEnd,0,docEnd,docEnd,TRUE
TRUE,6,TextUnit_Character,1,docEnd,docEnd,0,docEnd,docEnd,TRUE
TRUE,6,TextUnit_Character,5,docEnd,docEnd,0,docEnd,docEnd,TRUE
FALSE,6,TextUnit_Character,-5,docEnd,docEndP1C,-5,docEndM5C,docEndM5C,TRUE
FALSE,6,TextUnit_Character,-1,docEnd,docEndP1C,-1,docEndM1C,docEndM1C,TRUE
FALSE,6,TextUnit_Character,0,docEnd,docEndP1C,0,docEnd,docEnd,TRUE
FALSE,6,TextUnit_Character,1,docEnd,docEndP1C,0,docEnd,docEnd,TRUE
FALSE,6,TextUnit_Character,5,docEnd,docEndP1C,0,docEnd,docEnd,TRUE
TRUE,6,TextUnit_Line,-5,docEnd,docEnd,-5,docEndM4L,docEndM4L,TRUE
TRUE,6,TextUnit_Line,-1,docEnd,docEnd,-1,docEndLeft,docEndLeft,TRUE
TRUE,6,TextUnit_Line,0,docEnd,docEnd,0,docEnd,docEnd,TRUE
TRUE,6,TextUnit_Line,1,docEnd,docEnd,0,docEnd,docEnd,TRUE
TRUE,6,TextUnit_Line,5,docEnd,docEnd,0,docEnd,docEnd,TRUE
FALSE,6,TextUnit_Line,-5,docEnd,docEndP1C,-5,docEndM4L,docEndM4L,TRUE
FALSE,6,TextUnit_Line,-1,docEnd,docEndP1C,-1,docEndLeft,docEndLeft,TRUE
FALSE,6,TextUnit_Line,0,docEnd,docEndP1C,0,docEnd,docEnd,TRUE
FALSE,6,TextUnit_Line,1,docEnd,docEndP1C,0,docEnd,docEnd,TRUE
FALSE,6,TextUnit_Line,5,docEnd,docEndP1C,0,docEnd,docEnd,TRUE
TRUE,6,TextUnit_Document,-5,docEnd,docEnd,-1,origin,origin,TRUE
TRUE,6,TextUnit_Document,-1,docEnd,docEnd,-1,origin,origin,TRUE
TRUE,6,TextUnit_Document,0,docEnd,docEnd,0,docEnd,docEnd,TRUE
TRUE,6,TextUnit_Document,1,docEnd,docEnd,0,docEnd,docEnd,TRUE
TRUE,6,TextUnit_Document,5,docEnd,docEnd,0,docEnd,docEnd,TRUE
FALSE,6,TextUnit_Document,-5,docEnd,docEndP1C,-1,origin,origin,TRUE
FALSE,6,TextUnit_Document,-1,docEnd,docEndP1C,-1,origin,origin,TRUE
FALSE,6,TextUnit_Document,0,docEnd,docEndP1C,0,docEnd,docEnd,TRUE
FALSE,6,TextUnit_Document,1,docEnd,docEndP1C,0,docEnd,docEnd,TRUE
FALSE,6,TextUnit_Document,5,docEnd,docEndP1C,0,docEnd,docEnd,TRUE
TRUE,7,TextUnit_Character,-5,midEmptySpace,midEmptySpace,-5,docEndM5C,docEndM5C,TRUE
TRUE,7,TextUnit_Character,-1,midEmptySpace,midEmptySpace,-1,docEndM1C,docEndM1C,TRUE
TRUE,7,TextUnit_Character,0,midEmptySpace,midEmptySpace,0,docEnd,docEnd,TRUE
TRUE,7,TextUnit_Character,1,midEmptySpace,midEmptySpace,0,docEnd,docEnd,TRUE
TRUE,7,TextUnit_Character,5,midEmptySpace,midEmptySpace,0,docEnd,docEnd,TRUE
FALSE,7,TextUnit_Character,-5,midEmptySpace,midEmptySpaceP1C,-5,docEndM5C,docEndM5C,TRUE
FALSE,7,TextUnit_Character,-1,midEmptySpace,midEmptySpaceP1C,-1,docEndM1C,docEndM1C,TRUE
FALSE,7,TextUnit_Character,0,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,TRUE
FALSE,7,TextUnit_Character,1,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,TRUE
FALSE,7,TextUnit_Character,5,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,TRUE
TRUE,7,TextUnit_Line,-5,midEmptySpace,midEmptySpace,-5,docEndM4L,docEndM4L,TRUE
TRUE,7,TextUnit_Line,-1,midEmptySpace,midEmptySpace,-1,docEndLeft,docEndLeft,TRUE
TRUE,7,TextUnit_Line,0,midEmptySpace,midEmptySpace,0,docEnd,docEnd,TRUE
TRUE,7,TextUnit_Line,1,midEmptySpace,midEmptySpace,0,docEnd,docEnd,TRUE
TRUE,7,TextUnit_Line,5,midEmptySpace,midEmptySpace,0,docEnd,docEnd,TRUE
FALSE,7,TextUnit_Line,-5,midEmptySpace,midEmptySpaceP1C,-5,docEndM4L,docEndM4L,TRUE
FALSE,7,TextUnit_Line,-1,midEmptySpace,midEmptySpaceP1C,-1,docEndLeft,docEndLeft,TRUE
FALSE,7,TextUnit_Line,0,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,TRUE
FALSE,7,TextUnit_Line,1,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,TRUE
FALSE,7,TextUnit_Line,5,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,TRUE
TRUE,7,TextUnit_Document,-5,midEmptySpace,midEmptySpace,-1,origin,origin,TRUE
TRUE,7,TextUnit_Document,-1,midEmptySpace,midEmptySpace,-1,origin,origin,TRUE
TRUE,7,TextUnit_Document,0,midEmptySpace,midEmptySpace,0,docEnd,docEnd,TRUE
TRUE,7,TextUnit_Document,1,midEmptySpace,midEmptySpace,0,docEnd,docEnd,TRUE
TRUE,7,TextUnit_Document,5,midEmptySpace,midEmptySpace,0,docEnd,docEnd,TRUE
FALSE,7,TextUnit_Document,-5,midEmptySpace,midEmptySpaceP1C,-1,origin,origin,TRUE
FALSE,7,TextUnit_Document,-1,midEmptySpace,midEmptySpaceP1C,-1,origin,origin,TRUE
FALSE,7,TextUnit_Document,0,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,TRUE
FALSE,7,TextUnit_Document,1,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,TRUE
FALSE,7,TextUnit_Document,5,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,TRUE
TRUE,9,TextUnit_Document,0,endExclusive,endExclusive,0,docEnd,docEnd,FALSE
TRUE,9,TextUnit_Document,1,endExclusive,endExclusive,0,docEnd,docEnd,FALSE
TRUE,9,TextUnit_Document,5,endExclusive,endExclusive,0,docEnd,docEnd,FALSE
TRUE,4,TextUnit_Character,-5,midDocEnd,midDocEnd,-5,midDocEndM5C,midDocEndM5C,FALSE
TRUE,4,TextUnit_Character,-1,midDocEnd,midDocEnd,-1,midDocEndM1C,midDocEndM1C,FALSE
TRUE,4,TextUnit_Character,0,midDocEnd,midDocEnd,0,midDocEnd,midDocEnd,FALSE
TRUE,4,TextUnit_Character,1,midDocEnd,midDocEnd,1,midDocEndP1C,midDocEndP1C,FALSE
TRUE,4,TextUnit_Character,5,midDocEnd,midDocEnd,5,midDocEndP5C,midDocEndP5C,FALSE
FALSE,4,TextUnit_Character,-5,midDocEnd,midDocEndP1C,-5,midDocEndM5C,midDocEndM4C,FALSE
FALSE,4,TextUnit_Character,-1,midDocEnd,midDocEndP1C,-1,midDocEndM1C,midDocEnd,FALSE
FALSE,4,TextUnit_Character,0,midDocEnd,midDocEndP1C,0,midDocEnd,midDocEndP1C,FALSE
FALSE,4,TextUnit_Character,1,midDocEnd,midDocEndP1C,1,midDocEndP1C,midDocEndP2C,FALSE
FALSE,4,TextUnit_Character,5,midDocEnd,midDocEndP1C,5,midDocEndP5C,midDocEndP6C,FALSE
TRUE,4,TextUnit_Line,-5,midDocEnd,midDocEnd,-5,midDocEndM4L,midDocEndM4L,FALSE
TRUE,4,TextUnit_Line,-1,midDocEnd,midDocEnd,-1,midDocEndLeft,midDocEndLeft,FALSE
TRUE,4,TextUnit_Line,0,midDocEnd,midDocEnd,0,midDocEnd,midDocEnd,FALSE
TRUE,4,TextUnit_Line,1,midDocEnd,midDocEnd,1,docEnd,docEnd,FALSE
TRUE,4,TextUnit_Line,5,midDocEnd,midDocEnd,1,docEnd,docEnd,FALSE
FALSE,4,TextUnit_Line,-5,midDocEnd,midDocEndP1C,-5,midDocEndM5L,midDocEndM4L,FALSE
FALSE,4,TextUnit_Line,-1,midDocEnd,midDocEndP1C,-1,midDocEndM1L,midDocEndLeft,FALSE
FALSE,4,TextUnit_Line,0,midDocEnd,midDocEndP1C,0,midDocEndLeft,docEnd,FALSE
FALSE,4,TextUnit_Line,1,midDocEnd,midDocEndP1C,0,midDocEndLeft,docEnd,FALSE
FALSE,4,TextUnit_Line,5,midDocEnd,midDocEndP1C,0,midDocEndLeft,docEnd,FALSE
TRUE,4,TextUnit_Document,-5,midDocEnd,midDocEnd,-1,origin,origin,FALSE
TRUE,4,TextUnit_Document,-1,midDocEnd,midDocEnd,-1,origin,origin,FALSE
TRUE,4,TextUnit_Document,0,midDocEnd,midDocEnd,0,midDocEnd,midDocEnd,FALSE
TRUE,4,TextUnit_Document,1,midDocEnd,midDocEnd,1,docEnd,docEnd,FALSE
TRUE,4,TextUnit_Document,5,midDocEnd,midDocEnd,1,docEnd,docEnd,FALSE
FALSE,4,TextUnit_Document,-5,midDocEnd,midDocEndP1C,0,origin,docEnd,FALSE
FALSE,4,TextUnit_Document,-1,midDocEnd,midDocEndP1C,0,origin,docEnd,FALSE
FALSE,4,TextUnit_Document,0,midDocEnd,midDocEndP1C,0,origin,docEnd,FALSE
FALSE,4,TextUnit_Document,1,midDocEnd,midDocEndP1C,0,origin,docEnd,FALSE
FALSE,4,TextUnit_Document,5,midDocEnd,midDocEndP1C,0,origin,docEnd,FALSE
TRUE,5,TextUnit_Character,-5,lastCharPos,lastCharPos,-5,lastCharPosM5C,lastCharPosM5C,FALSE
TRUE,5,TextUnit_Character,-1,lastCharPos,lastCharPos,-1,lastCharPosM1C,lastCharPosM1C,FALSE
TRUE,5,TextUnit_Character,0,lastCharPos,lastCharPos,0,lastCharPos,lastCharPos,FALSE
TRUE,5,TextUnit_Character,1,lastCharPos,lastCharPos,1,lastCharPosP1C,lastCharPosP1C,FALSE
TRUE,5,TextUnit_Character,5,lastCharPos,lastCharPos,5,lastCharPosP5C,lastCharPosP5C,FALSE
FALSE,5,TextUnit_Character,-5,lastCharPos,lastCharPosP1C,-5,lastCharPosM5C,lastCharPosM4C,FALSE
FALSE,5,TextUnit_Character,-1,lastCharPos,lastCharPosP1C,-1,lastCharPosM1C,lastCharPos,FALSE
FALSE,5,TextUnit_Character,0,lastCharPos,lastCharPosP1C,0,lastCharPos,lastCharPosP1C,FALSE
FALSE,5,TextUnit_Character,1,lastCharPos,lastCharPosP1C,1,lastCharPosP1C,lastCharPosP2C,FALSE
FALSE,5,TextUnit_Character,5,lastCharPos,lastCharPosP1C,5,lastCharPosP5C,lastCharPosP6C,FALSE
TRUE,5,TextUnit_Line,-5,lastCharPos,lastCharPos,-5,lastCharPosM4L,lastCharPosM4L,FALSE
TRUE,5,TextUnit_Line,-1,lastCharPos,lastCharPos,-1,lastCharPosLeft,lastCharPosLeft,FALSE
TRUE,5,TextUnit_Line,0,lastCharPos,lastCharPos,0,lastCharPos,lastCharPos,FALSE
TRUE,5,TextUnit_Line,1,lastCharPos,lastCharPos,1,docEnd,docEnd,FALSE
TRUE,5,TextUnit_Line,5,lastCharPos,lastCharPos,1,docEnd,docEnd,FALSE
FALSE,5,TextUnit_Line,-5,lastCharPos,lastCharPosP1C,-5,lastCharPosM5L,lastCharPosM4L,FALSE
FALSE,5,TextUnit_Line,-1,lastCharPos,lastCharPosP1C,-1,lastCharPosM1L,lastCharPosLeft,FALSE
FALSE,5,TextUnit_Line,0,lastCharPos,lastCharPosP1C,0,lastCharPosLeft,docEnd,FALSE
FALSE,5,TextUnit_Line,1,lastCharPos,lastCharPosP1C,0,lastCharPosLeft,docEnd,FALSE
FALSE,5,TextUnit_Line,5,lastCharPos,lastCharPosP1C,0,lastCharPosLeft,docEnd,FALSE
TRUE,5,TextUnit_Document,-5,lastCharPos,lastCharPos,-1,origin,origin,FALSE
TRUE,5,TextUnit_Document,-1,lastCharPos,lastCharPos,-1,origin,origin,FALSE
TRUE,5,TextUnit_Document,0,lastCharPos,lastCharPos,0,lastCharPos,lastCharPos,FALSE
TRUE,5,TextUnit_Document,1,lastCharPos,lastCharPos,1,docEnd,docEnd,FALSE
TRUE,5,TextUnit_Document,5,lastCharPos,lastCharPos,1,docEnd,docEnd,FALSE
FALSE,5,TextUnit_Document,-5,lastCharPos,lastCharPosP1C,0,origin,docEnd,FALSE
FALSE,5,TextUnit_Document,-1,lastCharPos,lastCharPosP1C,0,origin,docEnd,FALSE
FALSE,5,TextUnit_Document,0,lastCharPos,lastCharPosP1C,0,origin,docEnd,FALSE
FALSE,5,TextUnit_Document,1,lastCharPos,lastCharPosP1C,0,origin,docEnd,FALSE
FALSE,5,TextUnit_Document,5,lastCharPos,lastCharPosP1C,0,origin,docEnd,FALSE
TRUE,6,TextUnit_Character,-5,docEnd,docEnd,-5,docEndM5C,docEndM5C,FALSE
TRUE,6,TextUnit_Character,-1,docEnd,docEnd,-1,docEndM1C,docEndM1C,FALSE
TRUE,6,TextUnit_Character,0,docEnd,docEnd,0,docEnd,docEnd,FALSE
TRUE,6,TextUnit_Character,1,docEnd,docEnd,0,docEnd,docEnd,FALSE
TRUE,6,TextUnit_Character,5,docEnd,docEnd,0,docEnd,docEnd,FALSE
FALSE,6,TextUnit_Character,-5,docEnd,docEndP1C,-5,docEndM5C,docEndM5C,FALSE
FALSE,6,TextUnit_Character,-1,docEnd,docEndP1C,-1,docEndM1C,docEndM1C,FALSE
FALSE,6,TextUnit_Character,0,docEnd,docEndP1C,0,docEnd,docEnd,FALSE
FALSE,6,TextUnit_Character,1,docEnd,docEndP1C,0,docEnd,docEnd,FALSE
FALSE,6,TextUnit_Character,5,docEnd,docEndP1C,0,docEnd,docEnd,FALSE
TRUE,6,TextUnit_Line,-5,docEnd,docEnd,-5,docEndM5L,docEndM5L,FALSE
TRUE,6,TextUnit_Line,-1,docEnd,docEnd,-1,docEndM1L,docEndM1L,FALSE
TRUE,6,TextUnit_Line,0,docEnd,docEnd,0,docEnd,docEnd,FALSE
TRUE,6,TextUnit_Line,1,docEnd,docEnd,0,docEnd,docEnd,FALSE
TRUE,6,TextUnit_Line,5,docEnd,docEnd,0,docEnd,docEnd,FALSE
FALSE,6,TextUnit_Line,-5,docEnd,docEndP1C,-5,docEndM5L,docEndM5L,FALSE
FALSE,6,TextUnit_Line,-1,docEnd,docEndP1C,-1,docEndM1L,docEndM1L,FALSE
FALSE,6,TextUnit_Line,0,docEnd,docEndP1C,0,docEnd,docEnd,FALSE
FALSE,6,TextUnit_Line,1,docEnd,docEndP1C,0,docEnd,docEnd,FALSE
FALSE,6,TextUnit_Line,5,docEnd,docEndP1C,0,docEnd,docEnd,FALSE
TRUE,6,TextUnit_Document,-5,docEnd,docEnd,-1,origin,origin,FALSE
TRUE,6,TextUnit_Document,-1,docEnd,docEnd,-1,origin,origin,FALSE
TRUE,6,TextUnit_Document,0,docEnd,docEnd,0,docEnd,docEnd,FALSE
TRUE,6,TextUnit_Document,1,docEnd,docEnd,0,docEnd,docEnd,FALSE
TRUE,6,TextUnit_Document,5,docEnd,docEnd,0,docEnd,docEnd,FALSE
FALSE,6,TextUnit_Document,-5,docEnd,docEndP1C,-1,origin,origin,FALSE
FALSE,6,TextUnit_Document,-1,docEnd,docEndP1C,-1,origin,origin,FALSE
FALSE,6,TextUnit_Document,0,docEnd,docEndP1C,0,docEnd,docEnd,FALSE
FALSE,6,TextUnit_Document,1,docEnd,docEndP1C,0,docEnd,docEnd,FALSE
FALSE,6,TextUnit_Document,5,docEnd,docEndP1C,0,docEnd,docEnd,FALSE
TRUE,7,TextUnit_Character,-5,midEmptySpace,midEmptySpace,-5,docEndM5C,docEndM5C,FALSE
TRUE,7,TextUnit_Character,-1,midEmptySpace,midEmptySpace,-1,docEndM1C,docEndM1C,FALSE
TRUE,7,TextUnit_Character,0,midEmptySpace,midEmptySpace,0,docEnd,docEnd,FALSE
TRUE,7,TextUnit_Character,1,midEmptySpace,midEmptySpace,0,docEnd,docEnd,FALSE
TRUE,7,TextUnit_Character,5,midEmptySpace,midEmptySpace,0,docEnd,docEnd,FALSE
FALSE,7,TextUnit_Character,-5,midEmptySpace,midEmptySpaceP1C,-5,docEndM5C,docEndM5C,FALSE
FALSE,7,TextUnit_Character,-1,midEmptySpace,midEmptySpaceP1C,-1,docEndM1C,docEndM1C,FALSE
FALSE,7,TextUnit_Character,0,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,FALSE
FALSE,7,TextUnit_Character,1,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,FALSE
FALSE,7,TextUnit_Character,5,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,FALSE
TRUE,7,TextUnit_Line,-5,midEmptySpace,midEmptySpace,-5,docEndM5L,docEndM5L,FALSE
TRUE,7,TextUnit_Line,-1,midEmptySpace,midEmptySpace,-1,docEndM1L,docEndM1L,FALSE
TRUE,7,TextUnit_Line,0,midEmptySpace,midEmptySpace,0,docEnd,docEnd,FALSE
TRUE,7,TextUnit_Line,1,midEmptySpace,midEmptySpace,0,docEnd,docEnd,FALSE
TRUE,7,TextUnit_Line,5,midEmptySpace,midEmptySpace,0,docEnd,docEnd,FALSE
FALSE,7,TextUnit_Line,-5,midEmptySpace,midEmptySpaceP1C,-5,docEndM5L,docEndM5L,FALSE
FALSE,7,TextUnit_Line,-1,midEmptySpace,midEmptySpaceP1C,-1,docEndM1L,docEndM1L,FALSE
FALSE,7,TextUnit_Line,0,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,FALSE
FALSE,7,TextUnit_Line,1,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,FALSE
FALSE,7,TextUnit_Line,5,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,FALSE
TRUE,7,TextUnit_Document,-5,midEmptySpace,midEmptySpace,-1,origin,origin,FALSE
TRUE,7,TextUnit_Document,-1,midEmptySpace,midEmptySpace,-1,origin,origin,FALSE
TRUE,7,TextUnit_Document,0,midEmptySpace,midEmptySpace,0,docEnd,docEnd,FALSE
TRUE,7,TextUnit_Document,1,midEmptySpace,midEmptySpace,0,docEnd,docEnd,FALSE
TRUE,7,TextUnit_Document,5,midEmptySpace,midEmptySpace,0,docEnd,docEnd,FALSE
FALSE,7,TextUnit_Document,-5,midEmptySpace,midEmptySpaceP1C,-1,origin,origin,FALSE
FALSE,7,TextUnit_Document,-1,midEmptySpace,midEmptySpaceP1C,-1,origin,origin,FALSE
FALSE,7,TextUnit_Document,0,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,FALSE
FALSE,7,TextUnit_Document,1,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,FALSE
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

1 Degenerate Position TextUnit MoveAmount Start End Result_MoveAmount Result_Start Result_End Skip
62 TRUE 1 TextUnit_Document -5 origin origin 0 origin origin FALSE
63 TRUE 1 TextUnit_Document -1 origin origin 0 origin origin FALSE
64 TRUE 1 TextUnit_Document 0 origin origin 0 origin origin FALSE
65 TRUE 1 TextUnit_Document 1 origin origin 1 endExclusive docEnd endExclusive docEnd FALSE
66 TRUE 1 TextUnit_Document 5 origin origin 1 endExclusive docEnd endExclusive docEnd FALSE
67 FALSE 1 TextUnit_Document -5 origin originP1C 0 origin endExclusive docEnd FALSE
68 FALSE 1 TextUnit_Document -1 origin originP1C 0 origin endExclusive docEnd FALSE
69 FALSE 1 TextUnit_Document 0 origin originP1C 0 origin endExclusive docEnd FALSE
70 FALSE 1 TextUnit_Document 1 origin originP1C 0 origin endExclusive docEnd FALSE
71 FALSE 1 TextUnit_Document 5 origin originP1C 0 origin endExclusive docEnd FALSE
72 TRUE 2 TextUnit_Document -5 midTop midTop -1 origin origin FALSE
73 TRUE 2 TextUnit_Document -1 midTop midTop -1 origin origin FALSE
74 TRUE 2 TextUnit_Document 0 midTop midTop 0 midTop midTop FALSE
75 TRUE 2 TextUnit_Document 1 midTop midTop 1 endExclusive docEnd endExclusive docEnd FALSE
76 TRUE 2 TextUnit_Document 5 midTop midTop 1 endExclusive docEnd endExclusive docEnd FALSE
77 FALSE 2 TextUnit_Document -5 midTop midTopP1C 0 origin endExclusive docEnd FALSE
78 FALSE 2 TextUnit_Document -1 midTop midTopP1C 0 origin endExclusive docEnd FALSE
79 FALSE 2 TextUnit_Document 0 midTop midTopP1C 0 origin endExclusive docEnd FALSE
80 FALSE 2 TextUnit_Document 1 midTop midTopP1C 0 origin endExclusive docEnd FALSE
81 FALSE 2 TextUnit_Document 5 midTop midTopP1C 0 origin endExclusive docEnd FALSE
82 TRUE 3 TextUnit_Document -5 midHistory midHistory -1 origin origin FALSE
83 TRUE 3 TextUnit_Document -1 midHistory midHistory -1 origin origin FALSE
84 TRUE 3 TextUnit_Document 0 midHistory midHistory 0 midHistory midHistory FALSE
85 TRUE 3 TextUnit_Document 1 midHistory midHistory 1 endExclusive docEnd endExclusive docEnd FALSE
86 TRUE 3 TextUnit_Document 5 midHistory midHistory 1 endExclusive docEnd endExclusive docEnd FALSE
87 FALSE 3 TextUnit_Document -5 midHistory midHistoryP1C 0 origin endExclusive docEnd FALSE
88 FALSE 3 TextUnit_Document -1 midHistory midHistoryP1C 0 origin endExclusive docEnd FALSE
89 FALSE 3 TextUnit_Document 0 midHistory midHistoryP1C 0 origin endExclusive docEnd FALSE
90 FALSE 3 TextUnit_Document 1 midHistory midHistoryP1C 0 origin endExclusive docEnd FALSE
91 FALSE 3 TextUnit_Document 5 midHistory midHistoryP1C 0 origin endExclusive docEnd FALSE
92 TRUE 8 TextUnit_Character -5 bufferEnd bufferEnd -5 bufferEndM5C docEndM5C bufferEndM5C docEndM5C FALSE
93 TRUE 8 TextUnit_Character -1 bufferEnd bufferEnd -1 bufferEndM1C docEndM1C bufferEndM1C docEndM1C FALSE
94 TRUE 8 TextUnit_Character 0 bufferEnd bufferEnd 0 bufferEnd docEnd bufferEnd docEnd FALSE
95 TRUE 8 TextUnit_Character 1 bufferEnd bufferEnd 1 0 endExclusive docEnd endExclusive docEnd TRUE FALSE
96 TRUE 8 TextUnit_Character 5 bufferEnd bufferEnd 1 0 endExclusive docEnd endExclusive docEnd TRUE FALSE
97 FALSE 8 TextUnit_Character -5 bufferEnd endExclusive -5 bufferEndM5C docEndM5C bufferEndM4C docEndM5C FALSE
98 FALSE 8 TextUnit_Character -1 bufferEnd endExclusive -1 bufferEndM1C docEndM1C bufferEnd docEndM1C FALSE
99 FALSE 8 TextUnit_Character 0 bufferEnd endExclusive 0 bufferEnd docEnd endExclusive docEnd FALSE
100 FALSE 8 TextUnit_Character 1 bufferEnd endExclusive 0 bufferEnd docEnd endExclusive docEnd FALSE
101 FALSE 8 TextUnit_Character 5 bufferEnd endExclusive 0 bufferEnd docEnd endExclusive docEnd FALSE
102 TRUE 8 TextUnit_Line -5 bufferEnd bufferEnd -5 bufferEndM4L docEndM5L bufferEndM4L docEndM5L FALSE
103 TRUE 8 TextUnit_Line -1 bufferEnd bufferEnd -1 bufferEndLeft docEndM1L bufferEndLeft docEndM1L FALSE
104 TRUE 8 TextUnit_Line 0 bufferEnd bufferEnd 0 bufferEnd docEnd bufferEnd docEnd FALSE
105 TRUE 8 TextUnit_Line 1 bufferEnd bufferEnd 1 0 endExclusive docEnd endExclusive docEnd TRUE FALSE
106 TRUE 8 TextUnit_Line 5 bufferEnd bufferEnd 1 0 endExclusive docEnd endExclusive docEnd TRUE FALSE
107 FALSE 8 TextUnit_Line -5 bufferEnd endExclusive -5 bufferEndM5L docEndM5L bufferEndM4L docEndM5L TRUE FALSE
108 FALSE 8 TextUnit_Line -1 bufferEnd endExclusive -1 bufferEndM1L docEndM1L bufferEndLeft docEndM1L TRUE FALSE
109 FALSE 8 TextUnit_Line 0 bufferEnd endExclusive 0 bufferEndLeft docEnd endExclusive docEnd TRUE FALSE
110 FALSE 8 TextUnit_Line 1 bufferEnd endExclusive 0 bufferEndLeft docEnd endExclusive docEnd TRUE FALSE
111 FALSE 8 TextUnit_Line 5 bufferEnd endExclusive 0 bufferEndLeft docEnd endExclusive docEnd TRUE FALSE
112 TRUE 8 TextUnit_Document -5 bufferEnd bufferEnd -1 origin origin FALSE
113 TRUE 8 TextUnit_Document -1 bufferEnd bufferEnd -1 origin origin FALSE
114 TRUE 8 TextUnit_Document 0 bufferEnd bufferEnd 0 bufferEnd docEnd bufferEnd docEnd FALSE
115 TRUE 8 TextUnit_Document 1 bufferEnd bufferEnd 1 0 endExclusive docEnd endExclusive docEnd TRUE FALSE
116 TRUE 8 TextUnit_Document 5 bufferEnd bufferEnd 1 0 endExclusive docEnd endExclusive docEnd TRUE FALSE
117 FALSE 8 TextUnit_Document -5 bufferEnd endExclusive 0 -1 origin endExclusive origin TRUE FALSE
118 FALSE 8 TextUnit_Document -1 bufferEnd endExclusive 0 -1 origin endExclusive origin TRUE FALSE
119 FALSE 8 TextUnit_Document 0 bufferEnd endExclusive 0 origin docEnd endExclusive docEnd TRUE FALSE
120 FALSE 8 TextUnit_Document 1 bufferEnd endExclusive 0 origin docEnd endExclusive docEnd TRUE FALSE
121 FALSE 8 TextUnit_Document 5 bufferEnd endExclusive 0 origin docEnd endExclusive docEnd TRUE FALSE
122 TRUE 9 TextUnit_Character -5 endExclusive endExclusive -5 bufferEndM4C docEndM5C bufferEndM4C docEndM5C FALSE
123 TRUE 9 TextUnit_Character -1 endExclusive endExclusive -1 bufferEnd docEndM1C bufferEnd docEndM1C FALSE
124 TRUE 9 TextUnit_Character 0 endExclusive endExclusive 0 endExclusive docEnd endExclusive docEnd FALSE
125 TRUE 9 TextUnit_Character 1 endExclusive endExclusive 0 endExclusive docEnd endExclusive docEnd FALSE
126 TRUE 9 TextUnit_Character 5 endExclusive endExclusive 0 endExclusive docEnd endExclusive docEnd FALSE
127 TRUE 9 TextUnit_Line -5 endExclusive endExclusive -5 bufferEndM4L docEndM5L bufferEndM4L docEndM5L FALSE
128 TRUE 9 TextUnit_Line -1 endExclusive endExclusive -1 bufferEndLeft docEndM1L bufferEndLeft docEndM1L FALSE
129 TRUE 9 TextUnit_Line 0 endExclusive endExclusive 0 endExclusive docEnd endExclusive docEnd FALSE
130 TRUE 9 TextUnit_Line 1 endExclusive endExclusive 0 endExclusive docEnd endExclusive docEnd FALSE
131 TRUE 9 TextUnit_Line 5 endExclusive endExclusive 0 endExclusive docEnd endExclusive docEnd FALSE
132 TRUE 9 TextUnit_Document -5 endExclusive endExclusive -1 origin origin FALSE
133 TRUE 9 TextUnit_Document -1 endExclusive endExclusive -1 origin origin FALSE
134 TRUE 9 TextUnit_Document 0 endExclusive endExclusive 0 endExclusive docEnd endExclusive docEnd FALSE
135 TRUE 9 TextUnit_Document 1 endExclusive endExclusive 0 endExclusive docEnd endExclusive docEnd FALSE
136 TRUE 9 TextUnit_Document 5 endExclusive endExclusive 0 endExclusive docEnd endExclusive docEnd FALSE
137 TRUE 4 TextUnit_Character -5 midDocEnd midDocEnd -5 midDocEndM5C midDocEndM5C TRUE FALSE
138 TRUE 4 TextUnit_Character -1 midDocEnd midDocEnd -1 midDocEndM1C midDocEndM1C TRUE FALSE
139 TRUE 4 TextUnit_Character 0 midDocEnd midDocEnd 0 midDocEnd midDocEnd TRUE FALSE
140 TRUE 4 TextUnit_Character 1 midDocEnd midDocEnd 1 midDocEndP1C midDocEndP1C TRUE FALSE
141 TRUE 4 TextUnit_Character 5 midDocEnd midDocEnd 5 midDocEndP5C midDocEndP5C TRUE FALSE
142 FALSE 4 TextUnit_Character -5 midDocEnd midDocEndP1C -5 midDocEndM5C midDocEndM4C TRUE FALSE
143 FALSE 4 TextUnit_Character -1 midDocEnd midDocEndP1C -1 midDocEndM1C midDocEnd TRUE FALSE
144 FALSE 4 TextUnit_Character 0 midDocEnd midDocEndP1C 0 midDocEnd midDocEndP1C TRUE FALSE
145 FALSE 4 TextUnit_Character 1 midDocEnd midDocEndP1C 1 midDocEndP1C midDocEndP2C TRUE FALSE
146 FALSE 4 TextUnit_Character 5 midDocEnd midDocEndP1C 5 midDocEndP5C midDocEndP6C TRUE FALSE
147 TRUE 4 TextUnit_Line -5 midDocEnd midDocEnd -5 midDocEndM4L midDocEndM4L TRUE FALSE
148 TRUE 4 TextUnit_Line -1 midDocEnd midDocEnd -1 midDocEndLeft midDocEndLeft TRUE FALSE
149 TRUE 4 TextUnit_Line 0 midDocEnd midDocEnd 0 midDocEnd midDocEnd TRUE FALSE
150 TRUE 4 TextUnit_Line 1 midDocEnd midDocEnd 1 docEnd docEnd TRUE FALSE
151 TRUE 4 TextUnit_Line 5 midDocEnd midDocEnd 1 docEnd docEnd TRUE FALSE
152 FALSE 4 TextUnit_Line -5 midDocEnd midDocEndP1C -5 midDocEndM5L midDocEndM4L TRUE FALSE
153 FALSE 4 TextUnit_Line -1 midDocEnd midDocEndP1C -1 midDocEndM1L midDocEndLeft TRUE FALSE
154 FALSE 4 TextUnit_Line 0 midDocEnd midDocEndP1C 0 midDocEndLeft docEnd TRUE FALSE
155 FALSE 4 TextUnit_Line 1 midDocEnd midDocEndP1C 0 midDocEndLeft docEnd TRUE FALSE
156 FALSE 4 TextUnit_Line 5 midDocEnd midDocEndP1C 0 midDocEndLeft docEnd TRUE FALSE
157 TRUE 4 TextUnit_Document -5 midDocEnd midDocEnd -1 origin origin TRUE FALSE
158 TRUE 4 TextUnit_Document -1 midDocEnd midDocEnd -1 origin origin TRUE FALSE
159 TRUE 4 TextUnit_Document 0 midDocEnd midDocEnd 0 midDocEnd midDocEnd TRUE FALSE
160 TRUE 4 TextUnit_Document 1 midDocEnd midDocEnd 1 docEnd docEnd TRUE FALSE
161 TRUE 4 TextUnit_Document 5 midDocEnd midDocEnd 1 docEnd docEnd TRUE FALSE
162 FALSE 4 TextUnit_Document -5 midDocEnd midDocEndP1C 0 origin docEnd TRUE FALSE
163 FALSE 4 TextUnit_Document -1 midDocEnd midDocEndP1C 0 origin docEnd TRUE FALSE
164 FALSE 4 TextUnit_Document 0 midDocEnd midDocEndP1C 0 origin docEnd TRUE FALSE
165 FALSE 4 TextUnit_Document 1 midDocEnd midDocEndP1C 0 origin docEnd TRUE FALSE
166 FALSE 4 TextUnit_Document 5 midDocEnd midDocEndP1C 0 origin docEnd TRUE FALSE
167 TRUE 5 TextUnit_Character -5 lastCharPos lastCharPos -5 lastCharPosM5C lastCharPosM5C TRUE FALSE
168 TRUE 5 TextUnit_Character -1 lastCharPos lastCharPos -1 lastCharPosM1C lastCharPosM1C TRUE FALSE
169 TRUE 5 TextUnit_Character 0 lastCharPos lastCharPos 0 lastCharPos lastCharPos TRUE FALSE
170 TRUE 5 TextUnit_Character 1 lastCharPos lastCharPos 1 docEnd lastCharPosP1C docEnd lastCharPosP1C TRUE FALSE
171 TRUE 5 TextUnit_Character 5 lastCharPos lastCharPos 1 5 docEnd lastCharPosP5C docEnd lastCharPosP5C TRUE FALSE
172 FALSE 5 TextUnit_Character -5 lastCharPos lastCharPosP1C -5 lastCharPosM5C lastCharPosM4C TRUE FALSE
173 FALSE 5 TextUnit_Character -1 lastCharPos lastCharPosP1C -1 lastCharPosM1C lastCharPos TRUE FALSE
174 FALSE 5 TextUnit_Character 0 lastCharPos lastCharPosP1C 0 lastCharPos docEnd lastCharPosP1C TRUE FALSE
175 FALSE 5 TextUnit_Character 1 lastCharPos lastCharPosP1C 0 1 lastCharPos lastCharPosP1C docEnd lastCharPosP2C TRUE FALSE
176 FALSE 5 TextUnit_Character 5 lastCharPos lastCharPosP1C 0 5 lastCharPos lastCharPosP5C docEnd lastCharPosP6C TRUE FALSE
177 TRUE 5 TextUnit_Line -5 lastCharPos lastCharPos -5 lastCharPosM4L lastCharPosM4L TRUE FALSE
178 TRUE 5 TextUnit_Line -1 lastCharPos lastCharPos -1 lastCharPosLeft lastCharPosLeft TRUE FALSE
179 TRUE 5 TextUnit_Line 0 lastCharPos lastCharPos 0 lastCharPos lastCharPos TRUE FALSE
180 TRUE 5 TextUnit_Line 1 lastCharPos lastCharPos 1 docEnd docEnd TRUE FALSE
181 TRUE 5 TextUnit_Line 5 lastCharPos lastCharPos 1 docEnd docEnd TRUE FALSE
182 FALSE 5 TextUnit_Line -5 lastCharPos lastCharPosP1C -5 lastCharPosM5L lastCharPosM4L TRUE FALSE
183 FALSE 5 TextUnit_Line -1 lastCharPos lastCharPosP1C -1 lastCharPosM1L lastCharPosLeft TRUE FALSE
184 FALSE 5 TextUnit_Line 0 lastCharPos lastCharPosP1C 0 lastCharPosLeft docEnd TRUE FALSE
185 FALSE 5 TextUnit_Line 1 lastCharPos lastCharPosP1C 0 lastCharPosLeft docEnd TRUE FALSE
186 FALSE 5 TextUnit_Line 5 lastCharPos lastCharPosP1C 0 lastCharPosLeft docEnd TRUE FALSE
187 TRUE 5 TextUnit_Document -5 lastCharPos lastCharPos -1 origin origin TRUE FALSE
188 TRUE 5 TextUnit_Document -1 lastCharPos lastCharPos -1 origin origin TRUE FALSE
189 TRUE 5 TextUnit_Document 0 lastCharPos lastCharPos 0 lastCharPos lastCharPos TRUE FALSE
190 TRUE 5 TextUnit_Document 1 lastCharPos lastCharPos 1 docEnd docEnd TRUE FALSE
191 TRUE 5 TextUnit_Document 5 lastCharPos lastCharPos 1 docEnd docEnd TRUE FALSE
192 FALSE 5 TextUnit_Document -5 lastCharPos lastCharPosP1C 0 origin docEnd TRUE FALSE
193 FALSE 5 TextUnit_Document -1 lastCharPos lastCharPosP1C 0 origin docEnd TRUE FALSE
194 FALSE 5 TextUnit_Document 0 lastCharPos lastCharPosP1C 0 origin docEnd TRUE FALSE
195 FALSE 5 TextUnit_Document 1 lastCharPos lastCharPosP1C 0 origin docEnd TRUE FALSE
196 FALSE 5 TextUnit_Document 5 lastCharPos lastCharPosP1C 0 origin docEnd TRUE FALSE
197 TRUE 6 TextUnit_Character -5 docEnd docEnd -5 docEndM5C docEndM5C TRUE FALSE
198 TRUE 6 TextUnit_Character -1 docEnd docEnd -1 docEndM1C docEndM1C TRUE FALSE
199 TRUE 6 TextUnit_Character 0 docEnd docEnd 0 docEnd docEnd TRUE FALSE
200 TRUE 6 TextUnit_Character 1 docEnd docEnd 0 docEnd docEnd TRUE FALSE
201 TRUE 6 TextUnit_Character 5 docEnd docEnd 0 docEnd docEnd TRUE FALSE
202 FALSE 6 TextUnit_Character -5 docEnd docEndP1C -5 docEndM5C docEndM5C TRUE FALSE
203 FALSE 6 TextUnit_Character -1 docEnd docEndP1C -1 docEndM1C docEndM1C TRUE FALSE
204 FALSE 6 TextUnit_Character 0 docEnd docEndP1C 0 docEnd docEnd TRUE FALSE
205 FALSE 6 TextUnit_Character 1 docEnd docEndP1C 0 docEnd docEnd TRUE FALSE
206 FALSE 6 TextUnit_Character 5 docEnd docEndP1C 0 docEnd docEnd TRUE FALSE
207 TRUE 6 TextUnit_Line -5 docEnd docEnd -5 docEndM4L docEndM5L docEndM4L docEndM5L TRUE FALSE
208 TRUE 6 TextUnit_Line -1 docEnd docEnd -1 docEndLeft docEndM1L docEndLeft docEndM1L TRUE FALSE
209 TRUE 6 TextUnit_Line 0 docEnd docEnd 0 docEnd docEnd TRUE FALSE
210 TRUE 6 TextUnit_Line 1 docEnd docEnd 0 docEnd docEnd TRUE FALSE
211 TRUE 6 TextUnit_Line 5 docEnd docEnd 0 docEnd docEnd TRUE FALSE
212 FALSE 6 TextUnit_Line -5 docEnd docEndP1C -5 docEndM4L docEndM5L docEndM4L docEndM5L TRUE FALSE
213 FALSE 6 TextUnit_Line -1 docEnd docEndP1C -1 docEndLeft docEndM1L docEndLeft docEndM1L TRUE FALSE
214 FALSE 6 TextUnit_Line 0 docEnd docEndP1C 0 docEnd docEnd TRUE FALSE
215 FALSE 6 TextUnit_Line 1 docEnd docEndP1C 0 docEnd docEnd TRUE FALSE
216 FALSE 6 TextUnit_Line 5 docEnd docEndP1C 0 docEnd docEnd TRUE FALSE
217 TRUE 6 TextUnit_Document -5 docEnd docEnd -1 origin origin TRUE FALSE
218 TRUE 6 TextUnit_Document -1 docEnd docEnd -1 origin origin TRUE FALSE
219 TRUE 6 TextUnit_Document 0 docEnd docEnd 0 docEnd docEnd TRUE FALSE
220 TRUE 6 TextUnit_Document 1 docEnd docEnd 0 docEnd docEnd TRUE FALSE
221 TRUE 6 TextUnit_Document 5 docEnd docEnd 0 docEnd docEnd TRUE FALSE
222 FALSE 6 TextUnit_Document -5 docEnd docEndP1C -1 origin origin TRUE FALSE
223 FALSE 6 TextUnit_Document -1 docEnd docEndP1C -1 origin origin TRUE FALSE
224 FALSE 6 TextUnit_Document 0 docEnd docEndP1C 0 docEnd docEnd TRUE FALSE
225 FALSE 6 TextUnit_Document 1 docEnd docEndP1C 0 docEnd docEnd TRUE FALSE
226 FALSE 6 TextUnit_Document 5 docEnd docEndP1C 0 docEnd docEnd TRUE FALSE
227 TRUE 7 TextUnit_Character -5 midEmptySpace midEmptySpace -5 docEndM5C docEndM5C TRUE FALSE
228 TRUE 7 TextUnit_Character -1 midEmptySpace midEmptySpace -1 docEndM1C docEndM1C TRUE FALSE
229 TRUE 7 TextUnit_Character 0 midEmptySpace midEmptySpace 0 docEnd docEnd TRUE FALSE
230 TRUE 7 TextUnit_Character 1 midEmptySpace midEmptySpace 0 docEnd docEnd TRUE FALSE
231 TRUE 7 TextUnit_Character 5 midEmptySpace midEmptySpace 0 docEnd docEnd TRUE FALSE
232 FALSE 7 TextUnit_Character -5 midEmptySpace midEmptySpaceP1C -5 docEndM5C docEndM5C TRUE FALSE
233 FALSE 7 TextUnit_Character -1 midEmptySpace midEmptySpaceP1C -1 docEndM1C docEndM1C TRUE FALSE
234 FALSE 7 TextUnit_Character 0 midEmptySpace midEmptySpaceP1C 0 docEnd docEnd TRUE FALSE
235 FALSE 7 TextUnit_Character 1 midEmptySpace midEmptySpaceP1C 0 docEnd docEnd TRUE FALSE
236 FALSE 7 TextUnit_Character 5 midEmptySpace midEmptySpaceP1C 0 docEnd docEnd TRUE FALSE
237 TRUE 7 TextUnit_Line -5 midEmptySpace midEmptySpace -5 docEndM4L docEndM5L docEndM4L docEndM5L TRUE FALSE
238 TRUE 7 TextUnit_Line -1 midEmptySpace midEmptySpace -1 docEndLeft docEndM1L docEndLeft docEndM1L TRUE FALSE
239 TRUE 7 TextUnit_Line 0 midEmptySpace midEmptySpace 0 docEnd docEnd TRUE FALSE
240 TRUE 7 TextUnit_Line 1 midEmptySpace midEmptySpace 0 docEnd docEnd TRUE FALSE
241 TRUE 7 TextUnit_Line 5 midEmptySpace midEmptySpace 0 docEnd docEnd TRUE FALSE
242 FALSE 7 TextUnit_Line -5 midEmptySpace midEmptySpaceP1C -5 docEndM4L docEndM5L docEndM4L docEndM5L TRUE FALSE
243 FALSE 7 TextUnit_Line -1 midEmptySpace midEmptySpaceP1C -1 docEndLeft docEndM1L docEndLeft docEndM1L TRUE FALSE
244 FALSE 7 TextUnit_Line 0 midEmptySpace midEmptySpaceP1C 0 docEnd docEnd TRUE FALSE
245 FALSE 7 TextUnit_Line 1 midEmptySpace midEmptySpaceP1C 0 docEnd docEnd TRUE FALSE
246 FALSE 7 TextUnit_Line 5 midEmptySpace midEmptySpaceP1C 0 docEnd docEnd TRUE FALSE
247 TRUE 7 TextUnit_Document -5 midEmptySpace midEmptySpace -1 origin origin TRUE FALSE
248 TRUE 7 TextUnit_Document -1 midEmptySpace midEmptySpace -1 origin origin TRUE FALSE
249 TRUE 7 TextUnit_Document 0 midEmptySpace midEmptySpace 0 docEnd docEnd TRUE FALSE
250 TRUE 7 TextUnit_Document 1 midEmptySpace midEmptySpace 0 docEnd docEnd TRUE FALSE
251 TRUE 7 TextUnit_Document 5 midEmptySpace midEmptySpace 0 docEnd docEnd TRUE FALSE
252 FALSE 7 TextUnit_Document -5 midEmptySpace midEmptySpaceP1C -1 origin origin TRUE FALSE
253 FALSE 7 TextUnit_Document -1 midEmptySpace midEmptySpaceP1C -1 origin origin TRUE FALSE
254 FALSE 7 TextUnit_Document 0 midEmptySpace midEmptySpaceP1C 0 docEnd docEnd TRUE FALSE
255 FALSE 7 TextUnit_Document 1 midEmptySpace midEmptySpaceP1C 0 docEnd docEnd TRUE FALSE
256 FALSE 7 TextUnit_Document 5 midEmptySpace midEmptySpaceP1C 0 docEnd docEnd TRUE FALSE
257 TRUE 1 TextUnit_Word -5 origin origin 0 origin origin FALSE
258 TRUE 1 TextUnit_Word -1 origin origin 0 origin origin FALSE
259 TRUE 1 TextUnit_Word 0 origin origin 0 origin origin FALSE
260 TRUE 1 TextUnit_Word 1 origin origin 1 segment1LmidTop segment1LmidTop FALSE
261 TRUE 1 TextUnit_Word 5 origin origin 5 segment0LmidTopP1L segment0LmidTopP1L FALSE
262 FALSE 1 TextUnit_Word -5 origin originP1C 0 origin segment1LmidTop FALSE
263 FALSE 1 TextUnit_Word -1 origin originP1C 0 origin segment1LmidTop FALSE
264 FALSE 1 TextUnit_Word 0 origin originP1C 0 origin segment1LmidTop FALSE
265 FALSE 1 TextUnit_Word 1 origin originP1C 1 segment1LmidTop segment2LmidTop FALSE
266 FALSE 1 TextUnit_Word 5 origin originP1C 5 segment0LmidTopP1L segment1LmidTopP1L FALSE
267 TRUE 2 TextUnit_Word -5 midTop midTop -3 origin origin TRUE
268 TRUE 2 TextUnit_Word -1 midTop midTop -1 segment2LmidTop segment2LmidTop TRUE
269 TRUE 2 TextUnit_Word 0 midTop midTop 0 midTop midTop FALSE
270 TRUE 2 TextUnit_Word 1 midTop midTop 1 segment3LmidTop segment3LmidTop FALSE
271 TRUE 2 TextUnit_Word 5 midTop midTop 5 segment2LmidTopP1L segment2LmidTopP1L FALSE
272 FALSE 2 TextUnit_Word -5 midTop midTopP1C -2 origin segment1LmidTop FALSE
273 FALSE 2 TextUnit_Word -1 midTop midTopP1C -1 segment1LmidTop segment2LmidTop FALSE
274 FALSE 2 TextUnit_Word 0 midTop midTopP1C 0 segment2LmidTop segment3LmidTop FALSE
275 FALSE 2 TextUnit_Word 1 midTop midTopP1C 1 segment3LmidTop segment4LmidTop FALSE
276 FALSE 2 TextUnit_Word 5 midTop midTopP1C 5 segment2LmidTopP1L segment3LmidTopP1L FALSE
277 TRUE 3 TextUnit_Word -5 midHistory midHistory -5 segment3LmidHistoryM1L segment3LmidHistoryM1L TRUE
278 TRUE 3 TextUnit_Word -1 midHistory midHistory -1 segment2LmidHistory segment2LmidHistory TRUE
279 TRUE 3 TextUnit_Word 0 midHistory midHistory 0 midHistory midHistory FALSE
280 TRUE 3 TextUnit_Word 1 midHistory midHistory 1 segment3LmidHistory segment3LmidHistory FALSE
281 TRUE 3 TextUnit_Word 5 midHistory midHistory 5 segment2LmidHistoryP1L segment2LmidHistoryP1L FALSE
282 FALSE 3 TextUnit_Word -5 midHistory midHistoryP1C -5 segment3LmidHistoryM1L segment4LmidHistoryM1L TRUE
283 FALSE 3 TextUnit_Word -1 midHistory midHistoryP1C -1 segment1LmidHistory segment2LmidHistory FALSE
284 FALSE 3 TextUnit_Word 0 midHistory midHistoryP1C 0 segment2LmidHistory segment3LmidHistory FALSE
285 FALSE 3 TextUnit_Word 1 midHistory midHistoryP1C 1 segment3LmidHistory segment4LmidHistory FALSE
286 FALSE 3 TextUnit_Word 5 midHistory midHistoryP1C 5 segment2LmidHistoryP1L segment3LmidHistoryP1L FALSE
287 TRUE 4 TextUnit_Word -5 midDocEnd midDocEnd -5 segment3LmidDocEndM1L segment3LmidDocEndM1L TRUE
288 TRUE 4 TextUnit_Word -1 midDocEnd midDocEnd -1 segment2LmidDocEnd segment2LmidDocEnd TRUE
289 TRUE 4 TextUnit_Word 0 midDocEnd midDocEnd 0 midDocEnd midDocEnd FALSE
290 TRUE 4 TextUnit_Word 1 midDocEnd midDocEnd 1 segment3LmidDocEnd segment3LmidDocEnd FALSE
291 TRUE 4 TextUnit_Word 5 midDocEnd midDocEnd 3 docEnd docEnd FALSE
292 FALSE 4 TextUnit_Word -5 midDocEnd midDocEndP1C -5 segment3LmidDocEndM1L segment4LmidDocEndM1L TRUE
293 FALSE 4 TextUnit_Word -1 midDocEnd midDocEndP1C -1 segment1LmidDocEnd segment2LmidDocEnd FALSE
294 FALSE 4 TextUnit_Word 0 midDocEnd midDocEndP1C 0 segment2LmidDocEnd segment3LmidDocEnd FALSE
295 FALSE 4 TextUnit_Word 1 midDocEnd midDocEndP1C 1 segment3LmidDocEnd segment4LmidDocEnd FALSE
296 FALSE 4 TextUnit_Word 5 midDocEnd midDocEndP1C 2 segment4LmidDocEnd docEnd FALSE
297 TRUE 5 TextUnit_Word -5 lastCharPos lastCharPos -5 lastCharPosLeft lastCharPosLeft TRUE
298 TRUE 5 TextUnit_Word -1 lastCharPos lastCharPos -1 segment4LmidDocEnd segment4LmidDocEnd TRUE
299 TRUE 5 TextUnit_Word 0 lastCharPos lastCharPos 0 lastCharPos lastCharPos FALSE
300 TRUE 5 TextUnit_Word 1 lastCharPos lastCharPos 1 docEnd docEnd FALSE
301 TRUE 5 TextUnit_Word 5 lastCharPos lastCharPos 1 docEnd docEnd FALSE
302 FALSE 5 TextUnit_Word -5 lastCharPos lastCharPosP1C -5 segment4LlastCharPosM1L lastCharPosLeft FALSE
303 FALSE 5 TextUnit_Word -1 lastCharPos lastCharPosP1C -1 segment3LmidDocEnd segment4LmidDocEnd FALSE
304 FALSE 5 TextUnit_Word 0 lastCharPos lastCharPosP1C 0 segment4LmidDocEnd docEnd FALSE
305 FALSE 5 TextUnit_Word 1 lastCharPos lastCharPosP1C 0 segment4LmidDocEnd docEnd FALSE
306 FALSE 5 TextUnit_Word 5 lastCharPos lastCharPosP1C 0 segment4LmidDocEnd docEnd FALSE
307 TRUE 6 TextUnit_Word -5 docEnd docEnd -5 midDocEndLeft midDocEndLeft TRUE
308 TRUE 6 TextUnit_Word -1 docEnd docEnd -1 segment4LmidDocEnd segment4LmidDocEnd TRUE
309 TRUE 6 TextUnit_Word 0 docEnd docEnd 0 docEnd docEnd FALSE
310 TRUE 6 TextUnit_Word 1 docEnd docEnd 0 docEnd docEnd FALSE
311 TRUE 6 TextUnit_Word 5 docEnd docEnd 0 docEnd docEnd FALSE
312 FALSE 6 TextUnit_Word -5 docEnd docEndP1C -5 midDocEndLeft midDocEndLeft TRUE
313 FALSE 6 TextUnit_Word -1 docEnd docEndP1C -1 segment4LmidDocEnd segment4LmidDocEnd TRUE
314 FALSE 6 TextUnit_Word 0 docEnd docEndP1C 0 docEnd docEnd FALSE
315 FALSE 6 TextUnit_Word 1 docEnd docEndP1C 0 docEnd docEnd FALSE
316 FALSE 6 TextUnit_Word 5 docEnd docEndP1C 0 docEnd docEnd FALSE
317 TRUE 7 TextUnit_Word -5 midEmptySpace midEmptySpace -5 midDocEndLeft midDocEndLeft TRUE
318 TRUE 7 TextUnit_Word -1 midEmptySpace midEmptySpace -1 segment4LmidDocEnd segment4LmidDocEnd TRUE
319 TRUE 7 TextUnit_Word 0 midEmptySpace midEmptySpace 0 docEnd docEnd FALSE
320 TRUE 7 TextUnit_Word 1 midEmptySpace midEmptySpace 0 docEnd docEnd FALSE
321 TRUE 7 TextUnit_Word 5 midEmptySpace midEmptySpace 0 docEnd docEnd FALSE
322 FALSE 7 TextUnit_Word -5 midEmptySpace midEmptySpaceP1C -5 midDocEndLeft midDocEndLeft TRUE
323 FALSE 7 TextUnit_Word -1 midEmptySpace midEmptySpaceP1C -1 segment4LmidDocEnd segment4LmidDocEnd TRUE
324 FALSE 7 TextUnit_Word 0 midEmptySpace midEmptySpaceP1C 0 docEnd docEnd FALSE
325 FALSE 7 TextUnit_Word 1 midEmptySpace midEmptySpaceP1C 0 docEnd docEnd FALSE
326 FALSE 7 TextUnit_Word 5 midEmptySpace midEmptySpaceP1C 0 docEnd docEnd FALSE
327 TRUE 8 TextUnit_Word -5 bufferEnd bufferEnd -5 midDocEndLeft midDocEndLeft TRUE
328 TRUE 8 TextUnit_Word -1 bufferEnd bufferEnd -1 segment4LmidDocEnd segment4LmidDocEnd TRUE
329 TRUE 8 TextUnit_Word 0 bufferEnd bufferEnd 0 docEnd docEnd FALSE
330 TRUE 8 TextUnit_Word 1 bufferEnd bufferEnd 0 docEnd docEnd FALSE
331 TRUE 8 TextUnit_Word 5 bufferEnd bufferEnd 0 docEnd docEnd FALSE
332 FALSE 8 TextUnit_Word -5 bufferEnd endExclusive -5 midDocEndLeft midDocEndLeft TRUE
333 FALSE 8 TextUnit_Word -1 bufferEnd endExclusive -1 segment4LmidDocEnd segment4LmidDocEnd TRUE
334 FALSE 8 TextUnit_Word 0 bufferEnd endExclusive 0 docEnd docEnd FALSE
335 FALSE 8 TextUnit_Word 1 bufferEnd endExclusive 0 docEnd docEnd FALSE
336 FALSE 8 TextUnit_Word 5 bufferEnd endExclusive 0 docEnd docEnd FALSE
337 TRUE 9 TextUnit_Word -5 endExclusive endExclusive -5 midDocEndLeft midDocEndLeft TRUE
338 TRUE 9 TextUnit_Word -1 endExclusive endExclusive -1 segment4LmidDocEnd segment4LmidDocEnd TRUE
339 TRUE 9 TextUnit_Word 0 endExclusive endExclusive 0 docEnd docEnd FALSE
340 TRUE 9 TextUnit_Word 1 endExclusive endExclusive 0 docEnd docEnd FALSE
341 TRUE 9 TextUnit_Word 5 endExclusive endExclusive 0 docEnd docEnd FALSE