1 // Unit Tests for Scintilla internal data structures 2 3 #include <cstddef> 4 #include <cstring> 5 #include <stdexcept> 6 #include <string_view> 7 #include <vector> 8 #include <algorithm> 9 #include <memory> 10 11 #include "Platform.h" 12 13 #include "Scintilla.h" 14 #include "Position.h" 15 #include "SplitVector.h" 16 #include "Partitioning.h" 17 #include "RunStyles.h" 18 #include "CellBuffer.h" 19 20 #include "catch.hpp" 21 22 using namespace Scintilla; 23 24 // Test CellBuffer. 25 26 TEST_CASE("CellBuffer") { 27 28 const char sText[] = "Scintilla"; 29 const Sci::Position sLength = static_cast<Sci::Position>(strlen(sText)); 30 31 CellBuffer cb(true, false); 32 33 SECTION("InsertOneLine") { 34 bool startSequence = false; 35 const char *cpChange = cb.InsertString(0, sText, static_cast<int>(sLength), startSequence); 36 REQUIRE(startSequence); 37 REQUIRE(sLength == cb.Length()); 38 REQUIRE(memcmp(cpChange, sText, sLength) == 0); 39 REQUIRE(1 == cb.Lines()); 40 REQUIRE(0 == cb.LineStart(0)); 41 REQUIRE(0 == cb.LineFromPosition(0)); 42 REQUIRE(sLength == cb.LineStart(1)); 43 REQUIRE(0 == cb.LineFromPosition(static_cast<int>(sLength))); 44 REQUIRE(cb.CanUndo()); 45 REQUIRE(!cb.CanRedo()); 46 } 47 48 SECTION("InsertTwoLines") { 49 const char sText2[] = "Two\nLines"; 50 const Sci::Position sLength2 = static_cast<Sci::Position>(strlen(sText2)); 51 bool startSequence = false; 52 const char *cpChange = cb.InsertString(0, sText2, static_cast<int>(sLength), startSequence); 53 REQUIRE(startSequence); 54 REQUIRE(sLength2 == cb.Length()); 55 REQUIRE(memcmp(cpChange, sText2, sLength2) == 0); 56 REQUIRE(2 == cb.Lines()); 57 REQUIRE(0 == cb.LineStart(0)); 58 REQUIRE(0 == cb.LineFromPosition(0)); 59 REQUIRE(4 == cb.LineStart(1)); 60 REQUIRE(1 == cb.LineFromPosition(5)); 61 REQUIRE(sLength2 == cb.LineStart(2)); 62 REQUIRE(1 == cb.LineFromPosition(static_cast<int>(sLength))); 63 REQUIRE(cb.CanUndo()); 64 REQUIRE(!cb.CanRedo()); 65 } 66 67 SECTION("UndoOff") { 68 REQUIRE(cb.IsCollectingUndo()); 69 cb.SetUndoCollection(false); 70 REQUIRE(!cb.IsCollectingUndo()); 71 bool startSequence = false; 72 const char *cpChange = cb.InsertString(0, sText, static_cast<int>(sLength), startSequence); 73 REQUIRE(!startSequence); 74 REQUIRE(sLength == cb.Length()); 75 REQUIRE(memcmp(cpChange, sText, sLength) == 0); 76 REQUIRE(!cb.CanUndo()); 77 REQUIRE(!cb.CanRedo()); 78 } 79 80 SECTION("UndoRedo") { 81 const char sTextDeleted[] = "ci"; 82 const char sTextAfterDeletion[] = "Sntilla"; 83 bool startSequence = false; 84 const char *cpChange = cb.InsertString(0, sText, static_cast<int>(sLength), startSequence); 85 REQUIRE(startSequence); 86 REQUIRE(sLength == cb.Length()); 87 REQUIRE(memcmp(cpChange, sText, sLength) == 0); 88 REQUIRE(memcmp(cb.BufferPointer(), sText, sLength) == 0); 89 REQUIRE(cb.CanUndo()); 90 REQUIRE(!cb.CanRedo()); 91 const char *cpDeletion = cb.DeleteChars(1, 2, startSequence); 92 REQUIRE(startSequence); 93 REQUIRE(memcmp(cpDeletion, sTextDeleted, strlen(sTextDeleted)) == 0); 94 REQUIRE(memcmp(cb.BufferPointer(), sTextAfterDeletion, strlen(sTextAfterDeletion)) == 0); 95 REQUIRE(cb.CanUndo()); 96 REQUIRE(!cb.CanRedo()); 97 98 int steps = cb.StartUndo(); 99 REQUIRE(steps == 1); 100 cb.PerformUndoStep(); 101 REQUIRE(memcmp(cb.BufferPointer(), sText, sLength) == 0); 102 REQUIRE(cb.CanUndo()); 103 REQUIRE(cb.CanRedo()); 104 105 steps = cb.StartUndo(); 106 REQUIRE(steps == 1); 107 cb.PerformUndoStep(); 108 REQUIRE(cb.Length() == 0); 109 REQUIRE(!cb.CanUndo()); 110 REQUIRE(cb.CanRedo()); 111 112 steps = cb.StartRedo(); 113 REQUIRE(steps == 1); 114 cb.PerformRedoStep(); 115 REQUIRE(memcmp(cb.BufferPointer(), sText, sLength) == 0); 116 REQUIRE(cb.CanUndo()); 117 REQUIRE(cb.CanRedo()); 118 119 steps = cb.StartRedo(); 120 REQUIRE(steps == 1); 121 cb.PerformRedoStep(); 122 REQUIRE(memcmp(cb.BufferPointer(), sTextAfterDeletion, strlen(sTextAfterDeletion)) == 0); 123 REQUIRE(cb.CanUndo()); 124 REQUIRE(!cb.CanRedo()); 125 126 cb.DeleteUndoHistory(); 127 REQUIRE(!cb.CanUndo()); 128 REQUIRE(!cb.CanRedo()); 129 } 130 131 SECTION("LineEndTypes") { 132 REQUIRE(cb.GetLineEndTypes() == 0); 133 cb.SetLineEndTypes(1); 134 REQUIRE(cb.GetLineEndTypes() == 1); 135 cb.SetLineEndTypes(0); 136 REQUIRE(cb.GetLineEndTypes() == 0); 137 } 138 139 SECTION("ReadOnly") { 140 REQUIRE(!cb.IsReadOnly()); 141 cb.SetReadOnly(true); 142 REQUIRE(cb.IsReadOnly()); 143 bool startSequence = false; 144 cb.InsertString(0, sText, static_cast<int>(sLength), startSequence); 145 REQUIRE(cb.Length() == 0); 146 } 147 148 } 149 150 TEST_CASE("CharacterIndex") { 151 152 CellBuffer cb(true, false); 153 154 SECTION("Setup") { 155 REQUIRE(cb.LineCharacterIndex() == SC_LINECHARACTERINDEX_NONE); 156 REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF16) == 0); 157 REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF16) == 0); 158 cb.SetUTF8Substance(true); 159 160 cb.AllocateLineCharacterIndex(SC_LINECHARACTERINDEX_UTF16); 161 REQUIRE(cb.LineCharacterIndex() == SC_LINECHARACTERINDEX_UTF16); 162 163 REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF16) == 0); 164 REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF16) == 0); 165 166 cb.ReleaseLineCharacterIndex(SC_LINECHARACTERINDEX_UTF16); 167 REQUIRE(cb.LineCharacterIndex() == SC_LINECHARACTERINDEX_NONE); 168 } 169 170 SECTION("Insertion") { 171 cb.SetUTF8Substance(true); 172 173 cb.AllocateLineCharacterIndex(SC_LINECHARACTERINDEX_UTF16 | SC_LINECHARACTERINDEX_UTF32); 174 175 bool startSequence = false; 176 cb.InsertString(0, "a", 1, startSequence); 177 REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF16) == 0); 178 REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF16) == 1); 179 REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF32) == 0); 180 REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF32) == 1); 181 182 const char *hwair = "\xF0\x90\x8D\x88"; 183 cb.InsertString(0, hwair, strlen(hwair), startSequence); 184 REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF16) == 0); 185 REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF16) == 3); 186 REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF32) == 0); 187 REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF32) == 2); 188 } 189 190 SECTION("Deletion") { 191 cb.SetUTF8Substance(true); 192 193 cb.AllocateLineCharacterIndex(SC_LINECHARACTERINDEX_UTF16 | SC_LINECHARACTERINDEX_UTF32); 194 195 bool startSequence = false; 196 const char *hwair = "a\xF0\x90\x8D\x88z"; 197 cb.InsertString(0, hwair, strlen(hwair), startSequence); 198 199 REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF16) == 0); 200 REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF16) == 4); 201 REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF32) == 0); 202 REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF32) == 3); 203 204 cb.DeleteChars(5, 1, startSequence); 205 206 REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF16) == 0); 207 REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF16) == 3); 208 REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF32) == 0); 209 REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF32) == 2); 210 211 cb.DeleteChars(1, 4, startSequence); 212 213 REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF16) == 0); 214 REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF16) == 1); 215 REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF32) == 0); 216 REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF32) == 1); 217 } 218 219 SECTION("Insert Complex") { 220 cb.SetUTF8Substance(true); 221 cb.SetLineEndTypes(1); 222 cb.AllocateLineCharacterIndex(SC_LINECHARACTERINDEX_UTF16 | SC_LINECHARACTERINDEX_UTF32); 223 224 bool startSequence = false; 225 // 3 lines of text containing 8 bytes 226 const char *data = "a\n\xF0\x90\x8D\x88\nz"; 227 cb.InsertString(0, data, strlen(data), startSequence); 228 229 REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF16) == 0); 230 REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF16) == 2); 231 REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF16) == 5); 232 REQUIRE(cb.IndexLineStart(3, SC_LINECHARACTERINDEX_UTF16) == 6); 233 234 REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF32) == 0); 235 REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF32) == 2); 236 REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF32) == 4); 237 REQUIRE(cb.IndexLineStart(3, SC_LINECHARACTERINDEX_UTF32) == 5); 238 239 // Insert a new line at end -> "a\n\xF0\x90\x8D\x88\nz\n" 4 lines 240 // Last line empty 241 cb.InsertString(strlen(data), "\n", 1, startSequence); 242 243 REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF16) == 0); 244 REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF16) == 2); 245 REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF16) == 5); 246 REQUIRE(cb.IndexLineStart(3, SC_LINECHARACTERINDEX_UTF16) == 7); 247 REQUIRE(cb.IndexLineStart(4, SC_LINECHARACTERINDEX_UTF16) == 7); 248 249 REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF32) == 0); 250 REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF32) == 2); 251 REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF32) == 4); 252 REQUIRE(cb.IndexLineStart(3, SC_LINECHARACTERINDEX_UTF32) == 6); 253 REQUIRE(cb.IndexLineStart(4, SC_LINECHARACTERINDEX_UTF32) == 6); 254 255 // Insert a new line before end -> "a\n\xF0\x90\x8D\x88\nz\n\n" 5 lines 256 cb.InsertString(strlen(data), "\n", 1, startSequence); 257 258 REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF16) == 0); 259 REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF16) == 2); 260 REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF16) == 5); 261 REQUIRE(cb.IndexLineStart(3, SC_LINECHARACTERINDEX_UTF16) == 7); 262 REQUIRE(cb.IndexLineStart(4, SC_LINECHARACTERINDEX_UTF16) == 8); 263 REQUIRE(cb.IndexLineStart(5, SC_LINECHARACTERINDEX_UTF16) == 8); 264 265 REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF32) == 0); 266 REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF32) == 2); 267 REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF32) == 4); 268 REQUIRE(cb.IndexLineStart(3, SC_LINECHARACTERINDEX_UTF32) == 6); 269 REQUIRE(cb.IndexLineStart(4, SC_LINECHARACTERINDEX_UTF32) == 7); 270 REQUIRE(cb.IndexLineStart(5, SC_LINECHARACTERINDEX_UTF32) == 7); 271 272 // Insert a valid 3-byte UTF-8 character at start -> 273 // "\xE2\x82\xACa\n\xF0\x90\x8D\x88\nz\n\n" 5 lines 274 275 const char *euro = "\xE2\x82\xAC"; 276 cb.InsertString(0, euro, strlen(euro), startSequence); 277 278 REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF16) == 0); 279 REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF16) == 3); 280 REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF16) == 6); 281 REQUIRE(cb.IndexLineStart(3, SC_LINECHARACTERINDEX_UTF16) == 8); 282 REQUIRE(cb.IndexLineStart(4, SC_LINECHARACTERINDEX_UTF16) == 9); 283 REQUIRE(cb.IndexLineStart(5, SC_LINECHARACTERINDEX_UTF16) == 9); 284 285 REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF32) == 0); 286 REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF32) == 3); 287 REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF32) == 5); 288 REQUIRE(cb.IndexLineStart(3, SC_LINECHARACTERINDEX_UTF32) == 7); 289 REQUIRE(cb.IndexLineStart(4, SC_LINECHARACTERINDEX_UTF32) == 8); 290 REQUIRE(cb.IndexLineStart(5, SC_LINECHARACTERINDEX_UTF32) == 8); 291 292 // Insert a lone lead byte implying a 3 byte character at start of line 2 -> 293 // "\xE2\x82\xACa\n\EF\xF0\x90\x8D\x88\nz\n\n" 5 lines 294 // Should be treated as a single byte character 295 296 const char *lead = "\xEF"; 297 cb.InsertString(5, lead, strlen(lead), startSequence); 298 299 REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF16) == 0); 300 REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF16) == 3); 301 REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF16) == 7); 302 REQUIRE(cb.IndexLineStart(3, SC_LINECHARACTERINDEX_UTF16) == 9); 303 304 REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF32) == 0); 305 REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF32) == 3); 306 REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF32) == 6); 307 REQUIRE(cb.IndexLineStart(3, SC_LINECHARACTERINDEX_UTF32) == 8); 308 309 // Insert an ASCII lead byte inside the 3-byte initial character -> 310 // "\xE2!\x82\xACa\n\EF\xF0\x90\x8D\x88\nz\n\n" 5 lines 311 // It should b treated as a single character and should cause the 312 // byte before and the 2 bytes after also be each treated as singles 313 // so 3 more characters on line 0. 314 315 const char *ascii = "!"; 316 cb.InsertString(1, ascii, strlen(ascii), startSequence); 317 318 REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF16) == 0); 319 REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF16) == 6); 320 REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF16) == 10); 321 322 REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF32) == 0); 323 REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF32) == 6); 324 REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF32) == 9); 325 326 // Insert a NEL after the '!' to trigger the utf8 line end case -> 327 // "\xE2!\xC2\x85 \x82\xACa\n \EF\xF0\x90\x8D\x88\n z\n\n" 5 lines 328 329 const char *nel = "\xC2\x85"; 330 cb.InsertString(2, nel, strlen(nel), startSequence); 331 332 REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF16) == 0); 333 REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF16) == 3); 334 REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF16) == 7); 335 REQUIRE(cb.IndexLineStart(3, SC_LINECHARACTERINDEX_UTF16) == 11); 336 337 REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF32) == 0); 338 REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF32) == 3); 339 REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF32) == 7); 340 REQUIRE(cb.IndexLineStart(3, SC_LINECHARACTERINDEX_UTF32) == 10); 341 } 342 343 SECTION("Delete Multiple lines") { 344 cb.SetUTF8Substance(true); 345 cb.AllocateLineCharacterIndex(SC_LINECHARACTERINDEX_UTF16 | SC_LINECHARACTERINDEX_UTF32); 346 347 bool startSequence = false; 348 // 3 lines of text containing 8 bytes 349 const char *data = "a\n\xF0\x90\x8D\x88\nz\nc"; 350 cb.InsertString(0, data, strlen(data), startSequence); 351 352 // Delete first 2 new lines -> "az\nc" 353 cb.DeleteChars(1, strlen(data) - 4, startSequence); 354 355 REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF16) == 0); 356 REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF16) == 3); 357 REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF16) == 4); 358 359 REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF32) == 0); 360 REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF32) == 3); 361 REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF32) == 4); 362 } 363 364 SECTION("Delete Complex") { 365 cb.SetUTF8Substance(true); 366 cb.AllocateLineCharacterIndex(SC_LINECHARACTERINDEX_UTF16 | SC_LINECHARACTERINDEX_UTF32); 367 368 bool startSequence = false; 369 // 3 lines of text containing 8 bytes 370 const char *data = "a\n\xF0\x90\x8D\x88\nz"; 371 cb.InsertString(0, data, strlen(data), startSequence); 372 373 // Delete lead byte from character on line 1 -> 374 // "a\n\x90\x8D\x88\nz" 375 // line 1 becomes 4 single byte characters 376 cb.DeleteChars(2, 1, startSequence); 377 378 REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF16) == 0); 379 REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF16) == 2); 380 REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF16) == 6); 381 REQUIRE(cb.IndexLineStart(3, SC_LINECHARACTERINDEX_UTF16) == 7); 382 383 REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF32) == 0); 384 REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF32) == 2); 385 REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF32) == 6); 386 REQUIRE(cb.IndexLineStart(3, SC_LINECHARACTERINDEX_UTF32) == 7); 387 388 // Delete first new line -> 389 // "a\x90\x8D\x88\nz" 390 // Only 2 lines with line 0 containing 5 single byte characters 391 cb.DeleteChars(1, 1, startSequence); 392 393 REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF16) == 0); 394 REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF16) == 5); 395 REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF16) == 6); 396 397 REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF32) == 0); 398 REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF32) == 5); 399 REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF32) == 6); 400 401 // Restore lead byte from character on line 0 making a 4-byte character -> 402 // "a\xF0\x90\x8D\x88\nz" 403 404 const char *lead4 = "\xF0"; 405 cb.InsertString(1, lead4, strlen(lead4), startSequence); 406 407 REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF16) == 0); 408 REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF16) == 4); 409 REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF16) == 5); 410 411 REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF32) == 0); 412 REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF32) == 3); 413 REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF32) == 4); 414 } 415 416 SECTION("Insert separates new line bytes") { 417 cb.SetUTF8Substance(true); 418 cb.AllocateLineCharacterIndex(SC_LINECHARACTERINDEX_UTF16 | SC_LINECHARACTERINDEX_UTF32); 419 420 bool startSequence = false; 421 // 2 lines of text containing 4 bytes 422 const char *data = "a\r\nb"; 423 cb.InsertString(0, data, strlen(data), startSequence); 424 425 // 3 lines of text containing 5 bytes -> 426 // "a\r!\nb" 427 const char *ascii = "!"; 428 cb.InsertString(2, ascii, strlen(ascii), startSequence); 429 430 REQUIRE(cb.IndexLineStart(0, SC_LINECHARACTERINDEX_UTF16) == 0); 431 REQUIRE(cb.IndexLineStart(1, SC_LINECHARACTERINDEX_UTF16) == 2); 432 REQUIRE(cb.IndexLineStart(2, SC_LINECHARACTERINDEX_UTF16) == 4); 433 REQUIRE(cb.IndexLineStart(3, SC_LINECHARACTERINDEX_UTF16) == 5); 434 } 435 } 436