diff options
-rw-r--r-- | src/CellBuffer.cxx | 27 | ||||
-rw-r--r-- | src/CellBuffer.h | 1 | ||||
-rw-r--r-- | src/Document.cxx | 37 | ||||
-rw-r--r-- | src/Document.h | 6 | ||||
-rw-r--r-- | src/Editor.cxx | 6 | ||||
-rw-r--r-- | test/unit/testCellBuffer.cxx | 52 |
6 files changed, 92 insertions, 37 deletions
diff --git a/src/CellBuffer.cxx b/src/CellBuffer.cxx index ba11f1391..4b061db40 100644 --- a/src/CellBuffer.cxx +++ b/src/CellBuffer.cxx @@ -848,6 +848,33 @@ Sci::Position CellBuffer::LineStart(Sci::Line line) const noexcept { return plv->LineStart(line); } +Sci::Position CellBuffer::LineEnd(Sci::Line line) const noexcept { + if (line >= Lines() - 1) { + return LineStart(line + 1); + } else { + Sci::Position position = LineStart(line + 1); + if (LineEndType::Unicode == GetLineEndTypes()) { + const unsigned char bytes[] = { + UCharAt(position - 3), + UCharAt(position - 2), + UCharAt(position - 1), + }; + if (UTF8IsSeparator(bytes)) { + return position - UTF8SeparatorLength; + } + if (UTF8IsNEL(bytes + 1)) { + return position - UTF8NELLength; + } + } + position--; // Back over CR or LF + // When line terminator is CR+LF, may need to go back one more + if ((position > LineStart(line)) && (CharAt(position - 1) == '\r')) { + position--; + } + return position; + } +} + Sci::Line CellBuffer::LineFromPosition(Sci::Position pos) const noexcept { return plv->LineFromPosition(pos); } diff --git a/src/CellBuffer.h b/src/CellBuffer.h index 7f0b87c4d..f0aacf8ec 100644 --- a/src/CellBuffer.h +++ b/src/CellBuffer.h @@ -186,6 +186,7 @@ public: Sci::Line Lines() const noexcept; void AllocateLines(Sci::Line lines); Sci::Position LineStart(Sci::Line line) const noexcept; + Sci::Position LineEnd(Sci::Line line) const noexcept; Sci::Position IndexLineStart(Sci::Line line, Scintilla::LineCharacterIndexType lineCharacterIndex) const noexcept; Sci::Line LineFromPosition(Sci::Position pos) const noexcept; Sci::Line LineFromPositionIndex(Sci::Position pos, Scintilla::LineCharacterIndexType lineCharacterIndex) const noexcept; diff --git a/src/Document.cxx b/src/Document.cxx index a45479e4b..afe6717a1 100644 --- a/src/Document.cxx +++ b/src/Document.cxx @@ -458,30 +458,7 @@ bool Document::IsLineStartPosition(Sci::Position position) const { } Sci_Position SCI_METHOD Document::LineEnd(Sci_Position line) const { - if (line >= LinesTotal() - 1) { - return LineStart(line + 1); - } else { - Sci::Position position = LineStart(line + 1); - if (LineEndType::Unicode == cb.GetLineEndTypes()) { - const unsigned char bytes[] = { - cb.UCharAt(position-3), - cb.UCharAt(position-2), - cb.UCharAt(position-1), - }; - if (UTF8IsSeparator(bytes)) { - return position - UTF8SeparatorLength; - } - if (UTF8IsNEL(bytes+1)) { - return position - UTF8NELLength; - } - } - position--; // Back over CR or LF - // When line terminator is CR+LF, may need to go back one more - if ((position > LineStart(line)) && (cb.CharAt(position - 1) == '\r')) { - position--; - } - return position; - } + return cb.LineEnd(line); } void SCI_METHOD Document::SetErrorStatus(int status) { @@ -500,16 +477,16 @@ Sci::Line Document::SciLineFromPosition(Sci::Position pos) const noexcept { return cb.LineFromPosition(pos); } -Sci::Position Document::LineEndPosition(Sci::Position position) const { - return LineEnd(LineFromPosition(position)); +Sci::Position Document::LineEndPosition(Sci::Position position) const noexcept { + return cb.LineEnd(cb.LineFromPosition(position)); } -bool Document::IsLineEndPosition(Sci::Position position) const { - return LineEnd(LineFromPosition(position)) == position; +bool Document::IsLineEndPosition(Sci::Position position) const noexcept { + return LineEndPosition(position) == position; } -bool Document::IsPositionInLineEnd(Sci::Position position) const { - return position >= LineEnd(LineFromPosition(position)); +bool Document::IsPositionInLineEnd(Sci::Position position) const noexcept { + return position >= LineEndPosition(position); } Sci::Position Document::VCHomePosition(Sci::Position position) const { diff --git a/src/Document.h b/src/Document.h index 72ed532b3..8889d6454 100644 --- a/src/Document.h +++ b/src/Document.h @@ -457,9 +457,9 @@ public: [[nodiscard]] Range LineRange(Sci::Line line) const noexcept; bool IsLineStartPosition(Sci::Position position) const; Sci_Position SCI_METHOD LineEnd(Sci_Position line) const override; - Sci::Position LineEndPosition(Sci::Position position) const; - bool IsLineEndPosition(Sci::Position position) const; - bool IsPositionInLineEnd(Sci::Position position) const; + Sci::Position LineEndPosition(Sci::Position position) const noexcept; + bool IsLineEndPosition(Sci::Position position) const noexcept; + bool IsPositionInLineEnd(Sci::Position position) const noexcept; Sci::Position VCHomePosition(Sci::Position position) const; Sci::Position IndexLineStart(Sci::Line line, Scintilla::LineCharacterIndexType lineCharacterIndex) const noexcept; Sci::Line LineFromPositionIndex(Sci::Position pos, Scintilla::LineCharacterIndexType lineCharacterIndex) const noexcept; diff --git a/src/Editor.cxx b/src/Editor.cxx index 80436fd21..dd2fc7e98 100644 --- a/src/Editor.cxx +++ b/src/Editor.cxx @@ -625,13 +625,11 @@ SelectionRange Editor::LineSelectionRange(SelectionPosition currentPos_, Selecti if (currentPos_ > anchor_) { anchor_ = SelectionPosition( pdoc->LineStart(pdoc->LineFromPosition(anchor_.Position()))); - currentPos_ = SelectionPosition( - pdoc->LineEnd(pdoc->LineFromPosition(currentPos_.Position()))); + currentPos_ = SelectionPosition(pdoc->LineEndPosition(currentPos_.Position())); } else { currentPos_ = SelectionPosition( pdoc->LineStart(pdoc->LineFromPosition(currentPos_.Position()))); - anchor_ = SelectionPosition( - pdoc->LineEnd(pdoc->LineFromPosition(anchor_.Position()))); + anchor_ = SelectionPosition(pdoc->LineEndPosition(anchor_.Position())); } return SelectionRange(currentPos_, anchor_); } diff --git a/test/unit/testCellBuffer.cxx b/test/unit/testCellBuffer.cxx index 690611d2f..b44f47735 100644 --- a/test/unit/testCellBuffer.cxx +++ b/test/unit/testCellBuffer.cxx @@ -72,6 +72,58 @@ TEST_CASE("CellBuffer") { REQUIRE(!cb.CanRedo()); } + SECTION("LineEnds") { + // Check that various line ends produce correct result from LineEnd. + cb.SetLineEndTypes(LineEndType::Unicode); + bool startSequence = false; + { + // Unix \n + const char sText2[] = "Two\nLines"; + const Sci::Position sLength2 = static_cast<Sci::Position>(strlen(sText2)); + cb.InsertString(0, sText2, strlen(sText2), startSequence); + REQUIRE(3 == cb.LineEnd(0)); + REQUIRE(sLength2 == cb.LineEnd(1)); + cb.DeleteChars(0, sLength2, startSequence); + } + { + // Windows \r\n + const char sText2[] = "Two\r\nLines"; + const Sci::Position sLength2 = static_cast<Sci::Position>(strlen(sText2)); + cb.InsertString(0, sText2, sLength2, startSequence); + REQUIRE(3 == cb.LineEnd(0)); + REQUIRE(sLength2 == cb.LineEnd(1)); + cb.DeleteChars(0, sLength2, startSequence); + } + { + // Old macOS \r + const char sText2[] = "Two\rLines"; + const Sci::Position sLength2 = static_cast<Sci::Position>(strlen(sText2)); + cb.InsertString(0, sText2, strlen(sText2), startSequence); + REQUIRE(3 == cb.LineEnd(0)); + REQUIRE(sLength2 == cb.LineEnd(1)); + cb.DeleteChars(0, sLength2, startSequence); + } + { + // Unicode NEL is U+0085 \xc2\x85 + const char sText2[] = "Two\xc2\x85Lines"; + const Sci::Position sLength2 = static_cast<Sci::Position>(strlen(sText2)); + cb.InsertString(0, sText2, sLength2, startSequence); + REQUIRE(3 == cb.LineEnd(0)); + REQUIRE(sLength2 == cb.LineEnd(1)); + cb.DeleteChars(0, sLength2, startSequence); + } + { + // Unicode LS line separator is U+2028 \xe2\x80\xa8 + const char sText2[] = "Two\xe2\x80\xa8Lines"; + const Sci::Position sLength2 = static_cast<Sci::Position>(strlen(sText2)); + cb.InsertString(0, sText2, sLength2, startSequence); + REQUIRE(3 == cb.LineEnd(0)); + REQUIRE(sLength2 == cb.LineEnd(1)); + cb.DeleteChars(0, sLength2, startSequence); + } + cb.SetLineEndTypes(LineEndType::Default); + } + SECTION("UndoOff") { REQUIRE(cb.IsCollectingUndo()); cb.SetUndoCollection(false); |