2015-06-07 21:19:26 +00:00
|
|
|
// Unit Tests for Scintilla internal data structures
|
|
|
|
|
2019-05-04 18:14:48 +00:00
|
|
|
#include <cstddef>
|
2015-06-07 21:19:26 +00:00
|
|
|
#include <cstring>
|
|
|
|
#include <stdexcept>
|
2019-05-04 18:14:48 +00:00
|
|
|
#include <string_view>
|
|
|
|
#include <vector>
|
2015-06-07 21:19:26 +00:00
|
|
|
#include <algorithm>
|
2019-05-04 18:14:48 +00:00
|
|
|
#include <memory>
|
2015-06-07 21:19:26 +00:00
|
|
|
|
|
|
|
#include "Platform.h"
|
|
|
|
|
2019-05-04 18:14:48 +00:00
|
|
|
#include "Scintilla.h"
|
|
|
|
#include "Position.h"
|
2015-06-07 21:19:26 +00:00
|
|
|
#include "SplitVector.h"
|
|
|
|
#include "Partitioning.h"
|
|
|
|
#include "RunStyles.h"
|
|
|
|
#include "CellBuffer.h"
|
|
|
|
|
|
|
|
#include "catch.hpp"
|
|
|
|
|
2019-05-04 18:14:48 +00:00
|
|
|
using namespace Scintilla;
|
|
|
|
|
2015-06-07 21:19:26 +00:00
|
|
|
// Test CellBuffer.
|
|
|
|
|
|
|
|
TEST_CASE("CellBuffer") {
|
|
|
|
|
|
|
|
const char sText[] = "Scintilla";
|
2019-05-04 18:14:48 +00:00
|
|
|
const Sci::Position sLength = static_cast<Sci::Position>(strlen(sText));
|
|
|
|
|
|
|
|
CellBuffer cb(true, false);
|
2015-06-07 21:19:26 +00:00
|
|
|
|
|
|
|
SECTION("InsertOneLine") {
|
|
|
|
bool startSequence = false;
|
2019-05-04 18:14:48 +00:00
|
|
|
const char *cpChange = cb.InsertString(0, sText, static_cast<int>(sLength), startSequence);
|
2015-06-07 21:19:26 +00:00
|
|
|
REQUIRE(startSequence);
|
|
|
|
REQUIRE(sLength == cb.Length());
|
|
|
|
REQUIRE(memcmp(cpChange, sText, sLength) == 0);
|
|
|
|
REQUIRE(1 == cb.Lines());
|
|
|
|
REQUIRE(0 == cb.LineStart(0));
|
|
|
|
REQUIRE(0 == cb.LineFromPosition(0));
|
|
|
|
REQUIRE(sLength == cb.LineStart(1));
|
2019-05-04 18:14:48 +00:00
|
|
|
REQUIRE(0 == cb.LineFromPosition(static_cast<int>(sLength)));
|
2015-06-07 21:19:26 +00:00
|
|
|
REQUIRE(cb.CanUndo());
|
|
|
|
REQUIRE(!cb.CanRedo());
|
|
|
|
}
|
|
|
|
|
|
|
|
SECTION("InsertTwoLines") {
|
|
|
|
const char sText2[] = "Two\nLines";
|
2019-05-04 18:14:48 +00:00
|
|
|
const Sci::Position sLength2 = static_cast<Sci::Position>(strlen(sText2));
|
2015-06-07 21:19:26 +00:00
|
|
|
bool startSequence = false;
|
2019-05-04 18:14:48 +00:00
|
|
|
const char *cpChange = cb.InsertString(0, sText2, static_cast<int>(sLength), startSequence);
|
2015-06-07 21:19:26 +00:00
|
|
|
REQUIRE(startSequence);
|
|
|
|
REQUIRE(sLength2 == cb.Length());
|
|
|
|
REQUIRE(memcmp(cpChange, sText2, sLength2) == 0);
|
|
|
|
REQUIRE(2 == cb.Lines());
|
|
|
|
REQUIRE(0 == cb.LineStart(0));
|
|
|
|
REQUIRE(0 == cb.LineFromPosition(0));
|
|
|
|
REQUIRE(4 == cb.LineStart(1));
|
|
|
|
REQUIRE(1 == cb.LineFromPosition(5));
|
|
|
|
REQUIRE(sLength2 == cb.LineStart(2));
|
2019-05-04 18:14:48 +00:00
|
|
|
REQUIRE(1 == cb.LineFromPosition(static_cast<int>(sLength)));
|
2015-06-07 21:19:26 +00:00
|
|
|
REQUIRE(cb.CanUndo());
|
|
|
|
REQUIRE(!cb.CanRedo());
|
|
|
|
}
|
|
|
|
|
|
|
|
SECTION("UndoOff") {
|
|
|
|
REQUIRE(cb.IsCollectingUndo());
|
|
|
|
cb.SetUndoCollection(false);
|
|
|
|
REQUIRE(!cb.IsCollectingUndo());
|
|
|
|
bool startSequence = false;
|
2019-05-04 18:14:48 +00:00
|
|
|
const char *cpChange = cb.InsertString(0, sText, static_cast<int>(sLength), startSequence);
|
2015-06-07 21:19:26 +00:00
|
|
|
REQUIRE(!startSequence);
|
|
|
|
REQUIRE(sLength == cb.Length());
|
|
|
|
REQUIRE(memcmp(cpChange, sText, sLength) == 0);
|
|
|
|
REQUIRE(!cb.CanUndo());
|
|
|
|
REQUIRE(!cb.CanRedo());
|
|
|
|
}
|
|
|
|
|
|
|
|
SECTION("UndoRedo") {
|
|
|
|
const char sTextDeleted[] = "ci";
|
|
|
|
const char sTextAfterDeletion[] = "Sntilla";
|
|
|
|
bool startSequence = false;
|
2019-05-04 18:14:48 +00:00
|
|
|
const char *cpChange = cb.InsertString(0, sText, static_cast<int>(sLength), startSequence);
|
2015-06-07 21:19:26 +00:00
|
|
|
REQUIRE(startSequence);
|
|
|
|
REQUIRE(sLength == cb.Length());
|
|
|
|
REQUIRE(memcmp(cpChange, sText, sLength) == 0);
|
|
|
|
REQUIRE(memcmp(cb.BufferPointer(), sText, sLength) == 0);
|
|
|
|
REQUIRE(cb.CanUndo());
|
|
|
|
REQUIRE(!cb.CanRedo());
|
|
|
|
const char *cpDeletion = cb.DeleteChars(1, 2, startSequence);
|
|
|
|
REQUIRE(startSequence);
|
|
|
|
REQUIRE(memcmp(cpDeletion, sTextDeleted, strlen(sTextDeleted)) == 0);
|
|
|
|
REQUIRE(memcmp(cb.BufferPointer(), sTextAfterDeletion, strlen(sTextAfterDeletion)) == 0);
|
|
|
|
REQUIRE(cb.CanUndo());
|
|
|
|
REQUIRE(!cb.CanRedo());
|
|
|
|
|
|
|
|
int steps = cb.StartUndo();
|
|
|
|
REQUIRE(steps == 1);
|
|
|
|
cb.PerformUndoStep();
|
|
|
|
REQUIRE(memcmp(cb.BufferPointer(), sText, sLength) == 0);
|
|
|
|
REQUIRE(cb.CanUndo());
|
|
|
|
REQUIRE(cb.CanRedo());
|
|
|
|
|
|
|
|
steps = cb.StartUndo();
|
|
|
|
REQUIRE(steps == 1);
|
|
|
|
cb.PerformUndoStep();
|
|
|
|
REQUIRE(cb.Length() == 0);
|
|
|
|
REQUIRE(!cb.CanUndo());
|
|
|
|
REQUIRE(cb.CanRedo());
|
|
|
|
|
|
|
|
steps = cb.StartRedo();
|
|
|
|
REQUIRE(steps == 1);
|
|
|
|
cb.PerformRedoStep();
|
|
|
|
REQUIRE(memcmp(cb.BufferPointer(), sText, sLength) == 0);
|
|
|
|
REQUIRE(cb.CanUndo());
|
|
|
|
REQUIRE(cb.CanRedo());
|
|
|
|
|
|
|
|
steps = cb.StartRedo();
|
|
|
|
REQUIRE(steps == 1);
|
|
|
|
cb.PerformRedoStep();
|
|
|
|
REQUIRE(memcmp(cb.BufferPointer(), sTextAfterDeletion, strlen(sTextAfterDeletion)) == 0);
|
|
|
|
REQUIRE(cb.CanUndo());
|
|
|
|
REQUIRE(!cb.CanRedo());
|
2019-05-04 18:14:48 +00:00
|
|
|
|
2015-06-07 21:19:26 +00:00
|
|
|
cb.DeleteUndoHistory();
|
|
|
|
REQUIRE(!cb.CanUndo());
|
|
|
|
REQUIRE(!cb.CanRedo());
|
|
|
|
}
|
|
|
|
|
|
|
|
SECTION("LineEndTypes") {
|
|
|
|
REQUIRE(cb.GetLineEndTypes() == 0);
|
|
|
|
cb.SetLineEndTypes(1);
|
|
|
|
REQUIRE(cb.GetLineEndTypes() == 1);
|
|
|
|
cb.SetLineEndTypes(0);
|
|
|
|
REQUIRE(cb.GetLineEndTypes() == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
SECTION("ReadOnly") {
|
|
|
|
REQUIRE(!cb.IsReadOnly());
|
|
|
|
cb.SetReadOnly(true);
|
|
|
|
REQUIRE(cb.IsReadOnly());
|
|
|
|
bool startSequence = false;
|
2019-05-04 18:14:48 +00:00
|
|
|
cb.InsertString(0, sText, static_cast<int>(sLength), startSequence);
|
2015-06-07 21:19:26 +00:00
|
|
|
REQUIRE(cb.Length() == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2019-05-04 18:14:48 +00:00
|
|
|
|
|
|
|
TEST_CASE("CharacterIndex") {
|
|
|
|
|
|
|
|
CellBuffer cb(true, false);
|
|
|
|
|
|
|
|
SECTION("Setup") {
|
|
|
|
REQUIRE(cb.LineCharacterIndex() == SC_LINECHARACTERINDEX_NONE);
|
|
|
|
REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF16) == 0);
|
|
|
|
REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF16) == 0);
|
|
|
|
cb.SetUTF8Substance(true);
|
|
|
|
|
|
|
|
cb.AllocateLineCharacterIndex(SC_LINECHARACTERINDEX_UTF16);
|
|
|
|
REQUIRE(cb.LineCharacterIndex() == SC_LINECHARACTERINDEX_UTF16);
|
|
|
|
|
|
|
|
REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF16) == 0);
|
|
|
|
REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF16) == 0);
|
|
|
|
|
|
|
|
cb.ReleaseLineCharacterIndex(SC_LINECHARACTERINDEX_UTF16);
|
|
|
|
REQUIRE(cb.LineCharacterIndex() == SC_LINECHARACTERINDEX_NONE);
|
|
|
|
}
|
|
|
|
|
|
|
|
SECTION("Insertion") {
|
|
|
|
cb.SetUTF8Substance(true);
|
|
|
|
|
|
|
|
cb.AllocateLineCharacterIndex(SC_LINECHARACTERINDEX_UTF16 | SC_LINECHARACTERINDEX_UTF32);
|
|
|
|
|
|
|
|
bool startSequence = false;
|
|
|
|
cb.InsertString(0, "a", 1, startSequence);
|
|
|
|
REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF16) == 0);
|
|
|
|
REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF16) == 1);
|
|
|
|
REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF32) == 0);
|
|
|
|
REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF32) == 1);
|
|
|
|
|
|
|
|
const char *hwair = "\xF0\x90\x8D\x88";
|
|
|
|
cb.InsertString(0, hwair, strlen(hwair), startSequence);
|
|
|
|
REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF16) == 0);
|
|
|
|
REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF16) == 3);
|
|
|
|
REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF32) == 0);
|
|
|
|
REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF32) == 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
SECTION("Deletion") {
|
|
|
|
cb.SetUTF8Substance(true);
|
|
|
|
|
|
|
|
cb.AllocateLineCharacterIndex(SC_LINECHARACTERINDEX_UTF16 | SC_LINECHARACTERINDEX_UTF32);
|
|
|
|
|
|
|
|
bool startSequence = false;
|
|
|
|
const char *hwair = "a\xF0\x90\x8D\x88z";
|
|
|
|
cb.InsertString(0, hwair, strlen(hwair), startSequence);
|
|
|
|
|
|
|
|
REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF16) == 0);
|
|
|
|
REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF16) == 4);
|
|
|
|
REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF32) == 0);
|
|
|
|
REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF32) == 3);
|
|
|
|
|
|
|
|
cb.DeleteChars(5, 1, startSequence);
|
|
|
|
|
|
|
|
REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF16) == 0);
|
|
|
|
REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF16) == 3);
|
|
|
|
REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF32) == 0);
|
|
|
|
REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF32) == 2);
|
|
|
|
|
|
|
|
cb.DeleteChars(1, 4, startSequence);
|
|
|
|
|
|
|
|
REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF16) == 0);
|
|
|
|
REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF16) == 1);
|
|
|
|
REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF32) == 0);
|
|
|
|
REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF32) == 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
SECTION("Insert Complex") {
|
|
|
|
cb.SetUTF8Substance(true);
|
|
|
|
cb.SetLineEndTypes(1);
|
|
|
|
cb.AllocateLineCharacterIndex(SC_LINECHARACTERINDEX_UTF16 | SC_LINECHARACTERINDEX_UTF32);
|
|
|
|
|
|
|
|
bool startSequence = false;
|
|
|
|
// 3 lines of text containing 8 bytes
|
|
|
|
const char *data = "a\n\xF0\x90\x8D\x88\nz";
|
|
|
|
cb.InsertString(0, data, strlen(data), startSequence);
|
|
|
|
|
|
|
|
REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF16) == 0);
|
|
|
|
REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF16) == 2);
|
|
|
|
REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF16) == 5);
|
|
|
|
REQUIRE(cb.IndexLineStart(3, SC_LINECHARACTERINDEX_UTF16) == 6);
|
|
|
|
|
|
|
|
REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF32) == 0);
|
|
|
|
REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF32) == 2);
|
|
|
|
REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF32) == 4);
|
|
|
|
REQUIRE(cb.IndexLineStart(3, SC_LINECHARACTERINDEX_UTF32) == 5);
|
|
|
|
|
|
|
|
// Insert a new line at end -> "a\n\xF0\x90\x8D\x88\nz\n" 4 lines
|
|
|
|
// Last line empty
|
|
|
|
cb.InsertString(strlen(data), "\n", 1, startSequence);
|
|
|
|
|
|
|
|
REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF16) == 0);
|
|
|
|
REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF16) == 2);
|
|
|
|
REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF16) == 5);
|
|
|
|
REQUIRE(cb.IndexLineStart(3, SC_LINECHARACTERINDEX_UTF16) == 7);
|
|
|
|
REQUIRE(cb.IndexLineStart(4, SC_LINECHARACTERINDEX_UTF16) == 7);
|
|
|
|
|
|
|
|
REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF32) == 0);
|
|
|
|
REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF32) == 2);
|
|
|
|
REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF32) == 4);
|
|
|
|
REQUIRE(cb.IndexLineStart(3, SC_LINECHARACTERINDEX_UTF32) == 6);
|
|
|
|
REQUIRE(cb.IndexLineStart(4, SC_LINECHARACTERINDEX_UTF32) == 6);
|
|
|
|
|
|
|
|
// Insert a new line before end -> "a\n\xF0\x90\x8D\x88\nz\n\n" 5 lines
|
|
|
|
cb.InsertString(strlen(data), "\n", 1, startSequence);
|
|
|
|
|
|
|
|
REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF16) == 0);
|
|
|
|
REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF16) == 2);
|
|
|
|
REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF16) == 5);
|
|
|
|
REQUIRE(cb.IndexLineStart(3, SC_LINECHARACTERINDEX_UTF16) == 7);
|
|
|
|
REQUIRE(cb.IndexLineStart(4, SC_LINECHARACTERINDEX_UTF16) == 8);
|
|
|
|
REQUIRE(cb.IndexLineStart(5, SC_LINECHARACTERINDEX_UTF16) == 8);
|
|
|
|
|
|
|
|
REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF32) == 0);
|
|
|
|
REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF32) == 2);
|
|
|
|
REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF32) == 4);
|
|
|
|
REQUIRE(cb.IndexLineStart(3, SC_LINECHARACTERINDEX_UTF32) == 6);
|
|
|
|
REQUIRE(cb.IndexLineStart(4, SC_LINECHARACTERINDEX_UTF32) == 7);
|
|
|
|
REQUIRE(cb.IndexLineStart(5, SC_LINECHARACTERINDEX_UTF32) == 7);
|
|
|
|
|
|
|
|
// Insert a valid 3-byte UTF-8 character at start ->
|
|
|
|
// "\xE2\x82\xACa\n\xF0\x90\x8D\x88\nz\n\n" 5 lines
|
|
|
|
|
|
|
|
const char *euro = "\xE2\x82\xAC";
|
|
|
|
cb.InsertString(0, euro, strlen(euro), startSequence);
|
|
|
|
|
|
|
|
REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF16) == 0);
|
|
|
|
REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF16) == 3);
|
|
|
|
REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF16) == 6);
|
|
|
|
REQUIRE(cb.IndexLineStart(3, SC_LINECHARACTERINDEX_UTF16) == 8);
|
|
|
|
REQUIRE(cb.IndexLineStart(4, SC_LINECHARACTERINDEX_UTF16) == 9);
|
|
|
|
REQUIRE(cb.IndexLineStart(5, SC_LINECHARACTERINDEX_UTF16) == 9);
|
|
|
|
|
|
|
|
REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF32) == 0);
|
|
|
|
REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF32) == 3);
|
|
|
|
REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF32) == 5);
|
|
|
|
REQUIRE(cb.IndexLineStart(3, SC_LINECHARACTERINDEX_UTF32) == 7);
|
|
|
|
REQUIRE(cb.IndexLineStart(4, SC_LINECHARACTERINDEX_UTF32) == 8);
|
|
|
|
REQUIRE(cb.IndexLineStart(5, SC_LINECHARACTERINDEX_UTF32) == 8);
|
|
|
|
|
|
|
|
// Insert a lone lead byte implying a 3 byte character at start of line 2 ->
|
|
|
|
// "\xE2\x82\xACa\n\EF\xF0\x90\x8D\x88\nz\n\n" 5 lines
|
|
|
|
// Should be treated as a single byte character
|
|
|
|
|
|
|
|
const char *lead = "\xEF";
|
|
|
|
cb.InsertString(5, lead, strlen(lead), startSequence);
|
|
|
|
|
|
|
|
REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF16) == 0);
|
|
|
|
REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF16) == 3);
|
|
|
|
REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF16) == 7);
|
|
|
|
REQUIRE(cb.IndexLineStart(3, SC_LINECHARACTERINDEX_UTF16) == 9);
|
|
|
|
|
|
|
|
REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF32) == 0);
|
|
|
|
REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF32) == 3);
|
|
|
|
REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF32) == 6);
|
|
|
|
REQUIRE(cb.IndexLineStart(3, SC_LINECHARACTERINDEX_UTF32) == 8);
|
|
|
|
|
|
|
|
// Insert an ASCII lead byte inside the 3-byte initial character ->
|
|
|
|
// "\xE2!\x82\xACa\n\EF\xF0\x90\x8D\x88\nz\n\n" 5 lines
|
|
|
|
// It should b treated as a single character and should cause the
|
|
|
|
// byte before and the 2 bytes after also be each treated as singles
|
|
|
|
// so 3 more characters on line 0.
|
|
|
|
|
|
|
|
const char *ascii = "!";
|
|
|
|
cb.InsertString(1, ascii, strlen(ascii), startSequence);
|
|
|
|
|
|
|
|
REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF16) == 0);
|
|
|
|
REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF16) == 6);
|
|
|
|
REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF16) == 10);
|
|
|
|
|
|
|
|
REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF32) == 0);
|
|
|
|
REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF32) == 6);
|
|
|
|
REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF32) == 9);
|
|
|
|
|
|
|
|
// Insert a NEL after the '!' to trigger the utf8 line end case ->
|
|
|
|
// "\xE2!\xC2\x85 \x82\xACa\n \EF\xF0\x90\x8D\x88\n z\n\n" 5 lines
|
|
|
|
|
|
|
|
const char *nel = "\xC2\x85";
|
|
|
|
cb.InsertString(2, nel, strlen(nel), startSequence);
|
|
|
|
|
|
|
|
REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF16) == 0);
|
|
|
|
REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF16) == 3);
|
|
|
|
REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF16) == 7);
|
|
|
|
REQUIRE(cb.IndexLineStart(3, SC_LINECHARACTERINDEX_UTF16) == 11);
|
|
|
|
|
|
|
|
REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF32) == 0);
|
|
|
|
REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF32) == 3);
|
|
|
|
REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF32) == 7);
|
|
|
|
REQUIRE(cb.IndexLineStart(3, SC_LINECHARACTERINDEX_UTF32) == 10);
|
|
|
|
}
|
|
|
|
|
|
|
|
SECTION("Delete Multiple lines") {
|
|
|
|
cb.SetUTF8Substance(true);
|
|
|
|
cb.AllocateLineCharacterIndex(SC_LINECHARACTERINDEX_UTF16 | SC_LINECHARACTERINDEX_UTF32);
|
|
|
|
|
|
|
|
bool startSequence = false;
|
|
|
|
// 3 lines of text containing 8 bytes
|
|
|
|
const char *data = "a\n\xF0\x90\x8D\x88\nz\nc";
|
|
|
|
cb.InsertString(0, data, strlen(data), startSequence);
|
|
|
|
|
|
|
|
// Delete first 2 new lines -> "az\nc"
|
|
|
|
cb.DeleteChars(1, strlen(data) - 4, startSequence);
|
|
|
|
|
|
|
|
REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF16) == 0);
|
|
|
|
REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF16) == 3);
|
|
|
|
REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF16) == 4);
|
|
|
|
|
|
|
|
REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF32) == 0);
|
|
|
|
REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF32) == 3);
|
|
|
|
REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF32) == 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
SECTION("Delete Complex") {
|
|
|
|
cb.SetUTF8Substance(true);
|
|
|
|
cb.AllocateLineCharacterIndex(SC_LINECHARACTERINDEX_UTF16 | SC_LINECHARACTERINDEX_UTF32);
|
|
|
|
|
|
|
|
bool startSequence = false;
|
|
|
|
// 3 lines of text containing 8 bytes
|
|
|
|
const char *data = "a\n\xF0\x90\x8D\x88\nz";
|
|
|
|
cb.InsertString(0, data, strlen(data), startSequence);
|
|
|
|
|
|
|
|
// Delete lead byte from character on line 1 ->
|
|
|
|
// "a\n\x90\x8D\x88\nz"
|
|
|
|
// line 1 becomes 4 single byte characters
|
|
|
|
cb.DeleteChars(2, 1, startSequence);
|
|
|
|
|
|
|
|
REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF16) == 0);
|
|
|
|
REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF16) == 2);
|
|
|
|
REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF16) == 6);
|
|
|
|
REQUIRE(cb.IndexLineStart(3, SC_LINECHARACTERINDEX_UTF16) == 7);
|
|
|
|
|
|
|
|
REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF32) == 0);
|
|
|
|
REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF32) == 2);
|
|
|
|
REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF32) == 6);
|
|
|
|
REQUIRE(cb.IndexLineStart(3, SC_LINECHARACTERINDEX_UTF32) == 7);
|
|
|
|
|
|
|
|
// Delete first new line ->
|
|
|
|
// "a\x90\x8D\x88\nz"
|
|
|
|
// Only 2 lines with line 0 containing 5 single byte characters
|
|
|
|
cb.DeleteChars(1, 1, startSequence);
|
|
|
|
|
|
|
|
REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF16) == 0);
|
|
|
|
REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF16) == 5);
|
|
|
|
REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF16) == 6);
|
|
|
|
|
|
|
|
REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF32) == 0);
|
|
|
|
REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF32) == 5);
|
|
|
|
REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF32) == 6);
|
|
|
|
|
|
|
|
// Restore lead byte from character on line 0 making a 4-byte character ->
|
|
|
|
// "a\xF0\x90\x8D\x88\nz"
|
|
|
|
|
|
|
|
const char *lead4 = "\xF0";
|
|
|
|
cb.InsertString(1, lead4, strlen(lead4), startSequence);
|
|
|
|
|
|
|
|
REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF16) == 0);
|
|
|
|
REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF16) == 4);
|
|
|
|
REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF16) == 5);
|
|
|
|
|
|
|
|
REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF32) == 0);
|
|
|
|
REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF32) == 3);
|
|
|
|
REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF32) == 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
SECTION("Insert separates new line bytes") {
|
|
|
|
cb.SetUTF8Substance(true);
|
|
|
|
cb.AllocateLineCharacterIndex(SC_LINECHARACTERINDEX_UTF16 | SC_LINECHARACTERINDEX_UTF32);
|
|
|
|
|
|
|
|
bool startSequence = false;
|
|
|
|
// 2 lines of text containing 4 bytes
|
|
|
|
const char *data = "a\r\nb";
|
|
|
|
cb.InsertString(0, data, strlen(data), startSequence);
|
|
|
|
|
|
|
|
// 3 lines of text containing 5 bytes ->
|
|
|
|
// "a\r!\nb"
|
|
|
|
const char *ascii = "!";
|
|
|
|
cb.InsertString(2, ascii, strlen(ascii), startSequence);
|
|
|
|
|
|
|
|
REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF16) == 0);
|
|
|
|
REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF16) == 2);
|
|
|
|
REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF16) == 4);
|
|
|
|
REQUIRE(cb.IndexLineStart(3, SC_LINECHARACTERINDEX_UTF16) == 5);
|
|
|
|
}
|
|
|
|
}
|