From 5deb3326079b2f0ec9561c42f12182607e5711cd Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Wed, 22 Sep 2021 10:50:34 -0700 Subject: [PATCH] Fix UIA Word movement tests (#11253) ## Summary of the Pull Request Fixes the 24 failing generated tests. 20 of them were fixed by enforcing the following rule: when moving backwards by word... - a degenerate range moves to the beginning of the word, then to the word behind it. - a non-degenerate range outright moves to the word behind it. The fix was simple: if we're a degenerate range, check if we're at the beginning of the word. If not, move there. Otherwise, move to the word before it. See UiaTextRangeBase.cpp changes for implementation details. Along the way, several misauthored tests were found: - 2 generated tests: - Cause: MS Word considers a line break a word delimiter. We don't use line-wrapping to distinguish two separate words. - `MovementAtExclusiveEnd` backwards word movement tests: - `end` will always be `writeTarget` because... - [degenerate range case] both `start` and `end` are moved to the beginning of the word (`writeTarget`) - [non-degenerate range case] from the `UiaTextRangeBase` bugfix, we should be moving to the word behind it. - this misauthored test was explicitly found by fixing the bug first explained here. ## References #10925 Word navigation testing --- .../GeneratedUiaTextRangeMovementTests.g.cpp | 60 +++++++++---------- .../UiaTextRangeTests.cpp | 6 +- src/types/UiaTextRangeBase.cpp | 28 +++++++++ src/types/UiaTextRangeBase.hpp | 1 + tools/TestTableWriter/UiaTests.csv | 48 +++++++-------- 5 files changed, 87 insertions(+), 56 deletions(-) diff --git a/src/interactivity/win32/ut_interactivity_win32/GeneratedUiaTextRangeMovementTests.g.cpp b/src/interactivity/win32/ut_interactivity_win32/GeneratedUiaTextRangeMovementTests.g.cpp index 34cffaf97..a1339bb8c 100644 --- a/src/interactivity/win32/ut_interactivity_win32/GeneratedUiaTextRangeMovementTests.g.cpp +++ b/src/interactivity/win32/ut_interactivity_win32/GeneratedUiaTextRangeMovementTests.g.cpp @@ -89,7 +89,9 @@ constexpr til::point segment1LmidHistory{ segment1, midHistory.y() }; constexpr til::point segment1LmidTop{ segment1, midTop.y() }; constexpr til::point segment1LmidTopP1L{ segment1, midTopP1L.y() }; constexpr til::point segment2LmidDocEnd{ segment2, midDocEnd.y() }; +constexpr til::point segment2LmidDocEndM1L{ segment2, midDocEndM1L.y() }; constexpr til::point segment2LmidHistory{ segment2, midHistory.y() }; +constexpr til::point segment2LmidHistoryM1L{ segment2, midHistoryM1L.y() }; constexpr til::point segment2LmidHistoryP1L{ segment2, midHistoryP1L.y() }; constexpr til::point segment2LmidTop{ segment2, midTop.y() }; constexpr til::point segment2LmidTopP1L{ segment2, midTopP1L.y() }; @@ -102,9 +104,7 @@ constexpr til::point segment3LmidTop{ segment3, midTop.y() }; constexpr til::point segment3LmidTopP1L{ segment3, midTopP1L.y() }; constexpr til::point segment4LlastCharPosM1L{ segment4, lastCharPosM1L.y() }; constexpr til::point segment4LmidDocEnd{ segment4, midDocEnd.y() }; -constexpr til::point segment4LmidDocEndM1L{ segment4, midDocEndM1L.y() }; constexpr til::point segment4LmidHistory{ segment4, midHistory.y() }; -constexpr til::point segment4LmidHistoryM1L{ segment4, midHistoryM1L.y() }; constexpr til::point segment4LmidTop{ segment4, midTop.y() }; struct GeneratedMovementTestInput { @@ -3319,7 +3319,7 @@ static constexpr std::array s_movementTests{ -3, origin, origin }, - true }, + false }, GeneratedMovementTest{ L"Move degenerate range at position 2 -1 times by Word", GeneratedMovementTestInput{ @@ -3331,7 +3331,7 @@ static constexpr std::array s_movementTests{ -1, segment2LmidTop, segment2LmidTop }, - true }, + false }, GeneratedMovementTest{ L"Move degenerate range at position 2 0 times by Word", GeneratedMovementTestInput{ @@ -3439,7 +3439,7 @@ static constexpr std::array s_movementTests{ -5, segment3LmidHistoryM1L, segment3LmidHistoryM1L }, - true }, + false }, GeneratedMovementTest{ L"Move degenerate range at position 3 -1 times by Word", GeneratedMovementTestInput{ @@ -3451,7 +3451,7 @@ static constexpr std::array s_movementTests{ -1, segment2LmidHistory, segment2LmidHistory }, - true }, + false }, GeneratedMovementTest{ L"Move degenerate range at position 3 0 times by Word", GeneratedMovementTestInput{ @@ -3497,9 +3497,9 @@ static constexpr std::array s_movementTests{ midHistoryP1C }, GeneratedMovementTestExpected{ -5, - segment3LmidHistoryM1L, - segment4LmidHistoryM1L }, - true }, + segment2LmidHistoryM1L, + segment3LmidHistoryM1L }, + false }, GeneratedMovementTest{ L"Move non-degenerate range at position 3 -1 times by Word", GeneratedMovementTestInput{ @@ -3559,7 +3559,7 @@ static constexpr std::array s_movementTests{ -5, segment3LmidDocEndM1L, segment3LmidDocEndM1L }, - true }, + false }, GeneratedMovementTest{ L"Move degenerate range at position 4 -1 times by Word", GeneratedMovementTestInput{ @@ -3571,7 +3571,7 @@ static constexpr std::array s_movementTests{ -1, segment2LmidDocEnd, segment2LmidDocEnd }, - true }, + false }, GeneratedMovementTest{ L"Move degenerate range at position 4 0 times by Word", GeneratedMovementTestInput{ @@ -3617,9 +3617,9 @@ static constexpr std::array s_movementTests{ midDocEndP1C }, GeneratedMovementTestExpected{ -5, - segment3LmidDocEndM1L, - segment4LmidDocEndM1L }, - true }, + segment2LmidDocEndM1L, + segment3LmidDocEndM1L }, + false }, GeneratedMovementTest{ L"Move non-degenerate range at position 4 -1 times by Word", GeneratedMovementTestInput{ @@ -3679,7 +3679,7 @@ static constexpr std::array s_movementTests{ -5, lastCharPosLeft, lastCharPosLeft }, - true }, + false }, GeneratedMovementTest{ L"Move degenerate range at position 5 -1 times by Word", GeneratedMovementTestInput{ @@ -3691,7 +3691,7 @@ static constexpr std::array s_movementTests{ -1, segment4LmidDocEnd, segment4LmidDocEnd }, - true }, + false }, GeneratedMovementTest{ L"Move degenerate range at position 5 0 times by Word", GeneratedMovementTestInput{ @@ -3799,7 +3799,7 @@ static constexpr std::array s_movementTests{ -5, midDocEndLeft, midDocEndLeft }, - true }, + false }, GeneratedMovementTest{ L"Move degenerate range at position 6 -1 times by Word", GeneratedMovementTestInput{ @@ -3811,7 +3811,7 @@ static constexpr std::array s_movementTests{ -1, segment4LmidDocEnd, segment4LmidDocEnd }, - true }, + false }, GeneratedMovementTest{ L"Move degenerate range at position 6 0 times by Word", GeneratedMovementTestInput{ @@ -3859,7 +3859,7 @@ static constexpr std::array s_movementTests{ -5, midDocEndLeft, midDocEndLeft }, - true }, + false }, GeneratedMovementTest{ L"Move non-degenerate range at position 6 -1 times by Word", GeneratedMovementTestInput{ @@ -3871,7 +3871,7 @@ static constexpr std::array s_movementTests{ -1, segment4LmidDocEnd, segment4LmidDocEnd }, - true }, + false }, GeneratedMovementTest{ L"Move non-degenerate range at position 6 0 times by Word", GeneratedMovementTestInput{ @@ -3919,7 +3919,7 @@ static constexpr std::array s_movementTests{ -5, midDocEndLeft, midDocEndLeft }, - true }, + false }, GeneratedMovementTest{ L"Move degenerate range at position 7 -1 times by Word", GeneratedMovementTestInput{ @@ -3931,7 +3931,7 @@ static constexpr std::array s_movementTests{ -1, segment4LmidDocEnd, segment4LmidDocEnd }, - true }, + false }, GeneratedMovementTest{ L"Move degenerate range at position 7 0 times by Word", GeneratedMovementTestInput{ @@ -3979,7 +3979,7 @@ static constexpr std::array s_movementTests{ -5, midDocEndLeft, midDocEndLeft }, - true }, + false }, GeneratedMovementTest{ L"Move non-degenerate range at position 7 -1 times by Word", GeneratedMovementTestInput{ @@ -3991,7 +3991,7 @@ static constexpr std::array s_movementTests{ -1, segment4LmidDocEnd, segment4LmidDocEnd }, - true }, + false }, GeneratedMovementTest{ L"Move non-degenerate range at position 7 0 times by Word", GeneratedMovementTestInput{ @@ -4039,7 +4039,7 @@ static constexpr std::array s_movementTests{ -5, midDocEndLeft, midDocEndLeft }, - true }, + false }, GeneratedMovementTest{ L"Move degenerate range at position 8 -1 times by Word", GeneratedMovementTestInput{ @@ -4051,7 +4051,7 @@ static constexpr std::array s_movementTests{ -1, segment4LmidDocEnd, segment4LmidDocEnd }, - true }, + false }, GeneratedMovementTest{ L"Move degenerate range at position 8 0 times by Word", GeneratedMovementTestInput{ @@ -4099,7 +4099,7 @@ static constexpr std::array s_movementTests{ -5, midDocEndLeft, midDocEndLeft }, - true }, + false }, GeneratedMovementTest{ L"Move non-degenerate range at position 8 -1 times by Word", GeneratedMovementTestInput{ @@ -4111,7 +4111,7 @@ static constexpr std::array s_movementTests{ -1, segment4LmidDocEnd, segment4LmidDocEnd }, - true }, + false }, GeneratedMovementTest{ L"Move non-degenerate range at position 8 0 times by Word", GeneratedMovementTestInput{ @@ -4159,7 +4159,7 @@ static constexpr std::array s_movementTests{ -5, midDocEndLeft, midDocEndLeft }, - true }, + false }, GeneratedMovementTest{ L"Move degenerate range at position 9 -1 times by Word", GeneratedMovementTestInput{ @@ -4171,7 +4171,7 @@ static constexpr std::array s_movementTests{ -1, segment4LmidDocEnd, segment4LmidDocEnd }, - true }, + false }, GeneratedMovementTest{ L"Move degenerate range at position 9 0 times by Word", GeneratedMovementTestInput{ diff --git a/src/interactivity/win32/ut_interactivity_win32/UiaTextRangeTests.cpp b/src/interactivity/win32/ut_interactivity_win32/UiaTextRangeTests.cpp index 816913825..99ae12628 100644 --- a/src/interactivity/win32/ut_interactivity_win32/UiaTextRangeTests.cpp +++ b/src/interactivity/win32/ut_interactivity_win32/UiaTextRangeTests.cpp @@ -1417,11 +1417,13 @@ class UiaTextRangeTests // reset the UTR if (degenerate) { + // UTR: (exclusive, exclusive) range const auto utrStart{ atDocumentEnd ? documentEndExclusive : endExclusive }; THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize(&utr, _pUiaData, &_dummyProvider, utrStart, utrEnd)); } else { + // UTR: (inclusive, exclusive) range const auto utrStart{ atDocumentEnd ? documentEndInclusive : endInclusive }; THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize(&utr, _pUiaData, &_dummyProvider, utrStart, utrEnd)); } @@ -1443,8 +1445,8 @@ class UiaTextRangeTests else if (textUnit <= TextUnit::TextUnit_Word) { VERIFY_ARE_EQUAL(-1, moveAmt); - VERIFY_ARE_EQUAL(origin, til::point{ utr->_start }); - VERIFY_ARE_EQUAL(degenerate || !atDocumentEnd ? origin : writeTarget, til::point{ utr->_end }); + VERIFY_ARE_EQUAL(degenerate || !atDocumentEnd ? writeTarget : origin, til::point{ utr->_start }); + VERIFY_ARE_EQUAL(writeTarget, til::point{ utr->_end }); } else if (textUnit <= TextUnit::TextUnit_Line) { diff --git a/src/types/UiaTextRangeBase.cpp b/src/types/UiaTextRangeBase.cpp index ace7002c2..860dabcd7 100644 --- a/src/types/UiaTextRangeBase.cpp +++ b/src/types/UiaTextRangeBase.cpp @@ -1522,6 +1522,14 @@ void UiaTextRangeBase::_moveEndpointByUnitWord(_In_ const int moveCount, { success = false; } + else if (allowBottomExclusive && _tryMoveToWordStart(buffer, documentEnd, resultPos)) + { + // IMPORTANT: _tryMoveToWordStart modifies resultPos if successful + // Degenerate ranges first move to the beginning of the word, + // but if we're already at the beginning of the word, we continue + // to the next branch and move to the previous word! + (*pAmountMoved)--; + } else if (buffer.MoveToPreviousWord(nextPos, _wordDelimiters)) { resultPos = nextPos; @@ -1541,6 +1549,26 @@ void UiaTextRangeBase::_moveEndpointByUnitWord(_In_ const int moveCount, SetEndpoint(endpoint, resultPos); } +// Routine Description: +// - tries to move resultingPos to the beginning of the word +// Arguments: +// - buffer - the text buffer we're operating on +// - documentEnd - the document end of the buffer (see _getDocumentEnd()) +// - resultingPos - the position we're starting from and modifying +// Return Value: +// - true --> we were not at the beginning of the word, and we updated resultingPos to be so +// - false --> otherwise (we're already at the beginning of the word) +bool UiaTextRangeBase::_tryMoveToWordStart(const TextBuffer& buffer, const til::point documentEnd, COORD& resultingPos) const +{ + const auto wordStart{ buffer.GetWordStart(resultingPos, _wordDelimiters, true, documentEnd) }; + if (resultingPos != wordStart) + { + resultingPos = wordStart; + return true; + } + return false; +} + // Routine Description: // - moves the UTR's endpoint by moveCount times by line. // - if endpoints crossed, the degenerate range is created and both endpoints are moved diff --git a/src/types/UiaTextRangeBase.hpp b/src/types/UiaTextRangeBase.hpp index b3a171b98..24756648f 100644 --- a/src/types/UiaTextRangeBase.hpp +++ b/src/types/UiaTextRangeBase.hpp @@ -182,6 +182,7 @@ namespace Microsoft::Console::Types std::optional _verifyAttr(TEXTATTRIBUTEID attributeId, VARIANT val, const TextAttribute& attr) const; bool _initializeAttrQuery(TEXTATTRIBUTEID attributeId, VARIANT* pRetVal, const TextAttribute& attr) const; + bool _tryMoveToWordStart(const TextBuffer& buffer, const til::point documentEnd, COORD& resultingPos) const; COORD _getInclusiveEnd() noexcept; diff --git a/tools/TestTableWriter/UiaTests.csv b/tools/TestTableWriter/UiaTests.csv index 088283b25..44108c96a 100644 --- a/tools/TestTableWriter/UiaTests.csv +++ b/tools/TestTableWriter/UiaTests.csv @@ -264,8 +264,8 @@ 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,-5,midTop,midTop,-3,origin,origin,FALSE +TRUE,2,TextUnit_Word,-1,midTop,midTop,-1,segment2LmidTop,segment2LmidTop,FALSE 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 @@ -274,28 +274,28 @@ FALSE,2,TextUnit_Word,-1,midTop,midTopP1C,-1,segment1LmidTop,segment2LmidTop,FAL 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,-5,midHistory,midHistory,-5,segment3LmidHistoryM1L,segment3LmidHistoryM1L,FALSE +TRUE,3,TextUnit_Word,-1,midHistory,midHistory,-1,segment2LmidHistory,segment2LmidHistory,FALSE 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,-5,midHistory,midHistoryP1C,-5,segment2LmidHistoryM1L,segment3LmidHistoryM1L,FALSE 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,-5,midDocEnd,midDocEnd,-5,segment3LmidDocEndM1L,segment3LmidDocEndM1L,FALSE +TRUE,4,TextUnit_Word,-1,midDocEnd,midDocEnd,-1,segment2LmidDocEnd,segment2LmidDocEnd,FALSE 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,-5,midDocEnd,midDocEndP1C,-5,segment2LmidDocEndM1L,segment3LmidDocEndM1L,FALSE 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,-5,lastCharPos,lastCharPos,-5,lastCharPosLeft,lastCharPosLeft,FALSE +TRUE,5,TextUnit_Word,-1,lastCharPos,lastCharPos,-1,segment4LmidDocEnd,segment4LmidDocEnd,FALSE 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 @@ -304,38 +304,38 @@ FALSE,5,TextUnit_Word,-1,lastCharPos,lastCharPosP1C,-1,segment3LmidDocEnd,segmen 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,-5,docEnd,docEnd,-5,midDocEndLeft,midDocEndLeft,FALSE +TRUE,6,TextUnit_Word,-1,docEnd,docEnd,-1,segment4LmidDocEnd,segment4LmidDocEnd,FALSE 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,-5,docEnd,docEndP1C,-5,midDocEndLeft,midDocEndLeft,FALSE +FALSE,6,TextUnit_Word,-1,docEnd,docEndP1C,-1,segment4LmidDocEnd,segment4LmidDocEnd,FALSE 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,-5,midEmptySpace,midEmptySpace,-5,midDocEndLeft,midDocEndLeft,FALSE +TRUE,7,TextUnit_Word,-1,midEmptySpace,midEmptySpace,-1,segment4LmidDocEnd,segment4LmidDocEnd,FALSE 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,-5,midEmptySpace,midEmptySpaceP1C,-5,midDocEndLeft,midDocEndLeft,FALSE +FALSE,7,TextUnit_Word,-1,midEmptySpace,midEmptySpaceP1C,-1,segment4LmidDocEnd,segment4LmidDocEnd,FALSE 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,-5,bufferEnd,bufferEnd,-5,midDocEndLeft,midDocEndLeft,FALSE +TRUE,8,TextUnit_Word,-1,bufferEnd,bufferEnd,-1,segment4LmidDocEnd,segment4LmidDocEnd,FALSE 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,-5,bufferEnd,endExclusive,-5,midDocEndLeft,midDocEndLeft,FALSE +FALSE,8,TextUnit_Word,-1,bufferEnd,endExclusive,-1,segment4LmidDocEnd,segment4LmidDocEnd,FALSE 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,-5,endExclusive,endExclusive,-5,midDocEndLeft,midDocEndLeft,FALSE +TRUE,9,TextUnit_Word,-1,endExclusive,endExclusive,-1,segment4LmidDocEnd,segment4LmidDocEnd,FALSE 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