1023 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			1023 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| 
								 | 
							
								/** @file testDocument.cxx
							 | 
						||
| 
								 | 
							
								 ** Unit Tests for Scintilla internal data structures
							 | 
						||
| 
								 | 
							
								 **/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include <cstddef>
							 | 
						||
| 
								 | 
							
								#include <cstring>
							 | 
						||
| 
								 | 
							
								#include <stdexcept>
							 | 
						||
| 
								 | 
							
								#include <string_view>
							 | 
						||
| 
								 | 
							
								#include <vector>
							 | 
						||
| 
								 | 
							
								#include <set>
							 | 
						||
| 
								 | 
							
								#include <optional>
							 | 
						||
| 
								 | 
							
								#include <algorithm>
							 | 
						||
| 
								 | 
							
								#include <memory>
							 | 
						||
| 
								 | 
							
								#include <iostream>
							 | 
						||
| 
								 | 
							
								#include <fstream>
							 | 
						||
| 
								 | 
							
								#include <iomanip>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include "ScintillaTypes.h"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include "ILoader.h"
							 | 
						||
| 
								 | 
							
								#include "ILexer.h"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include "Debugging.h"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include "CharacterCategoryMap.h"
							 | 
						||
| 
								 | 
							
								#include "Position.h"
							 | 
						||
| 
								 | 
							
								#include "SplitVector.h"
							 | 
						||
| 
								 | 
							
								#include "Partitioning.h"
							 | 
						||
| 
								 | 
							
								#include "RunStyles.h"
							 | 
						||
| 
								 | 
							
								#include "CellBuffer.h"
							 | 
						||
| 
								 | 
							
								#include "CharClassify.h"
							 | 
						||
| 
								 | 
							
								#include "Decoration.h"
							 | 
						||
| 
								 | 
							
								#include "CaseFolder.h"
							 | 
						||
| 
								 | 
							
								#include "Document.h"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include "catch.hpp"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								using namespace Scintilla;
							 | 
						||
| 
								 | 
							
								using namespace Scintilla::Internal;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// set global locale to pass std::regex related tests
							 | 
						||
| 
								 | 
							
								// see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63776
							 | 
						||
| 
								 | 
							
								struct GlobalLocaleInitializer {
							 | 
						||
| 
								 | 
							
									GlobalLocaleInitializer() {
							 | 
						||
| 
								 | 
							
										try {
							 | 
						||
| 
								 | 
							
											std::locale::global(std::locale("en_US.UTF-8"));
							 | 
						||
| 
								 | 
							
										} catch (...) {}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								} globalLocaleInitializer;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Test Document.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								struct Folding {
							 | 
						||
| 
								 | 
							
									int from;
							 | 
						||
| 
								 | 
							
									int to;
							 | 
						||
| 
								 | 
							
									int length;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Table of case folding for non-ASCII bytes in Windows Latin code page 1252
							 | 
						||
| 
								 | 
							
								const Folding foldings1252[] = {
							 | 
						||
| 
								 | 
							
									{0x8a, 0x9a, 0x01},
							 | 
						||
| 
								 | 
							
									{0x8c, 0x9c, 0x01},
							 | 
						||
| 
								 | 
							
									{0x8e, 0x9e, 0x01},
							 | 
						||
| 
								 | 
							
									{0x9f, 0xff, 0x01},
							 | 
						||
| 
								 | 
							
									{0xc0, 0xe0, 0x17},
							 | 
						||
| 
								 | 
							
									{0xd8, 0xf8, 0x07},
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Table of case folding for non-ASCII bytes in Windows Russian code page 1251
							 | 
						||
| 
								 | 
							
								const Folding foldings1251[] = {
							 | 
						||
| 
								 | 
							
									{0x80, 0x90, 0x01},
							 | 
						||
| 
								 | 
							
									{0x81, 0x83, 0x01},
							 | 
						||
| 
								 | 
							
									{0x8a, 0x9a, 0x01},
							 | 
						||
| 
								 | 
							
									{0x8c, 0x9c, 0x04},
							 | 
						||
| 
								 | 
							
									{0xa1, 0xa2, 0x01},
							 | 
						||
| 
								 | 
							
									{0xa3, 0xbc, 0x01},
							 | 
						||
| 
								 | 
							
									{0xa5, 0xb4, 0x01},
							 | 
						||
| 
								 | 
							
									{0xa8, 0xb8, 0x01},
							 | 
						||
| 
								 | 
							
									{0xaa, 0xba, 0x01},
							 | 
						||
| 
								 | 
							
									{0xaf, 0xbf, 0x01},
							 | 
						||
| 
								 | 
							
									{0xb2, 0xb3, 0x01},
							 | 
						||
| 
								 | 
							
									{0xbd, 0xbe, 0x01},
							 | 
						||
| 
								 | 
							
									{0xc0, 0xe0, 0x20},
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								std::string ReadFile(const std::string &path) {
							 | 
						||
| 
								 | 
							
									std::ifstream ifs(path, std::ios::binary);
							 | 
						||
| 
								 | 
							
									std::string content((std::istreambuf_iterator<char>(ifs)),
							 | 
						||
| 
								 | 
							
										(std::istreambuf_iterator<char>()));
							 | 
						||
| 
								 | 
							
									return content;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								struct Match {
							 | 
						||
| 
								 | 
							
									Sci::Position location = 0;
							 | 
						||
| 
								 | 
							
									Sci::Position length = 0;
							 | 
						||
| 
								 | 
							
									constexpr Match() = default;
							 | 
						||
| 
								 | 
							
									constexpr Match(Sci::Position location_, Sci::Position length_=0) : location(location_), length(length_) {
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									constexpr bool operator==(const Match &other) const {
							 | 
						||
| 
								 | 
							
										return location == other.location && length == other.length;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								std::ostream &operator << (std::ostream &os, Match const &value) {
							 | 
						||
| 
								 | 
							
									os << value.location << "," << value.length;
							 | 
						||
| 
								 | 
							
									return os;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								struct DocPlus {
							 | 
						||
| 
								 | 
							
									Document document;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									DocPlus(std::string_view svInitial, int codePage) : document(DocumentOption::Default) {
							 | 
						||
| 
								 | 
							
										SetCodePage(codePage);
							 | 
						||
| 
								 | 
							
										document.InsertString(0, svInitial);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									void SetCodePage(int codePage) {
							 | 
						||
| 
								 | 
							
										document.SetDBCSCodePage(codePage);
							 | 
						||
| 
								 | 
							
										if (codePage == CpUtf8) {
							 | 
						||
| 
								 | 
							
											document.SetCaseFolder(std::make_unique<CaseFolderUnicode>());
							 | 
						||
| 
								 | 
							
										} else {
							 | 
						||
| 
								 | 
							
											// This case folder will not handle many DBCS cases. Scintilla uses platform-specific code for DBCS
							 | 
						||
| 
								 | 
							
											// case folding which can not easily be inserted in platform-independent tests.
							 | 
						||
| 
								 | 
							
											std::unique_ptr<CaseFolderTable> pcft = std::make_unique<CaseFolderTable>();
							 | 
						||
| 
								 | 
							
											document.SetCaseFolder(std::move(pcft));
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									void SetSBCSFoldings(const Folding *foldings, size_t length) {
							 | 
						||
| 
								 | 
							
										std::unique_ptr<CaseFolderTable> pcft = std::make_unique<CaseFolderTable>();
							 | 
						||
| 
								 | 
							
										for (size_t block = 0; block < length; block++) {
							 | 
						||
| 
								 | 
							
											for (int fold = 0; fold < foldings[block].length; fold++) {
							 | 
						||
| 
								 | 
							
												pcft->SetTranslation(foldings[block].from + fold, foldings[block].to + fold);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										document.SetCaseFolder(std::move(pcft));
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									Sci::Position FindNeedle(std::string_view needle, FindOption options, Sci::Position *length) {
							 | 
						||
| 
								 | 
							
										assert(*length == static_cast<Sci::Position>(needle.length()));
							 | 
						||
| 
								 | 
							
										return document.FindText(0, document.Length(), needle.data(), options, length);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									Sci::Position FindNeedleReverse(std::string_view needle, FindOption options, Sci::Position *length) {
							 | 
						||
| 
								 | 
							
										assert(*length == static_cast<Sci::Position>(needle.length()));
							 | 
						||
| 
								 | 
							
										return document.FindText(document.Length(), 0, needle.data(), options, length);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									Match FindString(Sci::Position minPos, Sci::Position maxPos, std::string_view needle, FindOption flags) {
							 | 
						||
| 
								 | 
							
										Sci::Position lengthFinding = needle.length();
							 | 
						||
| 
								 | 
							
										const Sci::Position location = document.FindText(minPos, maxPos, needle.data(), flags, &lengthFinding);
							 | 
						||
| 
								 | 
							
										return { location, lengthFinding };
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									std::string Substitute(std::string_view substituteText) {
							 | 
						||
| 
								 | 
							
										Sci::Position lengthsubstitute = substituteText.length();
							 | 
						||
| 
								 | 
							
										std::string substituted = document.SubstituteByPosition(substituteText.data(), &lengthsubstitute);
							 | 
						||
| 
								 | 
							
										assert(lengthsubstitute == static_cast<Sci::Position>(substituted.length()));
							 | 
						||
| 
								 | 
							
										return substituted;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									void MoveGap(Sci::Position gapNew) {
							 | 
						||
| 
								 | 
							
										// Move gap to gapNew by inserting
							 | 
						||
| 
								 | 
							
										document.InsertString(gapNew, "!", 1);
							 | 
						||
| 
								 | 
							
										// Remove insertion
							 | 
						||
| 
								 | 
							
										document.DeleteChars(gapNew, 1);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									[[nodiscard]] std::string Contents() const {
							 | 
						||
| 
								 | 
							
										const Sci::Position length = document.Length();
							 | 
						||
| 
								 | 
							
										std::string contents(length, 0);
							 | 
						||
| 
								 | 
							
										document.GetCharRange(contents.data(), 0, length);
							 | 
						||
| 
								 | 
							
										return contents;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void TimeTrace(std::string_view sv, const Catch::Timer &tikka) {
							 | 
						||
| 
								 | 
							
									std::cout << sv << std::setw(5) << tikka.getElapsedMilliseconds() << " milliseconds" << std::endl;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								TEST_CASE("Document") {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									constexpr std::string_view sText = "Scintilla";
							 | 
						||
| 
								 | 
							
									constexpr Sci::Position sLength = sText.length();
							 | 
						||
| 
								 | 
							
									constexpr FindOption rePosix = FindOption::RegExp | FindOption::Posix;
							 | 
						||
| 
								 | 
							
									constexpr FindOption reCxx11 = FindOption::RegExp | FindOption::Cxx11RegEx;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									SECTION("InsertOneLine") {
							 | 
						||
| 
								 | 
							
										DocPlus doc("", 0);
							 | 
						||
| 
								 | 
							
										const Sci::Position length = doc.document.InsertString(0, sText);
							 | 
						||
| 
								 | 
							
										REQUIRE(sLength == doc.document.Length());
							 | 
						||
| 
								 | 
							
										REQUIRE(length == sLength);
							 | 
						||
| 
								 | 
							
										REQUIRE(1 == doc.document.LinesTotal());
							 | 
						||
| 
								 | 
							
										REQUIRE(0 == doc.document.LineStart(0));
							 | 
						||
| 
								 | 
							
										REQUIRE(0 == doc.document.LineFromPosition(0));
							 | 
						||
| 
								 | 
							
										REQUIRE(0 == doc.document.LineStartPosition(0));
							 | 
						||
| 
								 | 
							
										REQUIRE(sLength == doc.document.LineStart(1));
							 | 
						||
| 
								 | 
							
										REQUIRE(0 == doc.document.LineFromPosition(static_cast<int>(sLength)));
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.document.CanUndo());
							 | 
						||
| 
								 | 
							
										REQUIRE(!doc.document.CanRedo());
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Search ranges are from first argument to just before second argument
							 | 
						||
| 
								 | 
							
									// Arguments are expected to be at character boundaries and will be tweaked if
							 | 
						||
| 
								 | 
							
									// part way through a character.
							 | 
						||
| 
								 | 
							
									SECTION("SearchInLatin") {
							 | 
						||
| 
								 | 
							
										DocPlus doc("abcde", 0);	// a b c d e
							 | 
						||
| 
								 | 
							
										constexpr std::string_view finding = "b";
							 | 
						||
| 
								 | 
							
										Sci::Position lengthFinding = finding.length();
							 | 
						||
| 
								 | 
							
										Sci::Position location = doc.FindNeedle(finding, FindOption::MatchCase, &lengthFinding);
							 | 
						||
| 
								 | 
							
										REQUIRE(location == 1);
							 | 
						||
| 
								 | 
							
										location = doc.FindNeedleReverse(finding, FindOption::MatchCase, &lengthFinding);
							 | 
						||
| 
								 | 
							
										REQUIRE(location == 1);
							 | 
						||
| 
								 | 
							
										location = doc.document.FindText(0, 2, finding.data(), FindOption::MatchCase, &lengthFinding);
							 | 
						||
| 
								 | 
							
										REQUIRE(location == 1);
							 | 
						||
| 
								 | 
							
										location = doc.document.FindText(0, 1, finding.data(), FindOption::MatchCase, &lengthFinding);
							 | 
						||
| 
								 | 
							
										REQUIRE(location == -1);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									SECTION("SearchInBothSegments") {
							 | 
						||
| 
								 | 
							
										DocPlus doc("ab-ab", 0);	// a b - a b
							 | 
						||
| 
								 | 
							
										constexpr std::string_view finding = "ab";
							 | 
						||
| 
								 | 
							
										for (int gapPos = 0; gapPos <= 5; gapPos++) {
							 | 
						||
| 
								 | 
							
											doc.MoveGap(gapPos);
							 | 
						||
| 
								 | 
							
											Sci::Position lengthFinding = finding.length();
							 | 
						||
| 
								 | 
							
											Sci::Position location = doc.document.FindText(0, doc.document.Length(), finding.data(), FindOption::MatchCase, &lengthFinding);
							 | 
						||
| 
								 | 
							
											REQUIRE(location == 0);
							 | 
						||
| 
								 | 
							
											location = doc.document.FindText(2, doc.document.Length(), finding.data(), FindOption::MatchCase, &lengthFinding);
							 | 
						||
| 
								 | 
							
											REQUIRE(location == 3);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									SECTION("InsensitiveSearchInLatin") {
							 | 
						||
| 
								 | 
							
										DocPlus doc("abcde", 0);	// a b c d e
							 | 
						||
| 
								 | 
							
										constexpr std::string_view finding = "B";
							 | 
						||
| 
								 | 
							
										Sci::Position lengthFinding = finding.length();
							 | 
						||
| 
								 | 
							
										Sci::Position location = doc.FindNeedle(finding, FindOption::None, &lengthFinding);
							 | 
						||
| 
								 | 
							
										REQUIRE(location == 1);
							 | 
						||
| 
								 | 
							
										location = doc.FindNeedleReverse(finding, FindOption::None, &lengthFinding);
							 | 
						||
| 
								 | 
							
										REQUIRE(location == 1);
							 | 
						||
| 
								 | 
							
										location = doc.document.FindText(0, 2, finding.data(), FindOption::None, &lengthFinding);
							 | 
						||
| 
								 | 
							
										REQUIRE(location == 1);
							 | 
						||
| 
								 | 
							
										location = doc.document.FindText(0, 1, finding.data(), FindOption::None, &lengthFinding);
							 | 
						||
| 
								 | 
							
										REQUIRE(location == -1);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									SECTION("InsensitiveSearchIn1252") {
							 | 
						||
| 
								 | 
							
										// In Windows Latin, code page 1252, C6 is AE and E6 is ae
							 | 
						||
| 
								 | 
							
										DocPlus doc("tru\xc6s\xe6t", 0);	// t r u AE s ae t
							 | 
						||
| 
								 | 
							
										doc.SetSBCSFoldings(foldings1252, std::size(foldings1252));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Search for upper-case AE
							 | 
						||
| 
								 | 
							
										std::string_view finding = "\xc6";
							 | 
						||
| 
								 | 
							
										Sci::Position lengthFinding = finding.length();
							 | 
						||
| 
								 | 
							
										Sci::Position location = doc.FindNeedle(finding, FindOption::None, &lengthFinding);
							 | 
						||
| 
								 | 
							
										REQUIRE(location == 3);
							 | 
						||
| 
								 | 
							
										location = doc.document.FindText(4, doc.document.Length(), finding.data(), FindOption::None, &lengthFinding);
							 | 
						||
| 
								 | 
							
										REQUIRE(location == 5);
							 | 
						||
| 
								 | 
							
										location = doc.FindNeedleReverse(finding, FindOption::None, &lengthFinding);
							 | 
						||
| 
								 | 
							
										REQUIRE(location == 5);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Search for lower-case ae
							 | 
						||
| 
								 | 
							
										finding = "\xe6";
							 | 
						||
| 
								 | 
							
										location = doc.FindNeedle(finding, FindOption::None, &lengthFinding);
							 | 
						||
| 
								 | 
							
										REQUIRE(location == 3);
							 | 
						||
| 
								 | 
							
										location = doc.document.FindText(4, doc.document.Length(), finding.data(), FindOption::None, &lengthFinding);
							 | 
						||
| 
								 | 
							
										REQUIRE(location == 5);
							 | 
						||
| 
								 | 
							
										location = doc.FindNeedleReverse(finding, FindOption::None, &lengthFinding);
							 | 
						||
| 
								 | 
							
										REQUIRE(location == 5);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									SECTION("Search2InLatin") {
							 | 
						||
| 
								 | 
							
										// Checks that the initial '_' and final 'f' are ignored since they are outside the search bounds
							 | 
						||
| 
								 | 
							
										DocPlus doc("_abcdef", 0);	// _ a b c d e f
							 | 
						||
| 
								 | 
							
										constexpr std::string_view finding = "cd";
							 | 
						||
| 
								 | 
							
										Sci::Position lengthFinding = finding.length();
							 | 
						||
| 
								 | 
							
										const size_t docLength = doc.document.Length() - 1;
							 | 
						||
| 
								 | 
							
										Sci::Position location = doc.document.FindText(1, docLength, finding.data(), FindOption::MatchCase, &lengthFinding);
							 | 
						||
| 
								 | 
							
										REQUIRE(location == 3);
							 | 
						||
| 
								 | 
							
										location = doc.document.FindText(docLength, 1, finding.data(), FindOption::MatchCase, &lengthFinding);
							 | 
						||
| 
								 | 
							
										REQUIRE(location == 3);
							 | 
						||
| 
								 | 
							
										location = doc.document.FindText(docLength, 1, "bc", FindOption::MatchCase, &lengthFinding);
							 | 
						||
| 
								 | 
							
										REQUIRE(location == 2);
							 | 
						||
| 
								 | 
							
										location = doc.document.FindText(docLength, 1, "ab", FindOption::MatchCase, &lengthFinding);
							 | 
						||
| 
								 | 
							
										REQUIRE(location == 1);
							 | 
						||
| 
								 | 
							
										location = doc.document.FindText(docLength, 1, "de", FindOption::MatchCase, &lengthFinding);
							 | 
						||
| 
								 | 
							
										REQUIRE(location == 4);
							 | 
						||
| 
								 | 
							
										location = doc.document.FindText(docLength, 1, "_a", FindOption::MatchCase, &lengthFinding);
							 | 
						||
| 
								 | 
							
										REQUIRE(location == -1);
							 | 
						||
| 
								 | 
							
										location = doc.document.FindText(docLength, 1, "ef", FindOption::MatchCase, &lengthFinding);
							 | 
						||
| 
								 | 
							
										REQUIRE(location == -1);
							 | 
						||
| 
								 | 
							
										lengthFinding = 3;
							 | 
						||
| 
								 | 
							
										location = doc.document.FindText(docLength, 1, "cde", FindOption::MatchCase, &lengthFinding);
							 | 
						||
| 
								 | 
							
										REQUIRE(location == 3);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									SECTION("SearchInUTF8") {
							 | 
						||
| 
								 | 
							
										DocPlus doc("ab\xCE\x93" "d", CpUtf8);	// a b gamma d
							 | 
						||
| 
								 | 
							
										constexpr std::string_view finding = "b";
							 | 
						||
| 
								 | 
							
										Sci::Position lengthFinding = finding.length();
							 | 
						||
| 
								 | 
							
										Sci::Position location = doc.FindNeedle(finding, FindOption::MatchCase, &lengthFinding);
							 | 
						||
| 
								 | 
							
										REQUIRE(location == 1);
							 | 
						||
| 
								 | 
							
										location = doc.document.FindText(doc.document.Length(), 0, finding.data(), FindOption::MatchCase, &lengthFinding);
							 | 
						||
| 
								 | 
							
										REQUIRE(location == 1);
							 | 
						||
| 
								 | 
							
										location = doc.document.FindText(0, 1, finding.data(), FindOption::MatchCase, &lengthFinding);
							 | 
						||
| 
								 | 
							
										REQUIRE(location == -1);
							 | 
						||
| 
								 | 
							
										// Check doesn't try to follow a lead-byte past the search end
							 | 
						||
| 
								 | 
							
										constexpr std::string_view findingUTF = "\xCE\x93";
							 | 
						||
| 
								 | 
							
										lengthFinding = findingUTF.length();
							 | 
						||
| 
								 | 
							
										location = doc.document.FindText(0, 4, findingUTF.data(), FindOption::MatchCase, &lengthFinding);
							 | 
						||
| 
								 | 
							
										REQUIRE(location == 2);
							 | 
						||
| 
								 | 
							
										// Only succeeds as 3 is partway through character so adjusted to 4
							 | 
						||
| 
								 | 
							
										location = doc.document.FindText(0, 3, findingUTF.data(), FindOption::MatchCase, &lengthFinding);
							 | 
						||
| 
								 | 
							
										REQUIRE(location == 2);
							 | 
						||
| 
								 | 
							
										location = doc.document.FindText(0, 2, findingUTF.data(), FindOption::MatchCase, &lengthFinding);
							 | 
						||
| 
								 | 
							
										REQUIRE(location == -1);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									SECTION("InsensitiveSearchInUTF8") {
							 | 
						||
| 
								 | 
							
										DocPlus doc("ab\xCE\x93" "d", CpUtf8);	// a b gamma d
							 | 
						||
| 
								 | 
							
										constexpr std::string_view finding = "b";
							 | 
						||
| 
								 | 
							
										Sci::Position lengthFinding = finding.length();
							 | 
						||
| 
								 | 
							
										Sci::Position location = doc.FindNeedle(finding, FindOption::None, &lengthFinding);
							 | 
						||
| 
								 | 
							
										REQUIRE(location == 1);
							 | 
						||
| 
								 | 
							
										location = doc.document.FindText(doc.document.Length(), 0, finding.data(), FindOption::None, &lengthFinding);
							 | 
						||
| 
								 | 
							
										REQUIRE(location == 1);
							 | 
						||
| 
								 | 
							
										constexpr std::string_view findingUTF = "\xCE\x93";
							 | 
						||
| 
								 | 
							
										lengthFinding = findingUTF.length();
							 | 
						||
| 
								 | 
							
										location = doc.FindNeedle(findingUTF, FindOption::None, &lengthFinding);
							 | 
						||
| 
								 | 
							
										REQUIRE(location == 2);
							 | 
						||
| 
								 | 
							
										location = doc.document.FindText(doc.document.Length(), 0, findingUTF.data(), FindOption::None, &lengthFinding);
							 | 
						||
| 
								 | 
							
										REQUIRE(location == 2);
							 | 
						||
| 
								 | 
							
										location = doc.document.FindText(0, 4, findingUTF.data(), FindOption::None, &lengthFinding);
							 | 
						||
| 
								 | 
							
										REQUIRE(location == 2);
							 | 
						||
| 
								 | 
							
										// Only succeeds as 3 is partway through character so adjusted to 4
							 | 
						||
| 
								 | 
							
										location = doc.document.FindText(0, 3, findingUTF.data(), FindOption::None, &lengthFinding);
							 | 
						||
| 
								 | 
							
										REQUIRE(location == 2);
							 | 
						||
| 
								 | 
							
										location = doc.document.FindText(0, 2, findingUTF.data(), FindOption::None, &lengthFinding);
							 | 
						||
| 
								 | 
							
										REQUIRE(location == -1);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									SECTION("SearchInShiftJIS") {
							 | 
						||
| 
								 | 
							
										// {CJK UNIFIED IDEOGRAPH-9955} is two bytes: {0xE9, 'b'} in Shift-JIS
							 | 
						||
| 
								 | 
							
										// The 'b' can be incorrectly matched by the search string 'b' when the search
							 | 
						||
| 
								 | 
							
										// does not iterate the text correctly.
							 | 
						||
| 
								 | 
							
										DocPlus doc("ab\xe9" "b ", 932);	// a b {CJK UNIFIED IDEOGRAPH-9955} {space}
							 | 
						||
| 
								 | 
							
										constexpr std::string_view finding = "b";
							 | 
						||
| 
								 | 
							
										// Search forwards
							 | 
						||
| 
								 | 
							
										Sci::Position lengthFinding = finding.length();
							 | 
						||
| 
								 | 
							
										Sci::Position location = doc.FindNeedle(finding, FindOption::MatchCase, &lengthFinding);
							 | 
						||
| 
								 | 
							
										REQUIRE(location == 1);
							 | 
						||
| 
								 | 
							
										// Search backwards
							 | 
						||
| 
								 | 
							
										lengthFinding = finding.length();
							 | 
						||
| 
								 | 
							
										location = doc.document.FindText(doc.document.Length(), 0, finding.data(), FindOption::MatchCase, &lengthFinding);
							 | 
						||
| 
								 | 
							
										REQUIRE(location == 1);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									SECTION("InsensitiveSearchInShiftJIS") {
							 | 
						||
| 
								 | 
							
										// {CJK UNIFIED IDEOGRAPH-9955} is two bytes: {0xE9, 'b'} in Shift-JIS
							 | 
						||
| 
								 | 
							
										// The 'b' can be incorrectly matched by the search string 'b' when the search
							 | 
						||
| 
								 | 
							
										// does not iterate the text correctly.
							 | 
						||
| 
								 | 
							
										DocPlus doc("ab\xe9" "b ", 932);	// a b {CJK UNIFIED IDEOGRAPH-9955} {space}
							 | 
						||
| 
								 | 
							
										constexpr std::string_view finding = "b";
							 | 
						||
| 
								 | 
							
										// Search forwards
							 | 
						||
| 
								 | 
							
										Sci::Position lengthFinding = finding.length();
							 | 
						||
| 
								 | 
							
										Sci::Position location = doc.FindNeedle(finding, FindOption::None, &lengthFinding);
							 | 
						||
| 
								 | 
							
										REQUIRE(location == 1);
							 | 
						||
| 
								 | 
							
										// Search backwards
							 | 
						||
| 
								 | 
							
										lengthFinding = finding.length();
							 | 
						||
| 
								 | 
							
										location = doc.document.FindText(doc.document.Length(), 0, finding.data(), FindOption::None, &lengthFinding);
							 | 
						||
| 
								 | 
							
										REQUIRE(location == 1);
							 | 
						||
| 
								 | 
							
										constexpr std::string_view finding932 = "\xe9" "b";
							 | 
						||
| 
								 | 
							
										// Search forwards
							 | 
						||
| 
								 | 
							
										lengthFinding = finding932.length();
							 | 
						||
| 
								 | 
							
										location = doc.FindNeedle(finding932, FindOption::None, &lengthFinding);
							 | 
						||
| 
								 | 
							
										REQUIRE(location == 2);
							 | 
						||
| 
								 | 
							
										// Search backwards
							 | 
						||
| 
								 | 
							
										lengthFinding = finding932.length();
							 | 
						||
| 
								 | 
							
										location = doc.document.FindText(doc.document.Length(), 0, finding932.data(), FindOption::None, &lengthFinding);
							 | 
						||
| 
								 | 
							
										REQUIRE(location == 2);
							 | 
						||
| 
								 | 
							
										location = doc.document.FindText(0, 3, finding932.data(), FindOption::None, &lengthFinding);
							 | 
						||
| 
								 | 
							
										REQUIRE(location == 2);
							 | 
						||
| 
								 | 
							
										location = doc.document.FindText(0, 2, finding932.data(), FindOption::None, &lengthFinding);
							 | 
						||
| 
								 | 
							
										REQUIRE(location == -1);
							 | 
						||
| 
								 | 
							
										// Can not test case mapping of double byte text as folder available here does not implement this
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									SECTION("GetCharacterAndWidth DBCS") {
							 | 
						||
| 
								 | 
							
										Document doc(DocumentOption::Default);
							 | 
						||
| 
								 | 
							
										doc.SetDBCSCodePage(932);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.CodePage() == 932);
							 | 
						||
| 
								 | 
							
										const Sci::Position length = doc.InsertString(0, "H\x84\xff\x84H", 5);
							 | 
						||
| 
								 | 
							
										// This text is invalid in code page 932.
							 | 
						||
| 
								 | 
							
										// A reasonable interpretation is as 4 items: 2 characters and 2 character fragments
							 | 
						||
| 
								 | 
							
										// The last item is a 2-byte CYRILLIC CAPITAL LETTER ZE character
							 | 
						||
| 
								 | 
							
										// H [84] [FF] ZE
							 | 
						||
| 
								 | 
							
										REQUIRE(5 == length);
							 | 
						||
| 
								 | 
							
										REQUIRE(5 == doc.Length());
							 | 
						||
| 
								 | 
							
										Sci::Position width = 0;
							 | 
						||
| 
								 | 
							
										// test GetCharacterAndWidth()
							 | 
						||
| 
								 | 
							
										int ch = doc.GetCharacterAndWidth(0, &width);
							 | 
						||
| 
								 | 
							
										REQUIRE(width == 1);
							 | 
						||
| 
								 | 
							
										REQUIRE(ch == 'H');
							 | 
						||
| 
								 | 
							
										ch = doc.GetCharacterAndWidth(1, &width);
							 | 
						||
| 
								 | 
							
										REQUIRE(width == 1);
							 | 
						||
| 
								 | 
							
										REQUIRE(ch == 0x84);
							 | 
						||
| 
								 | 
							
										width = 0;
							 | 
						||
| 
								 | 
							
										ch = doc.GetCharacterAndWidth(2, &width);
							 | 
						||
| 
								 | 
							
										REQUIRE(width == 1);
							 | 
						||
| 
								 | 
							
										REQUIRE(ch == 0xff);
							 | 
						||
| 
								 | 
							
										width = 0;
							 | 
						||
| 
								 | 
							
										ch = doc.GetCharacterAndWidth(3, &width);
							 | 
						||
| 
								 | 
							
										REQUIRE(width == 2);
							 | 
						||
| 
								 | 
							
										REQUIRE(ch == 0x8448);
							 | 
						||
| 
								 | 
							
										// test LenChar()
							 | 
						||
| 
								 | 
							
										width = doc.LenChar(0);
							 | 
						||
| 
								 | 
							
										REQUIRE(width == 1);
							 | 
						||
| 
								 | 
							
										width = doc.LenChar(1);
							 | 
						||
| 
								 | 
							
										REQUIRE(width == 1);
							 | 
						||
| 
								 | 
							
										width = doc.LenChar(2);
							 | 
						||
| 
								 | 
							
										REQUIRE(width == 1);
							 | 
						||
| 
								 | 
							
										width = doc.LenChar(3);
							 | 
						||
| 
								 | 
							
										REQUIRE(width == 2);
							 | 
						||
| 
								 | 
							
										// test MovePositionOutsideChar()
							 | 
						||
| 
								 | 
							
										Sci::Position pos = doc.MovePositionOutsideChar(1, 1);
							 | 
						||
| 
								 | 
							
										REQUIRE(pos == 1);
							 | 
						||
| 
								 | 
							
										pos = doc.MovePositionOutsideChar(2, 1);
							 | 
						||
| 
								 | 
							
										REQUIRE(pos == 2);
							 | 
						||
| 
								 | 
							
										pos = doc.MovePositionOutsideChar(3, 1);
							 | 
						||
| 
								 | 
							
										REQUIRE(pos == 3);
							 | 
						||
| 
								 | 
							
										pos = doc.MovePositionOutsideChar(4, 1);
							 | 
						||
| 
								 | 
							
										REQUIRE(pos == 5);
							 | 
						||
| 
								 | 
							
										pos = doc.MovePositionOutsideChar(1, -1);
							 | 
						||
| 
								 | 
							
										REQUIRE(pos == 1);
							 | 
						||
| 
								 | 
							
										pos = doc.MovePositionOutsideChar(2, -1);
							 | 
						||
| 
								 | 
							
										REQUIRE(pos == 2);
							 | 
						||
| 
								 | 
							
										pos = doc.MovePositionOutsideChar(3, -1);
							 | 
						||
| 
								 | 
							
										REQUIRE(pos == 3);
							 | 
						||
| 
								 | 
							
										pos = doc.MovePositionOutsideChar(4, -1);
							 | 
						||
| 
								 | 
							
										REQUIRE(pos == 3);
							 | 
						||
| 
								 | 
							
										// test NextPosition()
							 | 
						||
| 
								 | 
							
										pos = doc.NextPosition(0, 1);
							 | 
						||
| 
								 | 
							
										REQUIRE(pos == 1);
							 | 
						||
| 
								 | 
							
										pos = doc.NextPosition(1, 1);
							 | 
						||
| 
								 | 
							
										REQUIRE(pos == 2);
							 | 
						||
| 
								 | 
							
										pos = doc.NextPosition(2, 1);
							 | 
						||
| 
								 | 
							
										REQUIRE(pos == 3);
							 | 
						||
| 
								 | 
							
										pos = doc.NextPosition(3, 1);
							 | 
						||
| 
								 | 
							
										REQUIRE(pos == 5);
							 | 
						||
| 
								 | 
							
										pos = doc.NextPosition(1, -1);
							 | 
						||
| 
								 | 
							
										REQUIRE(pos == 0);
							 | 
						||
| 
								 | 
							
										pos = doc.NextPosition(2, -1);
							 | 
						||
| 
								 | 
							
										REQUIRE(pos == 1);
							 | 
						||
| 
								 | 
							
										pos = doc.NextPosition(3, -1);
							 | 
						||
| 
								 | 
							
										REQUIRE(pos == 2);
							 | 
						||
| 
								 | 
							
										pos = doc.NextPosition(5, -1);
							 | 
						||
| 
								 | 
							
										REQUIRE(pos == 3);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									SECTION("NextPosition Valid DBCS") {
							 | 
						||
| 
								 | 
							
										Document doc(DocumentOption::Default);
							 | 
						||
| 
								 | 
							
										doc.SetDBCSCodePage(932);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.CodePage() == 932);
							 | 
						||
| 
								 | 
							
										// This text is valid in code page 932.
							 | 
						||
| 
								 | 
							
										// O p e n = U+958B Ku ( O ) U+7DE8 -
							 | 
						||
| 
								 | 
							
										// U+958B open
							 | 
						||
| 
								 | 
							
										// U+7DE8 arrange
							 | 
						||
| 
								 | 
							
										constexpr std::string_view japaneseText = "Open=\x8aJ\x82\xad(O)\x95\xd2-";
							 | 
						||
| 
								 | 
							
										const Sci::Position length = doc.InsertString(0, japaneseText);
							 | 
						||
| 
								 | 
							
										REQUIRE(length == 15);
							 | 
						||
| 
								 | 
							
										// Forwards
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.NextPosition( 0, 1) == 1);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.NextPosition( 1, 1) == 2);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.NextPosition( 2, 1) == 3);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.NextPosition( 3, 1) == 4);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.NextPosition( 4, 1) == 5);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.NextPosition( 5, 1) == 7);	// Double byte
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.NextPosition( 6, 1) == 7);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.NextPosition( 7, 1) == 9);	// Double byte
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.NextPosition( 8, 1) == 9);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.NextPosition( 9, 1) == 10);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.NextPosition(10, 1) == 11);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.NextPosition(11, 1) == 12);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.NextPosition(12, 1) == 14);	// Double byte
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.NextPosition(13, 1) == 14);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.NextPosition(14, 1) == 15);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.NextPosition(15, 1) == 15);
							 | 
						||
| 
								 | 
							
										// Backwards
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.NextPosition( 0, -1) == 0);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.NextPosition( 1, -1) == 0);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.NextPosition( 2, -1) == 1);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.NextPosition( 3, -1) == 2);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.NextPosition( 4, -1) == 3);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.NextPosition( 5, -1) == 4);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.NextPosition( 6, -1) == 5);	// Double byte
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.NextPosition( 7, -1) == 5);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.NextPosition( 8, -1) == 7);	// Double byte
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.NextPosition( 9, -1) == 7);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.NextPosition(10, -1) == 9);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.NextPosition(11, -1) == 10);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.NextPosition(12, -1) == 11);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.NextPosition(13, -1) == 12);	// Double byte
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.NextPosition(14, -1) == 12);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.NextPosition(15, -1) == 14);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									SECTION("RegexSearchAndSubstitution") {
							 | 
						||
| 
								 | 
							
										DocPlus doc("\n\r\r\n 1a\xCE\x93z \n\r\r\n 2b\xCE\x93y \n\r\r\n", CpUtf8);// 1a gamma z 2b gamma y
							 | 
						||
| 
								 | 
							
										const Sci::Position docLength = doc.document.Length();
							 | 
						||
| 
								 | 
							
										Match match;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										constexpr std::string_view finding = R"(\d+(\w+))";
							 | 
						||
| 
								 | 
							
										constexpr std::string_view substituteText = R"(\t\1\n)";
							 | 
						||
| 
								 | 
							
										constexpr std::string_view longest = "\\w+";
							 | 
						||
| 
								 | 
							
										std::string substituted;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										match = doc.FindString(0, docLength, finding, rePosix);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(5, 5));
							 | 
						||
| 
								 | 
							
										substituted = doc.Substitute(substituteText);
							 | 
						||
| 
								 | 
							
										REQUIRE(substituted == "\ta\xCE\x93z\n");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										match = doc.FindString(docLength, 0, finding, rePosix);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(16, 5));
							 | 
						||
| 
								 | 
							
										substituted = doc.Substitute(substituteText);
							 | 
						||
| 
								 | 
							
										REQUIRE(substituted == "\tb\xCE\x93y\n");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										match = doc.FindString(docLength, 0, longest, rePosix);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(16, 5));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										#ifndef NO_CXX11_REGEX
							 | 
						||
| 
								 | 
							
										match = doc.FindString(0, docLength, finding, reCxx11);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(5, 5));
							 | 
						||
| 
								 | 
							
										substituted = doc.Substitute(substituteText);
							 | 
						||
| 
								 | 
							
										REQUIRE(substituted == "\ta\xCE\x93z\n");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										match = doc.FindString(docLength, 0, finding, reCxx11);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(16, 5));
							 | 
						||
| 
								 | 
							
										substituted = doc.Substitute(substituteText);
							 | 
						||
| 
								 | 
							
										REQUIRE(substituted == "\tb\xCE\x93y\n");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										match = doc.FindString(docLength, 0, longest, reCxx11);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(16, 5));
							 | 
						||
| 
								 | 
							
										#endif
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									SECTION("RegexAssertion") {
							 | 
						||
| 
								 | 
							
										DocPlus doc("ab cd ef\r\ngh ij kl", CpUtf8);
							 | 
						||
| 
								 | 
							
										const Sci::Position docLength = doc.document.Length();
							 | 
						||
| 
								 | 
							
										Match match;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										constexpr std::string_view findingBOL = "^";
							 | 
						||
| 
								 | 
							
										match = doc.FindString(0, docLength, findingBOL, rePosix);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(0));
							 | 
						||
| 
								 | 
							
										match = doc.FindString(1, docLength, findingBOL, rePosix);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(10));
							 | 
						||
| 
								 | 
							
										match = doc.FindString(docLength, 0, findingBOL, rePosix);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(10));
							 | 
						||
| 
								 | 
							
										match = doc.FindString(docLength - 1, 0, findingBOL, rePosix);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(10));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										#ifndef NO_CXX11_REGEX
							 | 
						||
| 
								 | 
							
										match = doc.FindString(0, docLength, findingBOL, reCxx11);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(0));
							 | 
						||
| 
								 | 
							
										match = doc.FindString(1, docLength, findingBOL, reCxx11);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(10));
							 | 
						||
| 
								 | 
							
										match = doc.FindString(docLength, 0, findingBOL, reCxx11);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(10));
							 | 
						||
| 
								 | 
							
										match = doc.FindString(docLength - 1, 0, findingBOL, reCxx11);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(10));
							 | 
						||
| 
								 | 
							
										#endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										constexpr std::string_view findingEOL = "$";
							 | 
						||
| 
								 | 
							
										match = doc.FindString(0, docLength, findingEOL, rePosix);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(8));
							 | 
						||
| 
								 | 
							
										match = doc.FindString(1, docLength, findingEOL, rePosix);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(8));
							 | 
						||
| 
								 | 
							
										match = doc.FindString(docLength, 0, findingEOL, rePosix);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(18));
							 | 
						||
| 
								 | 
							
										match = doc.FindString(docLength - 1, 0, findingEOL, rePosix);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(8));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										#if !defined(NO_CXX11_REGEX) && !defined(_LIBCPP_VERSION)
							 | 
						||
| 
								 | 
							
										match = doc.FindString(0, docLength, findingEOL, reCxx11);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(8));
							 | 
						||
| 
								 | 
							
										match = doc.FindString(1, docLength, findingEOL, reCxx11);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(8));
							 | 
						||
| 
								 | 
							
										match = doc.FindString(docLength, 0, findingEOL, reCxx11);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(18));
							 | 
						||
| 
								 | 
							
										match = doc.FindString(docLength - 1, 0, findingEOL, reCxx11);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(8));
							 | 
						||
| 
								 | 
							
										#endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										constexpr std::string_view findingBOW = "\\<";
							 | 
						||
| 
								 | 
							
										match = doc.FindString(0, docLength, findingBOW, rePosix);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(0));
							 | 
						||
| 
								 | 
							
										match = doc.FindString(1, docLength, findingBOW, rePosix);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(3));
							 | 
						||
| 
								 | 
							
										match = doc.FindString(docLength, 0, findingBOW, rePosix);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(16));
							 | 
						||
| 
								 | 
							
										match = doc.FindString(docLength - 1, 0, findingBOW, rePosix);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(16));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										constexpr std::string_view findingEOW = "\\>";
							 | 
						||
| 
								 | 
							
										match = doc.FindString(0, docLength, findingEOW, rePosix);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(2));
							 | 
						||
| 
								 | 
							
										match = doc.FindString(1, docLength, findingEOW, rePosix);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(2));
							 | 
						||
| 
								 | 
							
										match = doc.FindString(docLength, 0, findingEOW, rePosix);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(18));
							 | 
						||
| 
								 | 
							
										match = doc.FindString(docLength - 1, 0, findingEOW, rePosix);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(15));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										constexpr std::string_view findingEOWEOL = "\\>$";
							 | 
						||
| 
								 | 
							
										match = doc.FindString(0, docLength, findingEOWEOL, rePosix);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(8));
							 | 
						||
| 
								 | 
							
										match = doc.FindString(10, docLength, findingEOWEOL, rePosix);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(18));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										#ifndef NO_CXX11_REGEX
							 | 
						||
| 
								 | 
							
										constexpr std::string_view findingWB = "\\b";
							 | 
						||
| 
								 | 
							
										match = doc.FindString(0, docLength, findingWB, reCxx11);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(0));
							 | 
						||
| 
								 | 
							
										match = doc.FindString(1, docLength, findingWB, reCxx11);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(2));
							 | 
						||
| 
								 | 
							
										match = doc.FindString(docLength, 0, findingWB, reCxx11);
							 | 
						||
| 
								 | 
							
										#ifdef _LIBCPP_VERSION
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(16));
							 | 
						||
| 
								 | 
							
										#else
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(18));
							 | 
						||
| 
								 | 
							
										#endif
							 | 
						||
| 
								 | 
							
										match = doc.FindString(docLength - 1, 0, findingWB, reCxx11);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(16));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										constexpr std::string_view findingNWB = "\\B";
							 | 
						||
| 
								 | 
							
										match = doc.FindString(0, docLength, findingNWB, reCxx11);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(1));
							 | 
						||
| 
								 | 
							
										match = doc.FindString(1, docLength, findingNWB, reCxx11);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(1));
							 | 
						||
| 
								 | 
							
										#ifdef _LIBCPP_VERSION
							 | 
						||
| 
								 | 
							
										match = doc.FindString(docLength, 0, findingNWB, reCxx11);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(18));
							 | 
						||
| 
								 | 
							
										match = doc.FindString(docLength - 1, 0, findingNWB, reCxx11);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(14));
							 | 
						||
| 
								 | 
							
										#else
							 | 
						||
| 
								 | 
							
										match = doc.FindString(docLength, 0, findingNWB, reCxx11);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(17));
							 | 
						||
| 
								 | 
							
										match = doc.FindString(docLength - 1, 0, findingNWB, reCxx11);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(17));
							 | 
						||
| 
								 | 
							
										#endif
							 | 
						||
| 
								 | 
							
										#endif
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									SECTION("RegexContextualAssertion") {
							 | 
						||
| 
								 | 
							
										// For std::regex, check the use of assertions next to text in forward direction
							 | 
						||
| 
								 | 
							
										// These are more common than empty assertions
							 | 
						||
| 
								 | 
							
										DocPlus doc("ab cd ef\r\ngh ij kl", CpUtf8);
							 | 
						||
| 
								 | 
							
										const Sci::Position docLength = doc.document.Length();
							 | 
						||
| 
								 | 
							
										Match match;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										#ifndef NO_CXX11_REGEX
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										match = doc.FindString(0, docLength, "^[a-z]", reCxx11);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(0, 1));
							 | 
						||
| 
								 | 
							
										match = doc.FindString(1, docLength, "^[a-z]", reCxx11);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(10, 1));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										match = doc.FindString(0, docLength, "[a-z]$", reCxx11);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(7, 1));
							 | 
						||
| 
								 | 
							
										match = doc.FindString(10, docLength, "[a-z]$", reCxx11);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(17, 1));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										match = doc.FindString(0, docLength, "\\b[a-z]", reCxx11);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(0, 1));
							 | 
						||
| 
								 | 
							
										match = doc.FindString(1, docLength, "\\b[a-z]", reCxx11);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(3, 1));
							 | 
						||
| 
								 | 
							
										match = doc.FindString(0, docLength, "[a-z]\\b", reCxx11);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(1, 1));
							 | 
						||
| 
								 | 
							
										match = doc.FindString(2, docLength, "[a-z]\\b", reCxx11);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(4, 1));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										match = doc.FindString(0, docLength, "\\B[a-z]", reCxx11);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(1, 1));
							 | 
						||
| 
								 | 
							
										match = doc.FindString(1, docLength, "\\B[a-z]", reCxx11);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(1, 1));
							 | 
						||
| 
								 | 
							
										match = doc.FindString(0, docLength, "[a-z]\\B", reCxx11);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(0, 1));
							 | 
						||
| 
								 | 
							
										match = doc.FindString(2, docLength, "[a-z]\\B", reCxx11);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(3, 1));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										#endif
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									SECTION("RESearchMovePositionOutsideCharUTF8") {
							 | 
						||
| 
								 | 
							
										DocPlus doc(" a\xCE\x93\xCE\x93z ", CpUtf8);// a gamma gamma z
							 | 
						||
| 
								 | 
							
										const Sci::Position docLength = doc.document.Length();
							 | 
						||
| 
								 | 
							
										constexpr std::string_view finding = R"([a-z](\w)\1)";
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										Match match = doc.FindString(0, docLength, finding, rePosix);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(1, 5));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										constexpr std::string_view substituteText = R"(\t\1\n)";
							 | 
						||
| 
								 | 
							
										std::string substituted = doc.Substitute(substituteText);
							 | 
						||
| 
								 | 
							
										REQUIRE(substituted == "\t\xCE\x93\n");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										#ifndef NO_CXX11_REGEX
							 | 
						||
| 
								 | 
							
										match = doc.FindString(0, docLength, finding, reCxx11);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(1, 5));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										substituted = doc.Substitute(substituteText);
							 | 
						||
| 
								 | 
							
										REQUIRE(substituted == "\t\xCE\x93\n");
							 | 
						||
| 
								 | 
							
										#endif
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									SECTION("RESearchMovePositionOutsideCharDBCS") {
							 | 
						||
| 
								 | 
							
										DocPlus doc(" \x98\x61xx 1aa\x83\xA1\x83\xA1z ", 932);// U+548C xx 1aa gamma gamma z
							 | 
						||
| 
								 | 
							
										const Sci::Position docLength = doc.document.Length();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										Match match = doc.FindString(0, docLength, R"([a-z](\w)\1)", rePosix);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(8, 5));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										constexpr std::string_view substituteText = R"(\t\1\n)";
							 | 
						||
| 
								 | 
							
										std::string substituted = doc.Substitute(substituteText);
							 | 
						||
| 
								 | 
							
										REQUIRE(substituted == "\t\x83\xA1\n");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										match = doc.FindString(0, docLength, R"(\w([a-z])\1)", rePosix);
							 | 
						||
| 
								 | 
							
										REQUIRE(match == Match(6, 3));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										substituted = doc.Substitute(substituteText);
							 | 
						||
| 
								 | 
							
										REQUIRE(substituted == "\ta\n");
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								TEST_CASE("DocumentUndo") {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// These tests check that Undo reports the end of coalesced deletes
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									constexpr std::string_view sText = "Scintilla";
							 | 
						||
| 
								 | 
							
									DocPlus doc(sText, 0);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									SECTION("CheckDeleteForwards") {
							 | 
						||
| 
								 | 
							
										// Delete forwards like the Del key
							 | 
						||
| 
								 | 
							
										doc.document.DeleteUndoHistory();
							 | 
						||
| 
								 | 
							
										doc.document.DeleteChars(1, 1);
							 | 
						||
| 
								 | 
							
										doc.document.DeleteChars(1, 1);
							 | 
						||
| 
								 | 
							
										doc.document.DeleteChars(1, 1);
							 | 
						||
| 
								 | 
							
										const Sci::Position position = doc.document.Undo();
							 | 
						||
| 
								 | 
							
										REQUIRE(position == 4);	// End of reinsertion
							 | 
						||
| 
								 | 
							
										REQUIRE(!doc.document.CanUndo());	// Exhausted undo stack
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.document.CanRedo());
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									SECTION("CheckDeleteBackwards") {
							 | 
						||
| 
								 | 
							
										// Delete backwards like the backspace key
							 | 
						||
| 
								 | 
							
										doc.document.DeleteUndoHistory();
							 | 
						||
| 
								 | 
							
										doc.document.DeleteChars(5, 1);
							 | 
						||
| 
								 | 
							
										doc.document.DeleteChars(4, 1);
							 | 
						||
| 
								 | 
							
										doc.document.DeleteChars(3, 1);
							 | 
						||
| 
								 | 
							
										const Sci::Position position = doc.document.Undo();
							 | 
						||
| 
								 | 
							
										REQUIRE(position == 6);	// End of reinsertion
							 | 
						||
| 
								 | 
							
										REQUIRE(!doc.document.CanUndo());	// Exhausted undo stack
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									SECTION("CheckBothWays") {
							 | 
						||
| 
								 | 
							
										// Delete backwards like the backspace key
							 | 
						||
| 
								 | 
							
										doc.document.DeleteUndoHistory();
							 | 
						||
| 
								 | 
							
										// Like having the caret at position 5 then
							 | 
						||
| 
								 | 
							
										doc.document.DeleteChars(5, 1);	// Del
							 | 
						||
| 
								 | 
							
										doc.document.DeleteChars(4, 1); // Backspace
							 | 
						||
| 
								 | 
							
										doc.document.DeleteChars(4, 1); // Del
							 | 
						||
| 
								 | 
							
										doc.document.DeleteChars(3, 1); // Backspace
							 | 
						||
| 
								 | 
							
										const Sci::Position position = doc.document.Undo();
							 | 
						||
| 
								 | 
							
										REQUIRE(position == 7);	// End of reinsertion, Start at 5, 2*Del
							 | 
						||
| 
								 | 
							
										REQUIRE(!doc.document.CanUndo());	// Exhausted undo stack
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									SECTION("CheckInsert") {
							 | 
						||
| 
								 | 
							
										// Insertions are only coalesced when following previous
							 | 
						||
| 
								 | 
							
										doc.document.DeleteUndoHistory();
							 | 
						||
| 
								 | 
							
										doc.document.InsertString(1, "1");
							 | 
						||
| 
								 | 
							
										doc.document.InsertString(2, "2");
							 | 
						||
| 
								 | 
							
										doc.document.InsertString(3, "3");
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.Contents() == "S123cintilla");
							 | 
						||
| 
								 | 
							
										const Sci::Position position = doc.document.Undo();
							 | 
						||
| 
								 | 
							
										REQUIRE(position == 1);	// Start of insertions
							 | 
						||
| 
								 | 
							
										REQUIRE(!doc.document.CanUndo());	// Exhausted undo stack
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									SECTION("CheckGrouped") {
							 | 
						||
| 
								 | 
							
										// Check that position returned for group is that at end of first deletion set
							 | 
						||
| 
								 | 
							
										// Also include a container undo action.
							 | 
						||
| 
								 | 
							
										doc.document.DeleteUndoHistory();
							 | 
						||
| 
								 | 
							
										doc.document.BeginUndoAction();
							 | 
						||
| 
								 | 
							
										// At 1, 2*Del so end of initial deletion sequence is 3
							 | 
						||
| 
								 | 
							
										doc.document.DeleteChars(1, 1); // 'c'
							 | 
						||
| 
								 | 
							
										doc.document.DeleteChars(1, 1); // 'i'
							 | 
						||
| 
								 | 
							
										doc.document.AddUndoAction(99, true);
							 | 
						||
| 
								 | 
							
										doc.document.InsertString(1, "1");
							 | 
						||
| 
								 | 
							
										doc.document.DeleteChars(4, 2); // 'il'
							 | 
						||
| 
								 | 
							
										doc.document.BeginUndoAction();
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.Contents() == "S1ntla");
							 | 
						||
| 
								 | 
							
										const Sci::Position position = doc.document.Undo();
							 | 
						||
| 
								 | 
							
										REQUIRE(position == 3);	// Start of insertions
							 | 
						||
| 
								 | 
							
										REQUIRE(!doc.document.CanUndo());	// Exhausted undo stack
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								TEST_CASE("Words") {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									SECTION("WordsInText") {
							 | 
						||
| 
								 | 
							
										const DocPlus doc(" abc ", 0);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.document.IsWordAt(1, 4));
							 | 
						||
| 
								 | 
							
										REQUIRE(!doc.document.IsWordAt(0, 1));
							 | 
						||
| 
								 | 
							
										REQUIRE(!doc.document.IsWordAt(1, 2));
							 | 
						||
| 
								 | 
							
										const DocPlus docPunct(" [!] ", 0);
							 | 
						||
| 
								 | 
							
										REQUIRE(docPunct.document.IsWordAt(1, 4));
							 | 
						||
| 
								 | 
							
										REQUIRE(!docPunct.document.IsWordAt(0, 1));
							 | 
						||
| 
								 | 
							
										REQUIRE(!docPunct.document.IsWordAt(1, 2));
							 | 
						||
| 
								 | 
							
										const DocPlus docMixed(" -ab ", 0);	// '-' is punctuation, 'ab' is word
							 | 
						||
| 
								 | 
							
										REQUIRE(docMixed.document.IsWordAt(2, 4));
							 | 
						||
| 
								 | 
							
										REQUIRE(docMixed.document.IsWordAt(1, 4));
							 | 
						||
| 
								 | 
							
										REQUIRE(docMixed.document.IsWordAt(1, 2));
							 | 
						||
| 
								 | 
							
										REQUIRE(!docMixed.document.IsWordAt(1, 3));	// 3 is between a and b so not word edge
							 | 
						||
| 
								 | 
							
										// Scintilla's word definition just examines the ends
							 | 
						||
| 
								 | 
							
										const DocPlus docOverSpace(" a b ", 0);
							 | 
						||
| 
								 | 
							
										REQUIRE(docOverSpace.document.IsWordAt(1, 4));
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									SECTION("WordsAtEnds") {
							 | 
						||
| 
								 | 
							
										const DocPlus doc("a c", 0);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.document.IsWordAt(0, 1));
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.document.IsWordAt(2, 3));
							 | 
						||
| 
								 | 
							
										const DocPlus docEndSpace(" a c ", 0);
							 | 
						||
| 
								 | 
							
										REQUIRE(!docEndSpace.document.IsWordAt(0, 2));
							 | 
						||
| 
								 | 
							
										REQUIRE(!docEndSpace.document.IsWordAt(3, 5));
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								TEST_CASE("SafeSegment") {
							 | 
						||
| 
								 | 
							
									SECTION("Short") {
							 | 
						||
| 
								 | 
							
										const DocPlus doc("", 0);
							 | 
						||
| 
								 | 
							
										// all encoding: break before or after last space
							 | 
						||
| 
								 | 
							
										constexpr std::string_view text = "12 ";
							 | 
						||
| 
								 | 
							
										const size_t length = doc.document.SafeSegment(text);
							 | 
						||
| 
								 | 
							
										REQUIRE(length <= text.length());
							 | 
						||
| 
								 | 
							
										REQUIRE(text[length - 1] == '2');
							 | 
						||
| 
								 | 
							
										REQUIRE(text[length] == ' ');
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									SECTION("ASCII") {
							 | 
						||
| 
								 | 
							
										const DocPlus doc("", 0);
							 | 
						||
| 
								 | 
							
										// all encoding: break before or after last space
							 | 
						||
| 
								 | 
							
										std::string_view text = "12 3 \t45";
							 | 
						||
| 
								 | 
							
										size_t length = doc.document.SafeSegment(text);
							 | 
						||
| 
								 | 
							
										REQUIRE(text[length - 1] == ' ');
							 | 
						||
| 
								 | 
							
										REQUIRE(text[length] == '\t');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// UTF-8 and ASCII: word and punctuation boundary in middle of text
							 | 
						||
| 
								 | 
							
										text = "(IsBreakSpace(text[j]))";
							 | 
						||
| 
								 | 
							
										length = doc.document.SafeSegment(text);
							 | 
						||
| 
								 | 
							
										REQUIRE(text[length - 1] == 'j');
							 | 
						||
| 
								 | 
							
										REQUIRE(text[length] == ']');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// UTF-8 and ASCII: word and punctuation boundary near start of text
							 | 
						||
| 
								 | 
							
										text = "(IsBreakSpace";
							 | 
						||
| 
								 | 
							
										length = doc.document.SafeSegment(text);
							 | 
						||
| 
								 | 
							
										REQUIRE(text[length - 1] == '(');
							 | 
						||
| 
								 | 
							
										REQUIRE(text[length] == 'I');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// UTF-8 and ASCII: word and punctuation boundary near end of text
							 | 
						||
| 
								 | 
							
										text = "IsBreakSpace)";
							 | 
						||
| 
								 | 
							
										length = doc.document.SafeSegment(text);
							 | 
						||
| 
								 | 
							
										REQUIRE(text[length - 1] == 'e');
							 | 
						||
| 
								 | 
							
										REQUIRE(text[length] == ')');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// break before last character
							 | 
						||
| 
								 | 
							
										text = "JapaneseJa";
							 | 
						||
| 
								 | 
							
										length = doc.document.SafeSegment(text);
							 | 
						||
| 
								 | 
							
										REQUIRE(text[length - 1] == 'J');
							 | 
						||
| 
								 | 
							
										REQUIRE(text[length] == 'a');
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									SECTION("UTF-8") {
							 | 
						||
| 
								 | 
							
										const DocPlus doc("", CpUtf8);
							 | 
						||
| 
								 | 
							
										// break before last character: no trail byte
							 | 
						||
| 
								 | 
							
										std::string_view text = "JapaneseJa";
							 | 
						||
| 
								 | 
							
										size_t length = doc.document.SafeSegment(text);
							 | 
						||
| 
								 | 
							
										REQUIRE(text[length - 1] == 'J');
							 | 
						||
| 
								 | 
							
										REQUIRE(text[length] == 'a');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// break before last character: 1 trail byte
							 | 
						||
| 
								 | 
							
										text = "Japanese\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e\xc2\xa9";
							 | 
						||
| 
								 | 
							
										length = doc.document.SafeSegment(text);
							 | 
						||
| 
								 | 
							
										REQUIRE(text[length - 1] == '\x9e');
							 | 
						||
| 
								 | 
							
										REQUIRE(text[length] == '\xc2');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// break before last character: 2 trail bytes
							 | 
						||
| 
								 | 
							
										text = "Japanese\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e";
							 | 
						||
| 
								 | 
							
										length = doc.document.SafeSegment(text);
							 | 
						||
| 
								 | 
							
										REQUIRE(text[length - 1] == '\xac');
							 | 
						||
| 
								 | 
							
										REQUIRE(text[length] == '\xe8');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// break before last character: 3 trail bytes
							 | 
						||
| 
								 | 
							
										text = "Japanese\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e\xf0\x9f\x98\x8a";
							 | 
						||
| 
								 | 
							
										length = doc.document.SafeSegment(text);
							 | 
						||
| 
								 | 
							
										REQUIRE(text[length - 1] == '\x9e');
							 | 
						||
| 
								 | 
							
										REQUIRE(text[length] == '\xf0');
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									SECTION("DBCS Shift-JIS") {
							 | 
						||
| 
								 | 
							
										const DocPlus doc("", 932);
							 | 
						||
| 
								 | 
							
										// word and punctuation boundary in middle of text: single byte
							 | 
						||
| 
								 | 
							
										std::string_view text = "(IsBreakSpace(text[j]))";
							 | 
						||
| 
								 | 
							
										size_t length = doc.document.SafeSegment(text);
							 | 
						||
| 
								 | 
							
										REQUIRE(text[length - 1] == 'j');
							 | 
						||
| 
								 | 
							
										REQUIRE(text[length] == ']');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// word and punctuation boundary in middle of text: double byte
							 | 
						||
| 
								 | 
							
										text = "(IsBreakSpace(text[\x8c\xea]))";
							 | 
						||
| 
								 | 
							
										length = doc.document.SafeSegment(text);
							 | 
						||
| 
								 | 
							
										REQUIRE(text[length - 1] == '\xea');
							 | 
						||
| 
								 | 
							
										REQUIRE(text[length] == ']');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// word and punctuation boundary near start of text
							 | 
						||
| 
								 | 
							
										text = "(IsBreakSpace";
							 | 
						||
| 
								 | 
							
										length = doc.document.SafeSegment(text);
							 | 
						||
| 
								 | 
							
										REQUIRE(text[length - 1] == '(');
							 | 
						||
| 
								 | 
							
										REQUIRE(text[length] == 'I');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// word and punctuation boundary near end of text: single byte
							 | 
						||
| 
								 | 
							
										text = "IsBreakSpace)";
							 | 
						||
| 
								 | 
							
										length = doc.document.SafeSegment(text);
							 | 
						||
| 
								 | 
							
										REQUIRE(text[length - 1] == 'e');
							 | 
						||
| 
								 | 
							
										REQUIRE(text[length] == ')');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// word and punctuation boundary near end of text: double byte
							 | 
						||
| 
								 | 
							
										text = "IsBreakSpace\x8c\xea)";
							 | 
						||
| 
								 | 
							
										length = doc.document.SafeSegment(text);
							 | 
						||
| 
								 | 
							
										REQUIRE(text[length - 1] == '\xea');
							 | 
						||
| 
								 | 
							
										REQUIRE(text[length] == ')');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// break before last character: single byte
							 | 
						||
| 
								 | 
							
										text = "JapaneseJa";
							 | 
						||
| 
								 | 
							
										length = doc.document.SafeSegment(text);
							 | 
						||
| 
								 | 
							
										REQUIRE(text[length - 1] == 'J');
							 | 
						||
| 
								 | 
							
										REQUIRE(text[length] == 'a');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// break before last character: double byte
							 | 
						||
| 
								 | 
							
										text = "Japanese\x93\xfa\x96\x7b\x8c\xea";
							 | 
						||
| 
								 | 
							
										length = doc.document.SafeSegment(text);
							 | 
						||
| 
								 | 
							
										REQUIRE(text[length - 1] == '\x7b');
							 | 
						||
| 
								 | 
							
										REQUIRE(text[length] == '\x8c');
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								TEST_CASE("PerLine") {
							 | 
						||
| 
								 | 
							
									SECTION("LineMarkers") {
							 | 
						||
| 
								 | 
							
										DocPlus doc("1\n2\n", CpUtf8);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.document.LinesTotal() == 3);
							 | 
						||
| 
								 | 
							
										const int mh1 = doc.document.AddMark(0, 0);
							 | 
						||
| 
								 | 
							
										const int mh2 = doc.document.AddMark(1, 1);
							 | 
						||
| 
								 | 
							
										const int mh3 = doc.document.AddMark(2, 2);
							 | 
						||
| 
								 | 
							
										REQUIRE(mh1 != -1);
							 | 
						||
| 
								 | 
							
										REQUIRE(mh2 != -1);
							 | 
						||
| 
								 | 
							
										REQUIRE(mh3 != -1);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.document.AddMark(3, 3) == -1);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// delete first character, no change
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.document.CharAt(0) == '1');
							 | 
						||
| 
								 | 
							
										doc.document.DeleteChars(0, 1);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.document.LinesTotal() == 3);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.document.MarkerHandleFromLine(0, 0) == mh1);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.document.MarkerHandleFromLine(0, 1) == -1);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.document.MarkerHandleFromLine(1, 0) == mh2);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.document.MarkerHandleFromLine(1, 1) == -1);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// delete first line, so merged
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.document.CharAt(0) == '\n');
							 | 
						||
| 
								 | 
							
										doc.document.DeleteChars(0, 1);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.document.CharAt(0) == '2');
							 | 
						||
| 
								 | 
							
										const std::set handleSet {mh1, mh2};
							 | 
						||
| 
								 | 
							
										const int handle1 = doc.document.MarkerHandleFromLine(0, 0);
							 | 
						||
| 
								 | 
							
										const int handle2 = doc.document.MarkerHandleFromLine(0, 1);
							 | 
						||
| 
								 | 
							
										REQUIRE(handle1 != handle2);
							 | 
						||
| 
								 | 
							
										REQUIRE(handleSet.count(handle1) == 1);
							 | 
						||
| 
								 | 
							
										REQUIRE(handleSet.count(handle2) == 1);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.document.MarkerHandleFromLine(0, 2) == -1);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.document.MarkerHandleFromLine(1, 0) == mh3);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.document.MarkerHandleFromLine(1, 1) == -1);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									SECTION("LineAnnotation") {
							 | 
						||
| 
								 | 
							
										DocPlus doc("1\n2\n", CpUtf8);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.document.LinesTotal() == 3);
							 | 
						||
| 
								 | 
							
										Sci::Position length = doc.document.Length();
							 | 
						||
| 
								 | 
							
										doc.document.AnnotationSetText(0, "1");
							 | 
						||
| 
								 | 
							
										doc.document.AnnotationSetText(1, "1\n2");
							 | 
						||
| 
								 | 
							
										doc.document.AnnotationSetText(2, "1\n2\n3");
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.document.AnnotationLines(0) == 1);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.document.AnnotationLines(1) == 2);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.document.AnnotationLines(2) == 3);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.document.AnnotationLines(3) == 0);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// delete last line
							 | 
						||
| 
								 | 
							
										length -= 1;
							 | 
						||
| 
								 | 
							
										doc.document.DeleteChars(length, 1);
							 | 
						||
| 
								 | 
							
										// Deleting the last line moves its 3-line annotation to previous line,
							 | 
						||
| 
								 | 
							
										// deleting the 2-line annotation of the previous line.
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.document.LinesTotal() == 2);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.document.AnnotationLines(0) == 1);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.document.AnnotationLines(1) == 3);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.document.AnnotationLines(2) == 0);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// delete last character, no change
							 | 
						||
| 
								 | 
							
										length -= 1;
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.document.CharAt(length) == '2');
							 | 
						||
| 
								 | 
							
										doc.document.DeleteChars(length, 1);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.document.LinesTotal() == 2);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.document.AnnotationLines(0) == 1);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.document.AnnotationLines(1) == 3);
							 | 
						||
| 
								 | 
							
										REQUIRE(doc.document.AnnotationLines(2) == 0);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 |