xref: /MusicPlayer2/scintilla/src/Document.cxx (revision 8af74909132ed5e696cb05b6689ae4baf14c1c96)
1*8af74909SZhong Yang // Scintilla source code edit control
2*8af74909SZhong Yang /** @file Document.cxx
3*8af74909SZhong Yang  ** Text document that handles notifications, DBCS, styling, words and end of line.
4*8af74909SZhong Yang  **/
5*8af74909SZhong Yang // Copyright 1998-2011 by Neil Hodgson <[email protected]>
6*8af74909SZhong Yang // The License.txt file describes the conditions under which this software may be distributed.
7*8af74909SZhong Yang 
8*8af74909SZhong Yang #include <cstddef>
9*8af74909SZhong Yang #include <cstdlib>
10*8af74909SZhong Yang #include <cassert>
11*8af74909SZhong Yang #include <cstring>
12*8af74909SZhong Yang #include <cstdio>
13*8af74909SZhong Yang #include <cmath>
14*8af74909SZhong Yang 
15*8af74909SZhong Yang #include <stdexcept>
16*8af74909SZhong Yang #include <string>
17*8af74909SZhong Yang #include <string_view>
18*8af74909SZhong Yang #include <vector>
19*8af74909SZhong Yang #include <forward_list>
20*8af74909SZhong Yang #include <algorithm>
21*8af74909SZhong Yang #include <memory>
22*8af74909SZhong Yang #include <chrono>
23*8af74909SZhong Yang 
24*8af74909SZhong Yang #ifndef NO_CXX11_REGEX
25*8af74909SZhong Yang #include <regex>
26*8af74909SZhong Yang #endif
27*8af74909SZhong Yang 
28*8af74909SZhong Yang #include "Platform.h"
29*8af74909SZhong Yang 
30*8af74909SZhong Yang #include "ILoader.h"
31*8af74909SZhong Yang #include "ILexer.h"
32*8af74909SZhong Yang #include "Scintilla.h"
33*8af74909SZhong Yang 
34*8af74909SZhong Yang #include "CharacterSet.h"
35*8af74909SZhong Yang #include "CharacterCategory.h"
36*8af74909SZhong Yang #include "Position.h"
37*8af74909SZhong Yang #include "SplitVector.h"
38*8af74909SZhong Yang #include "Partitioning.h"
39*8af74909SZhong Yang #include "RunStyles.h"
40*8af74909SZhong Yang #include "CellBuffer.h"
41*8af74909SZhong Yang #include "PerLine.h"
42*8af74909SZhong Yang #include "CharClassify.h"
43*8af74909SZhong Yang #include "Decoration.h"
44*8af74909SZhong Yang #include "CaseFolder.h"
45*8af74909SZhong Yang #include "Document.h"
46*8af74909SZhong Yang #include "RESearch.h"
47*8af74909SZhong Yang #include "UniConversion.h"
48*8af74909SZhong Yang #include "ElapsedPeriod.h"
49*8af74909SZhong Yang 
50*8af74909SZhong Yang using namespace Scintilla;
51*8af74909SZhong Yang 
Colourise(Sci::Position start,Sci::Position end)52*8af74909SZhong Yang void LexInterface::Colourise(Sci::Position start, Sci::Position end) {
53*8af74909SZhong Yang 	if (pdoc && instance && !performingStyle) {
54*8af74909SZhong Yang 		// Protect against reentrance, which may occur, for example, when
55*8af74909SZhong Yang 		// fold points are discovered while performing styling and the folding
56*8af74909SZhong Yang 		// code looks for child lines which may trigger styling.
57*8af74909SZhong Yang 		performingStyle = true;
58*8af74909SZhong Yang 
59*8af74909SZhong Yang 		const Sci::Position lengthDoc = pdoc->Length();
60*8af74909SZhong Yang 		if (end == -1)
61*8af74909SZhong Yang 			end = lengthDoc;
62*8af74909SZhong Yang 		const Sci::Position len = end - start;
63*8af74909SZhong Yang 
64*8af74909SZhong Yang 		PLATFORM_ASSERT(len >= 0);
65*8af74909SZhong Yang 		PLATFORM_ASSERT(start + len <= lengthDoc);
66*8af74909SZhong Yang 
67*8af74909SZhong Yang 		int styleStart = 0;
68*8af74909SZhong Yang 		if (start > 0)
69*8af74909SZhong Yang 			styleStart = pdoc->StyleAt(start - 1);
70*8af74909SZhong Yang 
71*8af74909SZhong Yang 		if (len > 0) {
72*8af74909SZhong Yang 			instance->Lex(start, len, styleStart, pdoc);
73*8af74909SZhong Yang 			instance->Fold(start, len, styleStart, pdoc);
74*8af74909SZhong Yang 		}
75*8af74909SZhong Yang 
76*8af74909SZhong Yang 		performingStyle = false;
77*8af74909SZhong Yang 	}
78*8af74909SZhong Yang }
79*8af74909SZhong Yang 
LineEndTypesSupported()80*8af74909SZhong Yang int LexInterface::LineEndTypesSupported() {
81*8af74909SZhong Yang 	if (instance) {
82*8af74909SZhong Yang 		return instance->LineEndTypesSupported();
83*8af74909SZhong Yang 	}
84*8af74909SZhong Yang 	return 0;
85*8af74909SZhong Yang }
86*8af74909SZhong Yang 
ActionDuration(double duration_,double minDuration_,double maxDuration_)87*8af74909SZhong Yang ActionDuration::ActionDuration(double duration_, double minDuration_, double maxDuration_) noexcept :
88*8af74909SZhong Yang 	duration(duration_), minDuration(minDuration_), maxDuration(maxDuration_) {
89*8af74909SZhong Yang }
90*8af74909SZhong Yang 
AddSample(size_t numberActions,double durationOfActions)91*8af74909SZhong Yang void ActionDuration::AddSample(size_t numberActions, double durationOfActions) noexcept {
92*8af74909SZhong Yang 	// Only adjust for multiple actions to avoid instability
93*8af74909SZhong Yang 	if (numberActions < 8)
94*8af74909SZhong Yang 		return;
95*8af74909SZhong Yang 
96*8af74909SZhong Yang 	// Alpha value for exponential smoothing.
97*8af74909SZhong Yang 	// Most recent value contributes 25% to smoothed value.
98*8af74909SZhong Yang 	constexpr double alpha = 0.25;
99*8af74909SZhong Yang 
100*8af74909SZhong Yang 	const double durationOne = durationOfActions / numberActions;
101*8af74909SZhong Yang 	duration = std::clamp(alpha * durationOne + (1.0 - alpha) * duration,
102*8af74909SZhong Yang 		minDuration, maxDuration);
103*8af74909SZhong Yang }
104*8af74909SZhong Yang 
Duration() const105*8af74909SZhong Yang double ActionDuration::Duration() const noexcept {
106*8af74909SZhong Yang 	return duration;
107*8af74909SZhong Yang }
108*8af74909SZhong Yang 
Document(int options)109*8af74909SZhong Yang Document::Document(int options) :
110*8af74909SZhong Yang 	cb((options & SC_DOCUMENTOPTION_STYLES_NONE) == 0, (options & SC_DOCUMENTOPTION_TEXT_LARGE) != 0),
111*8af74909SZhong Yang 	durationStyleOneLine(0.00001, 0.000001, 0.0001) {
112*8af74909SZhong Yang 	refCount = 0;
113*8af74909SZhong Yang #ifdef _WIN32
114*8af74909SZhong Yang 	eolMode = SC_EOL_CRLF;
115*8af74909SZhong Yang #else
116*8af74909SZhong Yang 	eolMode = SC_EOL_LF;
117*8af74909SZhong Yang #endif
118*8af74909SZhong Yang 	dbcsCodePage = SC_CP_UTF8;
119*8af74909SZhong Yang 	lineEndBitSet = SC_LINE_END_TYPE_DEFAULT;
120*8af74909SZhong Yang 	endStyled = 0;
121*8af74909SZhong Yang 	styleClock = 0;
122*8af74909SZhong Yang 	enteredModification = 0;
123*8af74909SZhong Yang 	enteredStyling = 0;
124*8af74909SZhong Yang 	enteredReadOnlyCount = 0;
125*8af74909SZhong Yang 	insertionSet = false;
126*8af74909SZhong Yang 	tabInChars = 8;
127*8af74909SZhong Yang 	indentInChars = 0;
128*8af74909SZhong Yang 	actualIndentInChars = 8;
129*8af74909SZhong Yang 	useTabs = true;
130*8af74909SZhong Yang 	tabIndents = true;
131*8af74909SZhong Yang 	backspaceUnindents = false;
132*8af74909SZhong Yang 
133*8af74909SZhong Yang 	matchesValid = false;
134*8af74909SZhong Yang 
135*8af74909SZhong Yang 	perLineData[ldMarkers] = std::make_unique<LineMarkers>();
136*8af74909SZhong Yang 	perLineData[ldLevels] = std::make_unique<LineLevels>();
137*8af74909SZhong Yang 	perLineData[ldState] = std::make_unique<LineState>();
138*8af74909SZhong Yang 	perLineData[ldMargin] = std::make_unique<LineAnnotation>();
139*8af74909SZhong Yang 	perLineData[ldAnnotation] = std::make_unique<LineAnnotation>();
140*8af74909SZhong Yang 	perLineData[ldEOLAnnotation] = std::make_unique<LineAnnotation>();
141*8af74909SZhong Yang 
142*8af74909SZhong Yang 	decorations = DecorationListCreate(IsLarge());
143*8af74909SZhong Yang 
144*8af74909SZhong Yang 	cb.SetPerLine(this);
145*8af74909SZhong Yang 	cb.SetUTF8Substance(SC_CP_UTF8 == dbcsCodePage);
146*8af74909SZhong Yang }
147*8af74909SZhong Yang 
~Document()148*8af74909SZhong Yang Document::~Document() {
149*8af74909SZhong Yang 	for (const WatcherWithUserData &watcher : watchers) {
150*8af74909SZhong Yang 		watcher.watcher->NotifyDeleted(this, watcher.userData);
151*8af74909SZhong Yang 	}
152*8af74909SZhong Yang }
153*8af74909SZhong Yang 
154*8af74909SZhong Yang // Increase reference count and return its previous value.
AddRef()155*8af74909SZhong Yang int Document::AddRef() {
156*8af74909SZhong Yang 	return refCount++;
157*8af74909SZhong Yang }
158*8af74909SZhong Yang 
159*8af74909SZhong Yang // Decrease reference count and return its previous value.
160*8af74909SZhong Yang // Delete the document if reference count reaches zero.
Release()161*8af74909SZhong Yang int SCI_METHOD Document::Release() {
162*8af74909SZhong Yang 	const int curRefCount = --refCount;
163*8af74909SZhong Yang 	if (curRefCount == 0)
164*8af74909SZhong Yang 		delete this;
165*8af74909SZhong Yang 	return curRefCount;
166*8af74909SZhong Yang }
167*8af74909SZhong Yang 
Init()168*8af74909SZhong Yang void Document::Init() {
169*8af74909SZhong Yang 	for (const std::unique_ptr<PerLine> &pl : perLineData) {
170*8af74909SZhong Yang 		if (pl)
171*8af74909SZhong Yang 			pl->Init();
172*8af74909SZhong Yang 	}
173*8af74909SZhong Yang }
174*8af74909SZhong Yang 
InsertLine(Sci::Line line)175*8af74909SZhong Yang void Document::InsertLine(Sci::Line line) {
176*8af74909SZhong Yang 	for (const std::unique_ptr<PerLine> &pl : perLineData) {
177*8af74909SZhong Yang 		if (pl)
178*8af74909SZhong Yang 			pl->InsertLine(line);
179*8af74909SZhong Yang 	}
180*8af74909SZhong Yang }
181*8af74909SZhong Yang 
InsertLines(Sci::Line line,Sci::Line lines)182*8af74909SZhong Yang void Document::InsertLines(Sci::Line line, Sci::Line lines) {
183*8af74909SZhong Yang 	for (const auto &pl : perLineData) {
184*8af74909SZhong Yang 		if (pl)
185*8af74909SZhong Yang 			pl->InsertLines(line, lines);
186*8af74909SZhong Yang 	}
187*8af74909SZhong Yang }
188*8af74909SZhong Yang 
RemoveLine(Sci::Line line)189*8af74909SZhong Yang void Document::RemoveLine(Sci::Line line) {
190*8af74909SZhong Yang 	for (const std::unique_ptr<PerLine> &pl : perLineData) {
191*8af74909SZhong Yang 		if (pl)
192*8af74909SZhong Yang 			pl->RemoveLine(line);
193*8af74909SZhong Yang 	}
194*8af74909SZhong Yang }
195*8af74909SZhong Yang 
Markers() const196*8af74909SZhong Yang LineMarkers *Document::Markers() const noexcept {
197*8af74909SZhong Yang 	return dynamic_cast<LineMarkers *>(perLineData[ldMarkers].get());
198*8af74909SZhong Yang }
199*8af74909SZhong Yang 
Levels() const200*8af74909SZhong Yang LineLevels *Document::Levels() const noexcept {
201*8af74909SZhong Yang 	return dynamic_cast<LineLevels *>(perLineData[ldLevels].get());
202*8af74909SZhong Yang }
203*8af74909SZhong Yang 
States() const204*8af74909SZhong Yang LineState *Document::States() const noexcept {
205*8af74909SZhong Yang 	return dynamic_cast<LineState *>(perLineData[ldState].get());
206*8af74909SZhong Yang }
207*8af74909SZhong Yang 
Margins() const208*8af74909SZhong Yang LineAnnotation *Document::Margins() const noexcept {
209*8af74909SZhong Yang 	return dynamic_cast<LineAnnotation *>(perLineData[ldMargin].get());
210*8af74909SZhong Yang }
211*8af74909SZhong Yang 
Annotations() const212*8af74909SZhong Yang LineAnnotation *Document::Annotations() const noexcept {
213*8af74909SZhong Yang 	return dynamic_cast<LineAnnotation *>(perLineData[ldAnnotation].get());
214*8af74909SZhong Yang }
215*8af74909SZhong Yang 
EOLAnnotations() const216*8af74909SZhong Yang LineAnnotation *Document::EOLAnnotations() const noexcept {
217*8af74909SZhong Yang 	return dynamic_cast<LineAnnotation *>(perLineData[ldEOLAnnotation].get());
218*8af74909SZhong Yang }
219*8af74909SZhong Yang 
LineEndTypesSupported() const220*8af74909SZhong Yang int Document::LineEndTypesSupported() const {
221*8af74909SZhong Yang 	if ((SC_CP_UTF8 == dbcsCodePage) && pli)
222*8af74909SZhong Yang 		return pli->LineEndTypesSupported();
223*8af74909SZhong Yang 	else
224*8af74909SZhong Yang 		return 0;
225*8af74909SZhong Yang }
226*8af74909SZhong Yang 
SetDBCSCodePage(int dbcsCodePage_)227*8af74909SZhong Yang bool Document::SetDBCSCodePage(int dbcsCodePage_) {
228*8af74909SZhong Yang 	if (dbcsCodePage != dbcsCodePage_) {
229*8af74909SZhong Yang 		dbcsCodePage = dbcsCodePage_;
230*8af74909SZhong Yang 		SetCaseFolder(nullptr);
231*8af74909SZhong Yang 		cb.SetLineEndTypes(lineEndBitSet & LineEndTypesSupported());
232*8af74909SZhong Yang 		cb.SetUTF8Substance(SC_CP_UTF8 == dbcsCodePage);
233*8af74909SZhong Yang 		ModifiedAt(0);	// Need to restyle whole document
234*8af74909SZhong Yang 		return true;
235*8af74909SZhong Yang 	} else {
236*8af74909SZhong Yang 		return false;
237*8af74909SZhong Yang 	}
238*8af74909SZhong Yang }
239*8af74909SZhong Yang 
SetLineEndTypesAllowed(int lineEndBitSet_)240*8af74909SZhong Yang bool Document::SetLineEndTypesAllowed(int lineEndBitSet_) {
241*8af74909SZhong Yang 	if (lineEndBitSet != lineEndBitSet_) {
242*8af74909SZhong Yang 		lineEndBitSet = lineEndBitSet_;
243*8af74909SZhong Yang 		const int lineEndBitSetActive = lineEndBitSet & LineEndTypesSupported();
244*8af74909SZhong Yang 		if (lineEndBitSetActive != cb.GetLineEndTypes()) {
245*8af74909SZhong Yang 			ModifiedAt(0);
246*8af74909SZhong Yang 			cb.SetLineEndTypes(lineEndBitSetActive);
247*8af74909SZhong Yang 			return true;
248*8af74909SZhong Yang 		} else {
249*8af74909SZhong Yang 			return false;
250*8af74909SZhong Yang 		}
251*8af74909SZhong Yang 	} else {
252*8af74909SZhong Yang 		return false;
253*8af74909SZhong Yang 	}
254*8af74909SZhong Yang }
255*8af74909SZhong Yang 
SetSavePoint()256*8af74909SZhong Yang void Document::SetSavePoint() {
257*8af74909SZhong Yang 	cb.SetSavePoint();
258*8af74909SZhong Yang 	NotifySavePoint(true);
259*8af74909SZhong Yang }
260*8af74909SZhong Yang 
TentativeUndo()261*8af74909SZhong Yang void Document::TentativeUndo() {
262*8af74909SZhong Yang 	if (!TentativeActive())
263*8af74909SZhong Yang 		return;
264*8af74909SZhong Yang 	CheckReadOnly();
265*8af74909SZhong Yang 	if (enteredModification == 0) {
266*8af74909SZhong Yang 		enteredModification++;
267*8af74909SZhong Yang 		if (!cb.IsReadOnly()) {
268*8af74909SZhong Yang 			const bool startSavePoint = cb.IsSavePoint();
269*8af74909SZhong Yang 			bool multiLine = false;
270*8af74909SZhong Yang 			const int steps = cb.TentativeSteps();
271*8af74909SZhong Yang 			//Platform::DebugPrintf("Steps=%d\n", steps);
272*8af74909SZhong Yang 			for (int step = 0; step < steps; step++) {
273*8af74909SZhong Yang 				const Sci::Line prevLinesTotal = LinesTotal();
274*8af74909SZhong Yang 				const Action &action = cb.GetUndoStep();
275*8af74909SZhong Yang 				if (action.at == removeAction) {
276*8af74909SZhong Yang 					NotifyModified(DocModification(
277*8af74909SZhong Yang 									SC_MOD_BEFOREINSERT | SC_PERFORMED_UNDO, action));
278*8af74909SZhong Yang 				} else if (action.at == containerAction) {
279*8af74909SZhong Yang 					DocModification dm(SC_MOD_CONTAINER | SC_PERFORMED_UNDO);
280*8af74909SZhong Yang 					dm.token = action.position;
281*8af74909SZhong Yang 					NotifyModified(dm);
282*8af74909SZhong Yang 				} else {
283*8af74909SZhong Yang 					NotifyModified(DocModification(
284*8af74909SZhong Yang 									SC_MOD_BEFOREDELETE | SC_PERFORMED_UNDO, action));
285*8af74909SZhong Yang 				}
286*8af74909SZhong Yang 				cb.PerformUndoStep();
287*8af74909SZhong Yang 				if (action.at != containerAction) {
288*8af74909SZhong Yang 					ModifiedAt(action.position);
289*8af74909SZhong Yang 				}
290*8af74909SZhong Yang 
291*8af74909SZhong Yang 				int modFlags = SC_PERFORMED_UNDO;
292*8af74909SZhong Yang 				// With undo, an insertion action becomes a deletion notification
293*8af74909SZhong Yang 				if (action.at == removeAction) {
294*8af74909SZhong Yang 					modFlags |= SC_MOD_INSERTTEXT;
295*8af74909SZhong Yang 				} else if (action.at == insertAction) {
296*8af74909SZhong Yang 					modFlags |= SC_MOD_DELETETEXT;
297*8af74909SZhong Yang 				}
298*8af74909SZhong Yang 				if (steps > 1)
299*8af74909SZhong Yang 					modFlags |= SC_MULTISTEPUNDOREDO;
300*8af74909SZhong Yang 				const Sci::Line linesAdded = LinesTotal() - prevLinesTotal;
301*8af74909SZhong Yang 				if (linesAdded != 0)
302*8af74909SZhong Yang 					multiLine = true;
303*8af74909SZhong Yang 				if (step == steps - 1) {
304*8af74909SZhong Yang 					modFlags |= SC_LASTSTEPINUNDOREDO;
305*8af74909SZhong Yang 					if (multiLine)
306*8af74909SZhong Yang 						modFlags |= SC_MULTILINEUNDOREDO;
307*8af74909SZhong Yang 				}
308*8af74909SZhong Yang 				NotifyModified(DocModification(modFlags, action.position, action.lenData,
309*8af74909SZhong Yang 											   linesAdded, action.data.get()));
310*8af74909SZhong Yang 			}
311*8af74909SZhong Yang 
312*8af74909SZhong Yang 			const bool endSavePoint = cb.IsSavePoint();
313*8af74909SZhong Yang 			if (startSavePoint != endSavePoint)
314*8af74909SZhong Yang 				NotifySavePoint(endSavePoint);
315*8af74909SZhong Yang 
316*8af74909SZhong Yang 			cb.TentativeCommit();
317*8af74909SZhong Yang 		}
318*8af74909SZhong Yang 		enteredModification--;
319*8af74909SZhong Yang 	}
320*8af74909SZhong Yang }
321*8af74909SZhong Yang 
GetMark(Sci::Line line) const322*8af74909SZhong Yang int Document::GetMark(Sci::Line line) const noexcept {
323*8af74909SZhong Yang 	return Markers()->MarkValue(line);
324*8af74909SZhong Yang }
325*8af74909SZhong Yang 
MarkerNext(Sci::Line lineStart,int mask) const326*8af74909SZhong Yang Sci::Line Document::MarkerNext(Sci::Line lineStart, int mask) const noexcept {
327*8af74909SZhong Yang 	return Markers()->MarkerNext(lineStart, mask);
328*8af74909SZhong Yang }
329*8af74909SZhong Yang 
AddMark(Sci::Line line,int markerNum)330*8af74909SZhong Yang int Document::AddMark(Sci::Line line, int markerNum) {
331*8af74909SZhong Yang 	if (line >= 0 && line <= LinesTotal()) {
332*8af74909SZhong Yang 		const int prev = Markers()->AddMark(line, markerNum, LinesTotal());
333*8af74909SZhong Yang 		const DocModification mh(SC_MOD_CHANGEMARKER, LineStart(line), 0, 0, nullptr, line);
334*8af74909SZhong Yang 		NotifyModified(mh);
335*8af74909SZhong Yang 		return prev;
336*8af74909SZhong Yang 	} else {
337*8af74909SZhong Yang 		return -1;
338*8af74909SZhong Yang 	}
339*8af74909SZhong Yang }
340*8af74909SZhong Yang 
AddMarkSet(Sci::Line line,int valueSet)341*8af74909SZhong Yang void Document::AddMarkSet(Sci::Line line, int valueSet) {
342*8af74909SZhong Yang 	if (line < 0 || line > LinesTotal()) {
343*8af74909SZhong Yang 		return;
344*8af74909SZhong Yang 	}
345*8af74909SZhong Yang 	unsigned int m = valueSet;
346*8af74909SZhong Yang 	for (int i = 0; m; i++, m >>= 1) {
347*8af74909SZhong Yang 		if (m & 1)
348*8af74909SZhong Yang 			Markers()->AddMark(line, i, LinesTotal());
349*8af74909SZhong Yang 	}
350*8af74909SZhong Yang 	const DocModification mh(SC_MOD_CHANGEMARKER, LineStart(line), 0, 0, nullptr, line);
351*8af74909SZhong Yang 	NotifyModified(mh);
352*8af74909SZhong Yang }
353*8af74909SZhong Yang 
DeleteMark(Sci::Line line,int markerNum)354*8af74909SZhong Yang void Document::DeleteMark(Sci::Line line, int markerNum) {
355*8af74909SZhong Yang 	Markers()->DeleteMark(line, markerNum, false);
356*8af74909SZhong Yang 	const DocModification mh(SC_MOD_CHANGEMARKER, LineStart(line), 0, 0, nullptr, line);
357*8af74909SZhong Yang 	NotifyModified(mh);
358*8af74909SZhong Yang }
359*8af74909SZhong Yang 
DeleteMarkFromHandle(int markerHandle)360*8af74909SZhong Yang void Document::DeleteMarkFromHandle(int markerHandle) {
361*8af74909SZhong Yang 	Markers()->DeleteMarkFromHandle(markerHandle);
362*8af74909SZhong Yang 	DocModification mh(SC_MOD_CHANGEMARKER);
363*8af74909SZhong Yang 	mh.line = -1;
364*8af74909SZhong Yang 	NotifyModified(mh);
365*8af74909SZhong Yang }
366*8af74909SZhong Yang 
DeleteAllMarks(int markerNum)367*8af74909SZhong Yang void Document::DeleteAllMarks(int markerNum) {
368*8af74909SZhong Yang 	bool someChanges = false;
369*8af74909SZhong Yang 	for (Sci::Line line = 0; line < LinesTotal(); line++) {
370*8af74909SZhong Yang 		if (Markers()->DeleteMark(line, markerNum, true))
371*8af74909SZhong Yang 			someChanges = true;
372*8af74909SZhong Yang 	}
373*8af74909SZhong Yang 	if (someChanges) {
374*8af74909SZhong Yang 		DocModification mh(SC_MOD_CHANGEMARKER);
375*8af74909SZhong Yang 		mh.line = -1;
376*8af74909SZhong Yang 		NotifyModified(mh);
377*8af74909SZhong Yang 	}
378*8af74909SZhong Yang }
379*8af74909SZhong Yang 
LineFromHandle(int markerHandle) const380*8af74909SZhong Yang Sci::Line Document::LineFromHandle(int markerHandle) const noexcept {
381*8af74909SZhong Yang 	return Markers()->LineFromHandle(markerHandle);
382*8af74909SZhong Yang }
383*8af74909SZhong Yang 
MarkerNumberFromLine(Sci::Line line,int which) const384*8af74909SZhong Yang int Document::MarkerNumberFromLine(Sci::Line line, int which) const noexcept {
385*8af74909SZhong Yang 	return Markers()->NumberFromLine(line, which);
386*8af74909SZhong Yang }
387*8af74909SZhong Yang 
MarkerHandleFromLine(Sci::Line line,int which) const388*8af74909SZhong Yang int Document::MarkerHandleFromLine(Sci::Line line, int which) const noexcept {
389*8af74909SZhong Yang 	return Markers()->HandleFromLine(line, which);
390*8af74909SZhong Yang }
391*8af74909SZhong Yang 
LineStart(Sci_Position line) const392*8af74909SZhong Yang Sci_Position SCI_METHOD Document::LineStart(Sci_Position line) const {
393*8af74909SZhong Yang 	return cb.LineStart(line);
394*8af74909SZhong Yang }
395*8af74909SZhong Yang 
IsLineStartPosition(Sci::Position position) const396*8af74909SZhong Yang bool Document::IsLineStartPosition(Sci::Position position) const {
397*8af74909SZhong Yang 	return LineStart(LineFromPosition(position)) == position;
398*8af74909SZhong Yang }
399*8af74909SZhong Yang 
LineEnd(Sci_Position line) const400*8af74909SZhong Yang Sci_Position SCI_METHOD Document::LineEnd(Sci_Position line) const {
401*8af74909SZhong Yang 	if (line >= LinesTotal() - 1) {
402*8af74909SZhong Yang 		return LineStart(line + 1);
403*8af74909SZhong Yang 	} else {
404*8af74909SZhong Yang 		Sci::Position position = LineStart(line + 1);
405*8af74909SZhong Yang 		if (SC_LINE_END_TYPE_UNICODE == cb.GetLineEndTypes()) {
406*8af74909SZhong Yang 			const unsigned char bytes[] = {
407*8af74909SZhong Yang 				cb.UCharAt(position-3),
408*8af74909SZhong Yang 				cb.UCharAt(position-2),
409*8af74909SZhong Yang 				cb.UCharAt(position-1),
410*8af74909SZhong Yang 			};
411*8af74909SZhong Yang 			if (UTF8IsSeparator(bytes)) {
412*8af74909SZhong Yang 				return position - UTF8SeparatorLength;
413*8af74909SZhong Yang 			}
414*8af74909SZhong Yang 			if (UTF8IsNEL(bytes+1)) {
415*8af74909SZhong Yang 				return position - UTF8NELLength;
416*8af74909SZhong Yang 			}
417*8af74909SZhong Yang 		}
418*8af74909SZhong Yang 		position--; // Back over CR or LF
419*8af74909SZhong Yang 		// When line terminator is CR+LF, may need to go back one more
420*8af74909SZhong Yang 		if ((position > LineStart(line)) && (cb.CharAt(position - 1) == '\r')) {
421*8af74909SZhong Yang 			position--;
422*8af74909SZhong Yang 		}
423*8af74909SZhong Yang 		return position;
424*8af74909SZhong Yang 	}
425*8af74909SZhong Yang }
426*8af74909SZhong Yang 
SetErrorStatus(int status)427*8af74909SZhong Yang void SCI_METHOD Document::SetErrorStatus(int status) {
428*8af74909SZhong Yang 	// Tell the watchers an error has occurred.
429*8af74909SZhong Yang 	for (const WatcherWithUserData &watcher : watchers) {
430*8af74909SZhong Yang 		watcher.watcher->NotifyErrorOccurred(this, watcher.userData, status);
431*8af74909SZhong Yang 	}
432*8af74909SZhong Yang }
433*8af74909SZhong Yang 
LineFromPosition(Sci_Position pos) const434*8af74909SZhong Yang Sci_Position SCI_METHOD Document::LineFromPosition(Sci_Position pos) const {
435*8af74909SZhong Yang 	return cb.LineFromPosition(pos);
436*8af74909SZhong Yang }
437*8af74909SZhong Yang 
SciLineFromPosition(Sci::Position pos) const438*8af74909SZhong Yang Sci::Line Document::SciLineFromPosition(Sci::Position pos) const noexcept {
439*8af74909SZhong Yang 	// Avoids casting in callers for this very common function
440*8af74909SZhong Yang 	return cb.LineFromPosition(pos);
441*8af74909SZhong Yang }
442*8af74909SZhong Yang 
LineEndPosition(Sci::Position position) const443*8af74909SZhong Yang Sci::Position Document::LineEndPosition(Sci::Position position) const {
444*8af74909SZhong Yang 	return LineEnd(LineFromPosition(position));
445*8af74909SZhong Yang }
446*8af74909SZhong Yang 
IsLineEndPosition(Sci::Position position) const447*8af74909SZhong Yang bool Document::IsLineEndPosition(Sci::Position position) const {
448*8af74909SZhong Yang 	return LineEnd(LineFromPosition(position)) == position;
449*8af74909SZhong Yang }
450*8af74909SZhong Yang 
IsPositionInLineEnd(Sci::Position position) const451*8af74909SZhong Yang bool Document::IsPositionInLineEnd(Sci::Position position) const {
452*8af74909SZhong Yang 	return position >= LineEnd(LineFromPosition(position));
453*8af74909SZhong Yang }
454*8af74909SZhong Yang 
VCHomePosition(Sci::Position position) const455*8af74909SZhong Yang Sci::Position Document::VCHomePosition(Sci::Position position) const {
456*8af74909SZhong Yang 	const Sci::Line line = SciLineFromPosition(position);
457*8af74909SZhong Yang 	const Sci::Position startPosition = LineStart(line);
458*8af74909SZhong Yang 	const Sci::Position endLine = LineEnd(line);
459*8af74909SZhong Yang 	Sci::Position startText = startPosition;
460*8af74909SZhong Yang 	while (startText < endLine && (cb.CharAt(startText) == ' ' || cb.CharAt(startText) == '\t'))
461*8af74909SZhong Yang 		startText++;
462*8af74909SZhong Yang 	if (position == startText)
463*8af74909SZhong Yang 		return startPosition;
464*8af74909SZhong Yang 	else
465*8af74909SZhong Yang 		return startText;
466*8af74909SZhong Yang }
467*8af74909SZhong Yang 
IndexLineStart(Sci::Line line,int lineCharacterIndex) const468*8af74909SZhong Yang Sci::Position Document::IndexLineStart(Sci::Line line, int lineCharacterIndex) const noexcept {
469*8af74909SZhong Yang 	return cb.IndexLineStart(line, lineCharacterIndex);
470*8af74909SZhong Yang }
471*8af74909SZhong Yang 
LineFromPositionIndex(Sci::Position pos,int lineCharacterIndex) const472*8af74909SZhong Yang Sci::Line Document::LineFromPositionIndex(Sci::Position pos, int lineCharacterIndex) const noexcept {
473*8af74909SZhong Yang 	return cb.LineFromPositionIndex(pos, lineCharacterIndex);
474*8af74909SZhong Yang }
475*8af74909SZhong Yang 
SetLevel(Sci_Position line,int level)476*8af74909SZhong Yang int SCI_METHOD Document::SetLevel(Sci_Position line, int level) {
477*8af74909SZhong Yang 	const int prev = Levels()->SetLevel(line, level, LinesTotal());
478*8af74909SZhong Yang 	if (prev != level) {
479*8af74909SZhong Yang 		DocModification mh(SC_MOD_CHANGEFOLD | SC_MOD_CHANGEMARKER,
480*8af74909SZhong Yang 		                   LineStart(line), 0, 0, nullptr, line);
481*8af74909SZhong Yang 		mh.foldLevelNow = level;
482*8af74909SZhong Yang 		mh.foldLevelPrev = prev;
483*8af74909SZhong Yang 		NotifyModified(mh);
484*8af74909SZhong Yang 	}
485*8af74909SZhong Yang 	return prev;
486*8af74909SZhong Yang }
487*8af74909SZhong Yang 
GetLevel(Sci_Position line) const488*8af74909SZhong Yang int SCI_METHOD Document::GetLevel(Sci_Position line) const {
489*8af74909SZhong Yang 	return Levels()->GetLevel(line);
490*8af74909SZhong Yang }
491*8af74909SZhong Yang 
ClearLevels()492*8af74909SZhong Yang void Document::ClearLevels() {
493*8af74909SZhong Yang 	Levels()->ClearLevels();
494*8af74909SZhong Yang }
495*8af74909SZhong Yang 
IsSubordinate(int levelStart,int levelTry)496*8af74909SZhong Yang static bool IsSubordinate(int levelStart, int levelTry) noexcept {
497*8af74909SZhong Yang 	if (levelTry & SC_FOLDLEVELWHITEFLAG)
498*8af74909SZhong Yang 		return true;
499*8af74909SZhong Yang 	else
500*8af74909SZhong Yang 		return LevelNumber(levelStart) < LevelNumber(levelTry);
501*8af74909SZhong Yang }
502*8af74909SZhong Yang 
GetLastChild(Sci::Line lineParent,int level,Sci::Line lastLine)503*8af74909SZhong Yang Sci::Line Document::GetLastChild(Sci::Line lineParent, int level, Sci::Line lastLine) {
504*8af74909SZhong Yang 	if (level == -1)
505*8af74909SZhong Yang 		level = LevelNumber(GetLevel(lineParent));
506*8af74909SZhong Yang 	const Sci::Line maxLine = LinesTotal();
507*8af74909SZhong Yang 	const Sci::Line lookLastLine = (lastLine != -1) ? std::min(LinesTotal() - 1, lastLine) : -1;
508*8af74909SZhong Yang 	Sci::Line lineMaxSubord = lineParent;
509*8af74909SZhong Yang 	while (lineMaxSubord < maxLine - 1) {
510*8af74909SZhong Yang 		EnsureStyledTo(LineStart(lineMaxSubord + 2));
511*8af74909SZhong Yang 		if (!IsSubordinate(level, GetLevel(lineMaxSubord + 1)))
512*8af74909SZhong Yang 			break;
513*8af74909SZhong Yang 		if ((lookLastLine != -1) && (lineMaxSubord >= lookLastLine) && !(GetLevel(lineMaxSubord) & SC_FOLDLEVELWHITEFLAG))
514*8af74909SZhong Yang 			break;
515*8af74909SZhong Yang 		lineMaxSubord++;
516*8af74909SZhong Yang 	}
517*8af74909SZhong Yang 	if (lineMaxSubord > lineParent) {
518*8af74909SZhong Yang 		if (level > LevelNumber(GetLevel(lineMaxSubord + 1))) {
519*8af74909SZhong Yang 			// Have chewed up some whitespace that belongs to a parent so seek back
520*8af74909SZhong Yang 			if (GetLevel(lineMaxSubord) & SC_FOLDLEVELWHITEFLAG) {
521*8af74909SZhong Yang 				lineMaxSubord--;
522*8af74909SZhong Yang 			}
523*8af74909SZhong Yang 		}
524*8af74909SZhong Yang 	}
525*8af74909SZhong Yang 	return lineMaxSubord;
526*8af74909SZhong Yang }
527*8af74909SZhong Yang 
GetFoldParent(Sci::Line line) const528*8af74909SZhong Yang Sci::Line Document::GetFoldParent(Sci::Line line) const {
529*8af74909SZhong Yang 	const int level = LevelNumber(GetLevel(line));
530*8af74909SZhong Yang 	Sci::Line lineLook = line - 1;
531*8af74909SZhong Yang 	while ((lineLook > 0) && (
532*8af74909SZhong Yang 	            (!(GetLevel(lineLook) & SC_FOLDLEVELHEADERFLAG)) ||
533*8af74909SZhong Yang 	            (LevelNumber(GetLevel(lineLook)) >= level))
534*8af74909SZhong Yang 	      ) {
535*8af74909SZhong Yang 		lineLook--;
536*8af74909SZhong Yang 	}
537*8af74909SZhong Yang 	if ((GetLevel(lineLook) & SC_FOLDLEVELHEADERFLAG) &&
538*8af74909SZhong Yang 	        (LevelNumber(GetLevel(lineLook)) < level)) {
539*8af74909SZhong Yang 		return lineLook;
540*8af74909SZhong Yang 	} else {
541*8af74909SZhong Yang 		return -1;
542*8af74909SZhong Yang 	}
543*8af74909SZhong Yang }
544*8af74909SZhong Yang 
GetHighlightDelimiters(HighlightDelimiter & highlightDelimiter,Sci::Line line,Sci::Line lastLine)545*8af74909SZhong Yang void Document::GetHighlightDelimiters(HighlightDelimiter &highlightDelimiter, Sci::Line line, Sci::Line lastLine) {
546*8af74909SZhong Yang 	const int level = GetLevel(line);
547*8af74909SZhong Yang 	const Sci::Line lookLastLine = std::max(line, lastLine) + 1;
548*8af74909SZhong Yang 
549*8af74909SZhong Yang 	Sci::Line lookLine = line;
550*8af74909SZhong Yang 	int lookLineLevel = level;
551*8af74909SZhong Yang 	int lookLineLevelNum = LevelNumber(lookLineLevel);
552*8af74909SZhong Yang 	while ((lookLine > 0) && ((lookLineLevel & SC_FOLDLEVELWHITEFLAG) ||
553*8af74909SZhong Yang 		((lookLineLevel & SC_FOLDLEVELHEADERFLAG) && (lookLineLevelNum >= LevelNumber(GetLevel(lookLine + 1)))))) {
554*8af74909SZhong Yang 		lookLineLevel = GetLevel(--lookLine);
555*8af74909SZhong Yang 		lookLineLevelNum = LevelNumber(lookLineLevel);
556*8af74909SZhong Yang 	}
557*8af74909SZhong Yang 
558*8af74909SZhong Yang 	Sci::Line beginFoldBlock = (lookLineLevel & SC_FOLDLEVELHEADERFLAG) ? lookLine : GetFoldParent(lookLine);
559*8af74909SZhong Yang 	if (beginFoldBlock == -1) {
560*8af74909SZhong Yang 		highlightDelimiter.Clear();
561*8af74909SZhong Yang 		return;
562*8af74909SZhong Yang 	}
563*8af74909SZhong Yang 
564*8af74909SZhong Yang 	Sci::Line endFoldBlock = GetLastChild(beginFoldBlock, -1, lookLastLine);
565*8af74909SZhong Yang 	Sci::Line firstChangeableLineBefore = -1;
566*8af74909SZhong Yang 	if (endFoldBlock < line) {
567*8af74909SZhong Yang 		lookLine = beginFoldBlock - 1;
568*8af74909SZhong Yang 		lookLineLevel = GetLevel(lookLine);
569*8af74909SZhong Yang 		lookLineLevelNum = LevelNumber(lookLineLevel);
570*8af74909SZhong Yang 		while ((lookLine >= 0) && (lookLineLevelNum >= SC_FOLDLEVELBASE)) {
571*8af74909SZhong Yang 			if (lookLineLevel & SC_FOLDLEVELHEADERFLAG) {
572*8af74909SZhong Yang 				if (GetLastChild(lookLine, -1, lookLastLine) == line) {
573*8af74909SZhong Yang 					beginFoldBlock = lookLine;
574*8af74909SZhong Yang 					endFoldBlock = line;
575*8af74909SZhong Yang 					firstChangeableLineBefore = line - 1;
576*8af74909SZhong Yang 				}
577*8af74909SZhong Yang 			}
578*8af74909SZhong Yang 			if ((lookLine > 0) && (lookLineLevelNum == SC_FOLDLEVELBASE) && (LevelNumber(GetLevel(lookLine - 1)) > lookLineLevelNum))
579*8af74909SZhong Yang 				break;
580*8af74909SZhong Yang 			lookLineLevel = GetLevel(--lookLine);
581*8af74909SZhong Yang 			lookLineLevelNum = LevelNumber(lookLineLevel);
582*8af74909SZhong Yang 		}
583*8af74909SZhong Yang 	}
584*8af74909SZhong Yang 	if (firstChangeableLineBefore == -1) {
585*8af74909SZhong Yang 		for (lookLine = line - 1, lookLineLevel = GetLevel(lookLine), lookLineLevelNum = LevelNumber(lookLineLevel);
586*8af74909SZhong Yang 			lookLine >= beginFoldBlock;
587*8af74909SZhong Yang 			lookLineLevel = GetLevel(--lookLine), lookLineLevelNum = LevelNumber(lookLineLevel)) {
588*8af74909SZhong Yang 			if ((lookLineLevel & SC_FOLDLEVELWHITEFLAG) || (lookLineLevelNum > LevelNumber(level))) {
589*8af74909SZhong Yang 				firstChangeableLineBefore = lookLine;
590*8af74909SZhong Yang 				break;
591*8af74909SZhong Yang 			}
592*8af74909SZhong Yang 		}
593*8af74909SZhong Yang 	}
594*8af74909SZhong Yang 	if (firstChangeableLineBefore == -1)
595*8af74909SZhong Yang 		firstChangeableLineBefore = beginFoldBlock - 1;
596*8af74909SZhong Yang 
597*8af74909SZhong Yang 	Sci::Line firstChangeableLineAfter = -1;
598*8af74909SZhong Yang 	for (lookLine = line + 1, lookLineLevel = GetLevel(lookLine), lookLineLevelNum = LevelNumber(lookLineLevel);
599*8af74909SZhong Yang 		lookLine <= endFoldBlock;
600*8af74909SZhong Yang 		lookLineLevel = GetLevel(++lookLine), lookLineLevelNum = LevelNumber(lookLineLevel)) {
601*8af74909SZhong Yang 		if ((lookLineLevel & SC_FOLDLEVELHEADERFLAG) && (lookLineLevelNum < LevelNumber(GetLevel(lookLine + 1)))) {
602*8af74909SZhong Yang 			firstChangeableLineAfter = lookLine;
603*8af74909SZhong Yang 			break;
604*8af74909SZhong Yang 		}
605*8af74909SZhong Yang 	}
606*8af74909SZhong Yang 	if (firstChangeableLineAfter == -1)
607*8af74909SZhong Yang 		firstChangeableLineAfter = endFoldBlock + 1;
608*8af74909SZhong Yang 
609*8af74909SZhong Yang 	highlightDelimiter.beginFoldBlock = beginFoldBlock;
610*8af74909SZhong Yang 	highlightDelimiter.endFoldBlock = endFoldBlock;
611*8af74909SZhong Yang 	highlightDelimiter.firstChangeableLineBefore = firstChangeableLineBefore;
612*8af74909SZhong Yang 	highlightDelimiter.firstChangeableLineAfter = firstChangeableLineAfter;
613*8af74909SZhong Yang }
614*8af74909SZhong Yang 
ClampPositionIntoDocument(Sci::Position pos) const615*8af74909SZhong Yang Sci::Position Document::ClampPositionIntoDocument(Sci::Position pos) const noexcept {
616*8af74909SZhong Yang 	return std::clamp<Sci::Position>(pos, 0, LengthNoExcept());
617*8af74909SZhong Yang }
618*8af74909SZhong Yang 
IsCrLf(Sci::Position pos) const619*8af74909SZhong Yang bool Document::IsCrLf(Sci::Position pos) const noexcept {
620*8af74909SZhong Yang 	if (pos < 0)
621*8af74909SZhong Yang 		return false;
622*8af74909SZhong Yang 	if (pos >= (LengthNoExcept() - 1))
623*8af74909SZhong Yang 		return false;
624*8af74909SZhong Yang 	return (cb.CharAt(pos) == '\r') && (cb.CharAt(pos + 1) == '\n');
625*8af74909SZhong Yang }
626*8af74909SZhong Yang 
LenChar(Sci::Position pos) const627*8af74909SZhong Yang int Document::LenChar(Sci::Position pos) const noexcept {
628*8af74909SZhong Yang 	if (pos < 0 || pos >= LengthNoExcept()) {
629*8af74909SZhong Yang 		// Returning 1 instead of 0 to defend against hanging with a loop that goes (or starts) out of bounds.
630*8af74909SZhong Yang 		return 1;
631*8af74909SZhong Yang 	} else if (IsCrLf(pos)) {
632*8af74909SZhong Yang 		return 2;
633*8af74909SZhong Yang 	}
634*8af74909SZhong Yang 
635*8af74909SZhong Yang 	const unsigned char leadByte = cb.UCharAt(pos);
636*8af74909SZhong Yang 	if (!dbcsCodePage || UTF8IsAscii(leadByte)) {
637*8af74909SZhong Yang 		// Common case: ASCII character
638*8af74909SZhong Yang 		return 1;
639*8af74909SZhong Yang 	}
640*8af74909SZhong Yang 	if (SC_CP_UTF8 == dbcsCodePage) {
641*8af74909SZhong Yang 		const int widthCharBytes = UTF8BytesOfLead[leadByte];
642*8af74909SZhong Yang 		unsigned char charBytes[UTF8MaxBytes] = { leadByte, 0, 0, 0 };
643*8af74909SZhong Yang 		for (int b = 1; b < widthCharBytes; b++) {
644*8af74909SZhong Yang 			charBytes[b] = cb.UCharAt(pos + b);
645*8af74909SZhong Yang 		}
646*8af74909SZhong Yang 		const int utf8status = UTF8Classify(charBytes, widthCharBytes);
647*8af74909SZhong Yang 		if (utf8status & UTF8MaskInvalid) {
648*8af74909SZhong Yang 			// Treat as invalid and use up just one byte
649*8af74909SZhong Yang 			return 1;
650*8af74909SZhong Yang 		} else {
651*8af74909SZhong Yang 			return utf8status & UTF8MaskWidth;
652*8af74909SZhong Yang 		}
653*8af74909SZhong Yang 	} else {
654*8af74909SZhong Yang 		if (IsDBCSLeadByteNoExcept(leadByte) && ((pos + 1) < LengthNoExcept())) {
655*8af74909SZhong Yang 			return 2;
656*8af74909SZhong Yang 		} else {
657*8af74909SZhong Yang 			return 1;
658*8af74909SZhong Yang 		}
659*8af74909SZhong Yang 	}
660*8af74909SZhong Yang }
661*8af74909SZhong Yang 
InGoodUTF8(Sci::Position pos,Sci::Position & start,Sci::Position & end) const662*8af74909SZhong Yang bool Document::InGoodUTF8(Sci::Position pos, Sci::Position &start, Sci::Position &end) const noexcept {
663*8af74909SZhong Yang 	Sci::Position trail = pos;
664*8af74909SZhong Yang 	while ((trail>0) && (pos-trail < UTF8MaxBytes) && UTF8IsTrailByte(cb.UCharAt(trail-1)))
665*8af74909SZhong Yang 		trail--;
666*8af74909SZhong Yang 	start = (trail > 0) ? trail-1 : trail;
667*8af74909SZhong Yang 
668*8af74909SZhong Yang 	const unsigned char leadByte = cb.UCharAt(start);
669*8af74909SZhong Yang 	const int widthCharBytes = UTF8BytesOfLead[leadByte];
670*8af74909SZhong Yang 	if (widthCharBytes == 1) {
671*8af74909SZhong Yang 		return false;
672*8af74909SZhong Yang 	} else {
673*8af74909SZhong Yang 		const int trailBytes = widthCharBytes - 1;
674*8af74909SZhong Yang 		const Sci::Position len = pos - start;
675*8af74909SZhong Yang 		if (len > trailBytes)
676*8af74909SZhong Yang 			// pos too far from lead
677*8af74909SZhong Yang 			return false;
678*8af74909SZhong Yang 		unsigned char charBytes[UTF8MaxBytes] = {leadByte,0,0,0};
679*8af74909SZhong Yang 		for (Sci::Position b=1; b<widthCharBytes && ((start+b) < cb.Length()); b++)
680*8af74909SZhong Yang 			charBytes[b] = cb.CharAt(start+b);
681*8af74909SZhong Yang 		const int utf8status = UTF8Classify(charBytes, widthCharBytes);
682*8af74909SZhong Yang 		if (utf8status & UTF8MaskInvalid)
683*8af74909SZhong Yang 			return false;
684*8af74909SZhong Yang 		end = start + widthCharBytes;
685*8af74909SZhong Yang 		return true;
686*8af74909SZhong Yang 	}
687*8af74909SZhong Yang }
688*8af74909SZhong Yang 
689*8af74909SZhong Yang // Normalise a position so that it is not halfway through a two byte character.
690*8af74909SZhong Yang // This can occur in two situations -
691*8af74909SZhong Yang // When lines are terminated with \r\n pairs which should be treated as one character.
692*8af74909SZhong Yang // When displaying DBCS text such as Japanese.
693*8af74909SZhong Yang // If moving, move the position in the indicated direction.
MovePositionOutsideChar(Sci::Position pos,Sci::Position moveDir,bool checkLineEnd) const694*8af74909SZhong Yang Sci::Position Document::MovePositionOutsideChar(Sci::Position pos, Sci::Position moveDir, bool checkLineEnd) const noexcept {
695*8af74909SZhong Yang 	//Platform::DebugPrintf("NoCRLF %d %d\n", pos, moveDir);
696*8af74909SZhong Yang 	// If out of range, just return minimum/maximum value.
697*8af74909SZhong Yang 	if (pos <= 0)
698*8af74909SZhong Yang 		return 0;
699*8af74909SZhong Yang 	if (pos >= LengthNoExcept())
700*8af74909SZhong Yang 		return LengthNoExcept();
701*8af74909SZhong Yang 
702*8af74909SZhong Yang 	// PLATFORM_ASSERT(pos > 0 && pos < LengthNoExcept());
703*8af74909SZhong Yang 	if (checkLineEnd && IsCrLf(pos - 1)) {
704*8af74909SZhong Yang 		if (moveDir > 0)
705*8af74909SZhong Yang 			return pos + 1;
706*8af74909SZhong Yang 		else
707*8af74909SZhong Yang 			return pos - 1;
708*8af74909SZhong Yang 	}
709*8af74909SZhong Yang 
710*8af74909SZhong Yang 	if (dbcsCodePage) {
711*8af74909SZhong Yang 		if (SC_CP_UTF8 == dbcsCodePage) {
712*8af74909SZhong Yang 			const unsigned char ch = cb.UCharAt(pos);
713*8af74909SZhong Yang 			// If ch is not a trail byte then pos is valid intercharacter position
714*8af74909SZhong Yang 			if (UTF8IsTrailByte(ch)) {
715*8af74909SZhong Yang 				Sci::Position startUTF = pos;
716*8af74909SZhong Yang 				Sci::Position endUTF = pos;
717*8af74909SZhong Yang 				if (InGoodUTF8(pos, startUTF, endUTF)) {
718*8af74909SZhong Yang 					// ch is a trail byte within a UTF-8 character
719*8af74909SZhong Yang 					if (moveDir > 0)
720*8af74909SZhong Yang 						pos = endUTF;
721*8af74909SZhong Yang 					else
722*8af74909SZhong Yang 						pos = startUTF;
723*8af74909SZhong Yang 				}
724*8af74909SZhong Yang 				// Else invalid UTF-8 so return position of isolated trail byte
725*8af74909SZhong Yang 			}
726*8af74909SZhong Yang 		} else {
727*8af74909SZhong Yang 			// Anchor DBCS calculations at start of line because start of line can
728*8af74909SZhong Yang 			// not be a DBCS trail byte.
729*8af74909SZhong Yang 			const Sci::Position posStartLine = cb.LineStart(cb.LineFromPosition(pos));
730*8af74909SZhong Yang 			if (pos == posStartLine)
731*8af74909SZhong Yang 				return pos;
732*8af74909SZhong Yang 
733*8af74909SZhong Yang 			// Step back until a non-lead-byte is found.
734*8af74909SZhong Yang 			Sci::Position posCheck = pos;
735*8af74909SZhong Yang 			while ((posCheck > posStartLine) && IsDBCSLeadByteNoExcept(cb.CharAt(posCheck-1)))
736*8af74909SZhong Yang 				posCheck--;
737*8af74909SZhong Yang 
738*8af74909SZhong Yang 			// Check from known start of character.
739*8af74909SZhong Yang 			while (posCheck < pos) {
740*8af74909SZhong Yang 				const int mbsize = IsDBCSLeadByteNoExcept(cb.CharAt(posCheck)) ? 2 : 1;
741*8af74909SZhong Yang 				if (posCheck + mbsize == pos) {
742*8af74909SZhong Yang 					return pos;
743*8af74909SZhong Yang 				} else if (posCheck + mbsize > pos) {
744*8af74909SZhong Yang 					if (moveDir > 0) {
745*8af74909SZhong Yang 						return posCheck + mbsize;
746*8af74909SZhong Yang 					} else {
747*8af74909SZhong Yang 						return posCheck;
748*8af74909SZhong Yang 					}
749*8af74909SZhong Yang 				}
750*8af74909SZhong Yang 				posCheck += mbsize;
751*8af74909SZhong Yang 			}
752*8af74909SZhong Yang 		}
753*8af74909SZhong Yang 	}
754*8af74909SZhong Yang 
755*8af74909SZhong Yang 	return pos;
756*8af74909SZhong Yang }
757*8af74909SZhong Yang 
758*8af74909SZhong Yang // NextPosition moves between valid positions - it can not handle a position in the middle of a
759*8af74909SZhong Yang // multi-byte character. It is used to iterate through text more efficiently than MovePositionOutsideChar.
760*8af74909SZhong Yang // A \r\n pair is treated as two characters.
NextPosition(Sci::Position pos,int moveDir) const761*8af74909SZhong Yang Sci::Position Document::NextPosition(Sci::Position pos, int moveDir) const noexcept {
762*8af74909SZhong Yang 	// If out of range, just return minimum/maximum value.
763*8af74909SZhong Yang 	const int increment = (moveDir > 0) ? 1 : -1;
764*8af74909SZhong Yang 	if (pos + increment <= 0)
765*8af74909SZhong Yang 		return 0;
766*8af74909SZhong Yang 	if (pos + increment >= cb.Length())
767*8af74909SZhong Yang 		return cb.Length();
768*8af74909SZhong Yang 
769*8af74909SZhong Yang 	if (dbcsCodePage) {
770*8af74909SZhong Yang 		if (SC_CP_UTF8 == dbcsCodePage) {
771*8af74909SZhong Yang 			if (increment == 1) {
772*8af74909SZhong Yang 				// Simple forward movement case so can avoid some checks
773*8af74909SZhong Yang 				const unsigned char leadByte = cb.UCharAt(pos);
774*8af74909SZhong Yang 				if (UTF8IsAscii(leadByte)) {
775*8af74909SZhong Yang 					// Single byte character or invalid
776*8af74909SZhong Yang 					pos++;
777*8af74909SZhong Yang 				} else {
778*8af74909SZhong Yang 					const int widthCharBytes = UTF8BytesOfLead[leadByte];
779*8af74909SZhong Yang 					unsigned char charBytes[UTF8MaxBytes] = {leadByte,0,0,0};
780*8af74909SZhong Yang 					for (int b=1; b<widthCharBytes; b++)
781*8af74909SZhong Yang 						charBytes[b] = cb.CharAt(pos+b);
782*8af74909SZhong Yang 					const int utf8status = UTF8Classify(charBytes, widthCharBytes);
783*8af74909SZhong Yang 					if (utf8status & UTF8MaskInvalid)
784*8af74909SZhong Yang 						pos++;
785*8af74909SZhong Yang 					else
786*8af74909SZhong Yang 						pos += utf8status & UTF8MaskWidth;
787*8af74909SZhong Yang 				}
788*8af74909SZhong Yang 			} else {
789*8af74909SZhong Yang 				// Examine byte before position
790*8af74909SZhong Yang 				pos--;
791*8af74909SZhong Yang 				const unsigned char ch = cb.UCharAt(pos);
792*8af74909SZhong Yang 				// If ch is not a trail byte then pos is valid intercharacter position
793*8af74909SZhong Yang 				if (UTF8IsTrailByte(ch)) {
794*8af74909SZhong Yang 					// If ch is a trail byte in a valid UTF-8 character then return start of character
795*8af74909SZhong Yang 					Sci::Position startUTF = pos;
796*8af74909SZhong Yang 					Sci::Position endUTF = pos;
797*8af74909SZhong Yang 					if (InGoodUTF8(pos, startUTF, endUTF)) {
798*8af74909SZhong Yang 						pos = startUTF;
799*8af74909SZhong Yang 					}
800*8af74909SZhong Yang 					// Else invalid UTF-8 so return position of isolated trail byte
801*8af74909SZhong Yang 				}
802*8af74909SZhong Yang 			}
803*8af74909SZhong Yang 		} else {
804*8af74909SZhong Yang 			if (moveDir > 0) {
805*8af74909SZhong Yang 				const int mbsize = IsDBCSLeadByteNoExcept(cb.CharAt(pos)) ? 2 : 1;
806*8af74909SZhong Yang 				pos += mbsize;
807*8af74909SZhong Yang 				if (pos > cb.Length())
808*8af74909SZhong Yang 					pos = cb.Length();
809*8af74909SZhong Yang 			} else {
810*8af74909SZhong Yang 				// Anchor DBCS calculations at start of line because start of line can
811*8af74909SZhong Yang 				// not be a DBCS trail byte.
812*8af74909SZhong Yang 				const Sci::Position posStartLine = cb.LineStart(cb.LineFromPosition(pos));
813*8af74909SZhong Yang 				// See http://msdn.microsoft.com/en-us/library/cc194792%28v=MSDN.10%29.aspx
814*8af74909SZhong Yang 				// http://msdn.microsoft.com/en-us/library/cc194790.aspx
815*8af74909SZhong Yang 				if ((pos - 1) <= posStartLine) {
816*8af74909SZhong Yang 					return pos - 1;
817*8af74909SZhong Yang 				} else if (IsDBCSLeadByteNoExcept(cb.CharAt(pos - 1))) {
818*8af74909SZhong Yang 					// Must actually be trail byte
819*8af74909SZhong Yang 					return pos - 2;
820*8af74909SZhong Yang 				} else {
821*8af74909SZhong Yang 					// Otherwise, step back until a non-lead-byte is found.
822*8af74909SZhong Yang 					Sci::Position posTemp = pos - 1;
823*8af74909SZhong Yang 					while (posStartLine <= --posTemp && IsDBCSLeadByteNoExcept(cb.CharAt(posTemp)))
824*8af74909SZhong Yang 						;
825*8af74909SZhong Yang 					// Now posTemp+1 must point to the beginning of a character,
826*8af74909SZhong Yang 					// so figure out whether we went back an even or an odd
827*8af74909SZhong Yang 					// number of bytes and go back 1 or 2 bytes, respectively.
828*8af74909SZhong Yang 					return (pos - 1 - ((pos - posTemp) & 1));
829*8af74909SZhong Yang 				}
830*8af74909SZhong Yang 			}
831*8af74909SZhong Yang 		}
832*8af74909SZhong Yang 	} else {
833*8af74909SZhong Yang 		pos += increment;
834*8af74909SZhong Yang 	}
835*8af74909SZhong Yang 
836*8af74909SZhong Yang 	return pos;
837*8af74909SZhong Yang }
838*8af74909SZhong Yang 
NextCharacter(Sci::Position & pos,int moveDir) const839*8af74909SZhong Yang bool Document::NextCharacter(Sci::Position &pos, int moveDir) const noexcept {
840*8af74909SZhong Yang 	// Returns true if pos changed
841*8af74909SZhong Yang 	Sci::Position posNext = NextPosition(pos, moveDir);
842*8af74909SZhong Yang 	if (posNext == pos) {
843*8af74909SZhong Yang 		return false;
844*8af74909SZhong Yang 	} else {
845*8af74909SZhong Yang 		pos = posNext;
846*8af74909SZhong Yang 		return true;
847*8af74909SZhong Yang 	}
848*8af74909SZhong Yang }
849*8af74909SZhong Yang 
CharacterAfter(Sci::Position position) const850*8af74909SZhong Yang Document::CharacterExtracted Document::CharacterAfter(Sci::Position position) const noexcept {
851*8af74909SZhong Yang 	if (position >= LengthNoExcept()) {
852*8af74909SZhong Yang 		return CharacterExtracted(unicodeReplacementChar, 0);
853*8af74909SZhong Yang 	}
854*8af74909SZhong Yang 	const unsigned char leadByte = cb.UCharAt(position);
855*8af74909SZhong Yang 	if (!dbcsCodePage || UTF8IsAscii(leadByte)) {
856*8af74909SZhong Yang 		// Common case: ASCII character
857*8af74909SZhong Yang 		return CharacterExtracted(leadByte, 1);
858*8af74909SZhong Yang 	}
859*8af74909SZhong Yang 	if (SC_CP_UTF8 == dbcsCodePage) {
860*8af74909SZhong Yang 		const int widthCharBytes = UTF8BytesOfLead[leadByte];
861*8af74909SZhong Yang 		unsigned char charBytes[UTF8MaxBytes] = { leadByte, 0, 0, 0 };
862*8af74909SZhong Yang 		for (int b = 1; b<widthCharBytes; b++)
863*8af74909SZhong Yang 			charBytes[b] = cb.UCharAt(position + b);
864*8af74909SZhong Yang 		const int utf8status = UTF8Classify(charBytes, widthCharBytes);
865*8af74909SZhong Yang 		if (utf8status & UTF8MaskInvalid) {
866*8af74909SZhong Yang 			// Treat as invalid and use up just one byte
867*8af74909SZhong Yang 			return CharacterExtracted(unicodeReplacementChar, 1);
868*8af74909SZhong Yang 		} else {
869*8af74909SZhong Yang 			return CharacterExtracted(UnicodeFromUTF8(charBytes), utf8status & UTF8MaskWidth);
870*8af74909SZhong Yang 		}
871*8af74909SZhong Yang 	} else {
872*8af74909SZhong Yang 		if (IsDBCSLeadByteNoExcept(leadByte) && ((position + 1) < LengthNoExcept())) {
873*8af74909SZhong Yang 			return CharacterExtracted::DBCS(leadByte, cb.UCharAt(position + 1));
874*8af74909SZhong Yang 		} else {
875*8af74909SZhong Yang 			return CharacterExtracted(leadByte, 1);
876*8af74909SZhong Yang 		}
877*8af74909SZhong Yang 	}
878*8af74909SZhong Yang }
879*8af74909SZhong Yang 
CharacterBefore(Sci::Position position) const880*8af74909SZhong Yang Document::CharacterExtracted Document::CharacterBefore(Sci::Position position) const noexcept {
881*8af74909SZhong Yang 	if (position <= 0) {
882*8af74909SZhong Yang 		return CharacterExtracted(unicodeReplacementChar, 0);
883*8af74909SZhong Yang 	}
884*8af74909SZhong Yang 	const unsigned char previousByte = cb.UCharAt(position - 1);
885*8af74909SZhong Yang 	if (0 == dbcsCodePage) {
886*8af74909SZhong Yang 		return CharacterExtracted(previousByte, 1);
887*8af74909SZhong Yang 	}
888*8af74909SZhong Yang 	if (SC_CP_UTF8 == dbcsCodePage) {
889*8af74909SZhong Yang 		if (UTF8IsAscii(previousByte)) {
890*8af74909SZhong Yang 			return CharacterExtracted(previousByte, 1);
891*8af74909SZhong Yang 		}
892*8af74909SZhong Yang 		position--;
893*8af74909SZhong Yang 		// If previousByte is not a trail byte then its invalid
894*8af74909SZhong Yang 		if (UTF8IsTrailByte(previousByte)) {
895*8af74909SZhong Yang 			// If previousByte is a trail byte in a valid UTF-8 character then find start of character
896*8af74909SZhong Yang 			Sci::Position startUTF = position;
897*8af74909SZhong Yang 			Sci::Position endUTF = position;
898*8af74909SZhong Yang 			if (InGoodUTF8(position, startUTF, endUTF)) {
899*8af74909SZhong Yang 				const Sci::Position widthCharBytes = endUTF - startUTF;
900*8af74909SZhong Yang 				unsigned char charBytes[UTF8MaxBytes] = { 0, 0, 0, 0 };
901*8af74909SZhong Yang 				for (Sci::Position b = 0; b<widthCharBytes; b++)
902*8af74909SZhong Yang 					charBytes[b] = cb.UCharAt(startUTF + b);
903*8af74909SZhong Yang 				const int utf8status = UTF8Classify(charBytes, widthCharBytes);
904*8af74909SZhong Yang 				if (utf8status & UTF8MaskInvalid) {
905*8af74909SZhong Yang 					// Treat as invalid and use up just one byte
906*8af74909SZhong Yang 					return CharacterExtracted(unicodeReplacementChar, 1);
907*8af74909SZhong Yang 				} else {
908*8af74909SZhong Yang 					return CharacterExtracted(UnicodeFromUTF8(charBytes), utf8status & UTF8MaskWidth);
909*8af74909SZhong Yang 				}
910*8af74909SZhong Yang 			}
911*8af74909SZhong Yang 			// Else invalid UTF-8 so return position of isolated trail byte
912*8af74909SZhong Yang 		}
913*8af74909SZhong Yang 		return CharacterExtracted(unicodeReplacementChar, 1);
914*8af74909SZhong Yang 	} else {
915*8af74909SZhong Yang 		// Moving backwards in DBCS is complex so use NextPosition
916*8af74909SZhong Yang 		const Sci::Position posStartCharacter = NextPosition(position, -1);
917*8af74909SZhong Yang 		return CharacterAfter(posStartCharacter);
918*8af74909SZhong Yang 	}
919*8af74909SZhong Yang }
920*8af74909SZhong Yang 
921*8af74909SZhong Yang // Return -1  on out-of-bounds
GetRelativePosition(Sci_Position positionStart,Sci_Position characterOffset) const922*8af74909SZhong Yang Sci_Position SCI_METHOD Document::GetRelativePosition(Sci_Position positionStart, Sci_Position characterOffset) const {
923*8af74909SZhong Yang 	Sci::Position pos = positionStart;
924*8af74909SZhong Yang 	if (dbcsCodePage) {
925*8af74909SZhong Yang 		const int increment = (characterOffset > 0) ? 1 : -1;
926*8af74909SZhong Yang 		while (characterOffset != 0) {
927*8af74909SZhong Yang 			const Sci::Position posNext = NextPosition(pos, increment);
928*8af74909SZhong Yang 			if (posNext == pos)
929*8af74909SZhong Yang 				return INVALID_POSITION;
930*8af74909SZhong Yang 			pos = posNext;
931*8af74909SZhong Yang 			characterOffset -= increment;
932*8af74909SZhong Yang 		}
933*8af74909SZhong Yang 	} else {
934*8af74909SZhong Yang 		pos = positionStart + characterOffset;
935*8af74909SZhong Yang 		if ((pos < 0) || (pos > Length()))
936*8af74909SZhong Yang 			return INVALID_POSITION;
937*8af74909SZhong Yang 	}
938*8af74909SZhong Yang 	return pos;
939*8af74909SZhong Yang }
940*8af74909SZhong Yang 
GetRelativePositionUTF16(Sci::Position positionStart,Sci::Position characterOffset) const941*8af74909SZhong Yang Sci::Position Document::GetRelativePositionUTF16(Sci::Position positionStart, Sci::Position characterOffset) const noexcept {
942*8af74909SZhong Yang 	Sci::Position pos = positionStart;
943*8af74909SZhong Yang 	if (dbcsCodePage) {
944*8af74909SZhong Yang 		const int increment = (characterOffset > 0) ? 1 : -1;
945*8af74909SZhong Yang 		while (characterOffset != 0) {
946*8af74909SZhong Yang 			const Sci::Position posNext = NextPosition(pos, increment);
947*8af74909SZhong Yang 			if (posNext == pos)
948*8af74909SZhong Yang 				return INVALID_POSITION;
949*8af74909SZhong Yang 			if (std::abs(pos-posNext) > 3)	// 4 byte character = 2*UTF16.
950*8af74909SZhong Yang 				characterOffset -= increment;
951*8af74909SZhong Yang 			pos = posNext;
952*8af74909SZhong Yang 			characterOffset -= increment;
953*8af74909SZhong Yang 		}
954*8af74909SZhong Yang 	} else {
955*8af74909SZhong Yang 		pos = positionStart + characterOffset;
956*8af74909SZhong Yang 		if ((pos < 0) || (pos > LengthNoExcept()))
957*8af74909SZhong Yang 			return INVALID_POSITION;
958*8af74909SZhong Yang 	}
959*8af74909SZhong Yang 	return pos;
960*8af74909SZhong Yang }
961*8af74909SZhong Yang 
GetCharacterAndWidth(Sci_Position position,Sci_Position * pWidth) const962*8af74909SZhong Yang int SCI_METHOD Document::GetCharacterAndWidth(Sci_Position position, Sci_Position *pWidth) const {
963*8af74909SZhong Yang 	int character;
964*8af74909SZhong Yang 	int bytesInCharacter = 1;
965*8af74909SZhong Yang 	const unsigned char leadByte = cb.UCharAt(position);
966*8af74909SZhong Yang 	if (dbcsCodePage) {
967*8af74909SZhong Yang 		if (SC_CP_UTF8 == dbcsCodePage) {
968*8af74909SZhong Yang 			if (UTF8IsAscii(leadByte)) {
969*8af74909SZhong Yang 				// Single byte character or invalid
970*8af74909SZhong Yang 				character =  leadByte;
971*8af74909SZhong Yang 			} else {
972*8af74909SZhong Yang 				const int widthCharBytes = UTF8BytesOfLead[leadByte];
973*8af74909SZhong Yang 				unsigned char charBytes[UTF8MaxBytes] = {leadByte,0,0,0};
974*8af74909SZhong Yang 				for (int b=1; b<widthCharBytes; b++)
975*8af74909SZhong Yang 					charBytes[b] = cb.UCharAt(position+b);
976*8af74909SZhong Yang 				const int utf8status = UTF8Classify(charBytes, widthCharBytes);
977*8af74909SZhong Yang 				if (utf8status & UTF8MaskInvalid) {
978*8af74909SZhong Yang 					// Report as singleton surrogate values which are invalid Unicode
979*8af74909SZhong Yang 					character =  0xDC80 + leadByte;
980*8af74909SZhong Yang 				} else {
981*8af74909SZhong Yang 					bytesInCharacter = utf8status & UTF8MaskWidth;
982*8af74909SZhong Yang 					character = UnicodeFromUTF8(charBytes);
983*8af74909SZhong Yang 				}
984*8af74909SZhong Yang 			}
985*8af74909SZhong Yang 		} else {
986*8af74909SZhong Yang 			if (IsDBCSLeadByteNoExcept(leadByte)) {
987*8af74909SZhong Yang 				bytesInCharacter = 2;
988*8af74909SZhong Yang 				character = (leadByte << 8) | cb.UCharAt(position+1);
989*8af74909SZhong Yang 			} else {
990*8af74909SZhong Yang 				character = leadByte;
991*8af74909SZhong Yang 			}
992*8af74909SZhong Yang 		}
993*8af74909SZhong Yang 	} else {
994*8af74909SZhong Yang 		character = leadByte;
995*8af74909SZhong Yang 	}
996*8af74909SZhong Yang 	if (pWidth) {
997*8af74909SZhong Yang 		*pWidth = bytesInCharacter;
998*8af74909SZhong Yang 	}
999*8af74909SZhong Yang 	return character;
1000*8af74909SZhong Yang }
1001*8af74909SZhong Yang 
CodePage() const1002*8af74909SZhong Yang int SCI_METHOD Document::CodePage() const {
1003*8af74909SZhong Yang 	return dbcsCodePage;
1004*8af74909SZhong Yang }
1005*8af74909SZhong Yang 
IsDBCSLeadByte(char ch) const1006*8af74909SZhong Yang bool SCI_METHOD Document::IsDBCSLeadByte(char ch) const {
1007*8af74909SZhong Yang 	// Used by lexers so must match IDocument method exactly
1008*8af74909SZhong Yang 	return IsDBCSLeadByteNoExcept(ch);
1009*8af74909SZhong Yang }
1010*8af74909SZhong Yang 
IsDBCSLeadByteNoExcept(char ch) const1011*8af74909SZhong Yang bool Document::IsDBCSLeadByteNoExcept(char ch) const noexcept {
1012*8af74909SZhong Yang 	// Used inside core Scintilla
1013*8af74909SZhong Yang 	// Byte ranges found in Wikipedia articles with relevant search strings in each case
1014*8af74909SZhong Yang 	const unsigned char uch = ch;
1015*8af74909SZhong Yang 	switch (dbcsCodePage) {
1016*8af74909SZhong Yang 		case 932:
1017*8af74909SZhong Yang 			// Shift_jis
1018*8af74909SZhong Yang 			return ((uch >= 0x81) && (uch <= 0x9F)) ||
1019*8af74909SZhong Yang 				((uch >= 0xE0) && (uch <= 0xFC));
1020*8af74909SZhong Yang 				// Lead bytes F0 to FC may be a Microsoft addition.
1021*8af74909SZhong Yang 		case 936:
1022*8af74909SZhong Yang 			// GBK
1023*8af74909SZhong Yang 			return (uch >= 0x81) && (uch <= 0xFE);
1024*8af74909SZhong Yang 		case 949:
1025*8af74909SZhong Yang 			// Korean Wansung KS C-5601-1987
1026*8af74909SZhong Yang 			return (uch >= 0x81) && (uch <= 0xFE);
1027*8af74909SZhong Yang 		case 950:
1028*8af74909SZhong Yang 			// Big5
1029*8af74909SZhong Yang 			return (uch >= 0x81) && (uch <= 0xFE);
1030*8af74909SZhong Yang 		case 1361:
1031*8af74909SZhong Yang 			// Korean Johab KS C-5601-1992
1032*8af74909SZhong Yang 			return
1033*8af74909SZhong Yang 				((uch >= 0x84) && (uch <= 0xD3)) ||
1034*8af74909SZhong Yang 				((uch >= 0xD8) && (uch <= 0xDE)) ||
1035*8af74909SZhong Yang 				((uch >= 0xE0) && (uch <= 0xF9));
1036*8af74909SZhong Yang 	}
1037*8af74909SZhong Yang 	return false;
1038*8af74909SZhong Yang }
1039*8af74909SZhong Yang 
IsDBCSLeadByteInvalid(char ch) const1040*8af74909SZhong Yang bool Document::IsDBCSLeadByteInvalid(char ch) const noexcept {
1041*8af74909SZhong Yang 	const unsigned char lead = ch;
1042*8af74909SZhong Yang 	switch (dbcsCodePage) {
1043*8af74909SZhong Yang 	case 932:
1044*8af74909SZhong Yang 		// Shift_jis
1045*8af74909SZhong Yang 		return
1046*8af74909SZhong Yang 			(lead == 0x85) ||
1047*8af74909SZhong Yang 			(lead == 0x86) ||
1048*8af74909SZhong Yang 			(lead == 0xEB) ||
1049*8af74909SZhong Yang 			(lead == 0xEC) ||
1050*8af74909SZhong Yang 			(lead == 0xEF) ||
1051*8af74909SZhong Yang 			(lead == 0xFA) ||
1052*8af74909SZhong Yang 			(lead == 0xFB) ||
1053*8af74909SZhong Yang 			(lead == 0xFC);
1054*8af74909SZhong Yang 	case 936:
1055*8af74909SZhong Yang 		// GBK
1056*8af74909SZhong Yang 		return (lead == 0x80) || (lead == 0xFF);
1057*8af74909SZhong Yang 	case 949:
1058*8af74909SZhong Yang 		// Korean Wansung KS C-5601-1987
1059*8af74909SZhong Yang 		return (lead == 0x80) || (lead == 0xC9) || (lead >= 0xFE);
1060*8af74909SZhong Yang 	case 950:
1061*8af74909SZhong Yang 		// Big5
1062*8af74909SZhong Yang 		return
1063*8af74909SZhong Yang 			((lead >= 0x80) && (lead <= 0xA0)) ||
1064*8af74909SZhong Yang 			(lead == 0xC8) ||
1065*8af74909SZhong Yang 			(lead >= 0xFA);
1066*8af74909SZhong Yang 	case 1361:
1067*8af74909SZhong Yang 		// Korean Johab KS C-5601-1992
1068*8af74909SZhong Yang 		return
1069*8af74909SZhong Yang 			((lead >= 0x80) && (lead <= 0x83)) ||
1070*8af74909SZhong Yang 			((lead >= 0xD4) && (lead <= 0xD8)) ||
1071*8af74909SZhong Yang 			(lead == 0xDF) ||
1072*8af74909SZhong Yang 			(lead >= 0xFA);
1073*8af74909SZhong Yang 	}
1074*8af74909SZhong Yang 	return false;
1075*8af74909SZhong Yang }
1076*8af74909SZhong Yang 
IsDBCSTrailByteInvalid(char ch) const1077*8af74909SZhong Yang bool Document::IsDBCSTrailByteInvalid(char ch) const noexcept {
1078*8af74909SZhong Yang 	const unsigned char trail = ch;
1079*8af74909SZhong Yang 	switch (dbcsCodePage) {
1080*8af74909SZhong Yang 	case 932:
1081*8af74909SZhong Yang 		// Shift_jis
1082*8af74909SZhong Yang 		return
1083*8af74909SZhong Yang 			(trail <= 0x3F) ||
1084*8af74909SZhong Yang 			(trail == 0x7F) ||
1085*8af74909SZhong Yang 			(trail >= 0xFD);
1086*8af74909SZhong Yang 	case 936:
1087*8af74909SZhong Yang 		// GBK
1088*8af74909SZhong Yang 		return
1089*8af74909SZhong Yang 			(trail <= 0x3F) ||
1090*8af74909SZhong Yang 			(trail == 0x7F) ||
1091*8af74909SZhong Yang 			(trail == 0xFF);
1092*8af74909SZhong Yang 	case 949:
1093*8af74909SZhong Yang 		// Korean Wansung KS C-5601-1987
1094*8af74909SZhong Yang 		return
1095*8af74909SZhong Yang 			(trail <= 0x40) ||
1096*8af74909SZhong Yang 			((trail >= 0x5B) && (trail <= 0x60)) ||
1097*8af74909SZhong Yang 			((trail >= 0x7B) && (trail <= 0x80)) ||
1098*8af74909SZhong Yang 			(trail == 0xFF);
1099*8af74909SZhong Yang 	case 950:
1100*8af74909SZhong Yang 		// Big5
1101*8af74909SZhong Yang 		return
1102*8af74909SZhong Yang 			(trail <= 0x3F) ||
1103*8af74909SZhong Yang 			((trail >= 0x7F) && (trail <= 0xA0)) ||
1104*8af74909SZhong Yang 			(trail == 0xFF);
1105*8af74909SZhong Yang 	case 1361:
1106*8af74909SZhong Yang 		// Korean Johab KS C-5601-1992
1107*8af74909SZhong Yang 		return
1108*8af74909SZhong Yang 			(trail <= 0x30) ||
1109*8af74909SZhong Yang 			(trail == 0x7F) ||
1110*8af74909SZhong Yang 			(trail == 0x80) ||
1111*8af74909SZhong Yang 			(trail == 0xFF);
1112*8af74909SZhong Yang 	}
1113*8af74909SZhong Yang 	return false;
1114*8af74909SZhong Yang }
1115*8af74909SZhong Yang 
DBCSDrawBytes(std::string_view text) const1116*8af74909SZhong Yang int Document::DBCSDrawBytes(std::string_view text) const noexcept {
1117*8af74909SZhong Yang 	if (text.length() <= 1) {
1118*8af74909SZhong Yang 		return static_cast<int>(text.length());
1119*8af74909SZhong Yang 	}
1120*8af74909SZhong Yang 	if (IsDBCSLeadByteNoExcept(text[0])) {
1121*8af74909SZhong Yang 		return IsDBCSTrailByteInvalid(text[1]) ? 1 : 2;
1122*8af74909SZhong Yang 	} else {
1123*8af74909SZhong Yang 		return 1;
1124*8af74909SZhong Yang 	}
1125*8af74909SZhong Yang }
1126*8af74909SZhong Yang 
IsSpaceOrTab(int ch)1127*8af74909SZhong Yang static constexpr bool IsSpaceOrTab(int ch) noexcept {
1128*8af74909SZhong Yang 	return ch == ' ' || ch == '\t';
1129*8af74909SZhong Yang }
1130*8af74909SZhong Yang 
1131*8af74909SZhong Yang // Need to break text into segments near lengthSegment but taking into
1132*8af74909SZhong Yang // account the encoding to not break inside a UTF-8 or DBCS character
1133*8af74909SZhong Yang // and also trying to avoid breaking inside a pair of combining characters.
1134*8af74909SZhong Yang // The segment length must always be long enough (more than 4 bytes)
1135*8af74909SZhong Yang // so that there will be at least one whole character to make a segment.
1136*8af74909SZhong Yang // For UTF-8, text must consist only of valid whole characters.
1137*8af74909SZhong Yang // In preference order from best to worst:
1138*8af74909SZhong Yang //   1) Break after space
1139*8af74909SZhong Yang //   2) Break before punctuation
1140*8af74909SZhong Yang //   3) Break after whole character
1141*8af74909SZhong Yang 
SafeSegment(const char * text,int length,int lengthSegment) const1142*8af74909SZhong Yang int Document::SafeSegment(const char *text, int length, int lengthSegment) const noexcept {
1143*8af74909SZhong Yang 	if (length <= lengthSegment)
1144*8af74909SZhong Yang 		return length;
1145*8af74909SZhong Yang 	int lastSpaceBreak = -1;
1146*8af74909SZhong Yang 	int lastPunctuationBreak = -1;
1147*8af74909SZhong Yang 	int lastEncodingAllowedBreak = 0;
1148*8af74909SZhong Yang 	for (int j=0; j < lengthSegment;) {
1149*8af74909SZhong Yang 		const unsigned char ch = text[j];
1150*8af74909SZhong Yang 		if (j > 0) {
1151*8af74909SZhong Yang 			if (IsSpaceOrTab(text[j - 1]) && !IsSpaceOrTab(text[j])) {
1152*8af74909SZhong Yang 				lastSpaceBreak = j;
1153*8af74909SZhong Yang 			}
1154*8af74909SZhong Yang 			if (ch < 'A') {
1155*8af74909SZhong Yang 				lastPunctuationBreak = j;
1156*8af74909SZhong Yang 			}
1157*8af74909SZhong Yang 		}
1158*8af74909SZhong Yang 		lastEncodingAllowedBreak = j;
1159*8af74909SZhong Yang 
1160*8af74909SZhong Yang 		if (dbcsCodePage == SC_CP_UTF8) {
1161*8af74909SZhong Yang 			j += UTF8BytesOfLead[ch];
1162*8af74909SZhong Yang 		} else if (dbcsCodePage) {
1163*8af74909SZhong Yang 			j += IsDBCSLeadByteNoExcept(ch) ? 2 : 1;
1164*8af74909SZhong Yang 		} else {
1165*8af74909SZhong Yang 			j++;
1166*8af74909SZhong Yang 		}
1167*8af74909SZhong Yang 	}
1168*8af74909SZhong Yang 	if (lastSpaceBreak >= 0) {
1169*8af74909SZhong Yang 		return lastSpaceBreak;
1170*8af74909SZhong Yang 	} else if (lastPunctuationBreak >= 0) {
1171*8af74909SZhong Yang 		return lastPunctuationBreak;
1172*8af74909SZhong Yang 	}
1173*8af74909SZhong Yang 	return lastEncodingAllowedBreak;
1174*8af74909SZhong Yang }
1175*8af74909SZhong Yang 
CodePageFamily() const1176*8af74909SZhong Yang EncodingFamily Document::CodePageFamily() const noexcept {
1177*8af74909SZhong Yang 	if (SC_CP_UTF8 == dbcsCodePage)
1178*8af74909SZhong Yang 		return EncodingFamily::unicode;
1179*8af74909SZhong Yang 	else if (dbcsCodePage)
1180*8af74909SZhong Yang 		return EncodingFamily::dbcs;
1181*8af74909SZhong Yang 	else
1182*8af74909SZhong Yang 		return EncodingFamily::eightBit;
1183*8af74909SZhong Yang }
1184*8af74909SZhong Yang 
ModifiedAt(Sci::Position pos)1185*8af74909SZhong Yang void Document::ModifiedAt(Sci::Position pos) noexcept {
1186*8af74909SZhong Yang 	if (endStyled > pos)
1187*8af74909SZhong Yang 		endStyled = pos;
1188*8af74909SZhong Yang }
1189*8af74909SZhong Yang 
CheckReadOnly()1190*8af74909SZhong Yang void Document::CheckReadOnly() {
1191*8af74909SZhong Yang 	if (cb.IsReadOnly() && enteredReadOnlyCount == 0) {
1192*8af74909SZhong Yang 		enteredReadOnlyCount++;
1193*8af74909SZhong Yang 		NotifyModifyAttempt();
1194*8af74909SZhong Yang 		enteredReadOnlyCount--;
1195*8af74909SZhong Yang 	}
1196*8af74909SZhong Yang }
1197*8af74909SZhong Yang 
1198*8af74909SZhong Yang // Document only modified by gateways DeleteChars, InsertString, Undo, Redo, and SetStyleAt.
1199*8af74909SZhong Yang // SetStyleAt does not change the persistent state of a document
1200*8af74909SZhong Yang 
DeleteChars(Sci::Position pos,Sci::Position len)1201*8af74909SZhong Yang bool Document::DeleteChars(Sci::Position pos, Sci::Position len) {
1202*8af74909SZhong Yang 	if (pos < 0)
1203*8af74909SZhong Yang 		return false;
1204*8af74909SZhong Yang 	if (len <= 0)
1205*8af74909SZhong Yang 		return false;
1206*8af74909SZhong Yang 	if ((pos + len) > LengthNoExcept())
1207*8af74909SZhong Yang 		return false;
1208*8af74909SZhong Yang 	CheckReadOnly();
1209*8af74909SZhong Yang 	if (enteredModification != 0) {
1210*8af74909SZhong Yang 		return false;
1211*8af74909SZhong Yang 	} else {
1212*8af74909SZhong Yang 		enteredModification++;
1213*8af74909SZhong Yang 		if (!cb.IsReadOnly()) {
1214*8af74909SZhong Yang 			NotifyModified(
1215*8af74909SZhong Yang 			    DocModification(
1216*8af74909SZhong Yang 			        SC_MOD_BEFOREDELETE | SC_PERFORMED_USER,
1217*8af74909SZhong Yang 			        pos, len,
1218*8af74909SZhong Yang 			        0, 0));
1219*8af74909SZhong Yang 			const Sci::Line prevLinesTotal = LinesTotal();
1220*8af74909SZhong Yang 			const bool startSavePoint = cb.IsSavePoint();
1221*8af74909SZhong Yang 			bool startSequence = false;
1222*8af74909SZhong Yang 			const char *text = cb.DeleteChars(pos, len, startSequence);
1223*8af74909SZhong Yang 			if (startSavePoint && cb.IsCollectingUndo())
1224*8af74909SZhong Yang 				NotifySavePoint(false);
1225*8af74909SZhong Yang 			if ((pos < LengthNoExcept()) || (pos == 0))
1226*8af74909SZhong Yang 				ModifiedAt(pos);
1227*8af74909SZhong Yang 			else
1228*8af74909SZhong Yang 				ModifiedAt(pos-1);
1229*8af74909SZhong Yang 			NotifyModified(
1230*8af74909SZhong Yang 			    DocModification(
1231*8af74909SZhong Yang 			        SC_MOD_DELETETEXT | SC_PERFORMED_USER | (startSequence?SC_STARTACTION:0),
1232*8af74909SZhong Yang 			        pos, len,
1233*8af74909SZhong Yang 			        LinesTotal() - prevLinesTotal, text));
1234*8af74909SZhong Yang 		}
1235*8af74909SZhong Yang 		enteredModification--;
1236*8af74909SZhong Yang 	}
1237*8af74909SZhong Yang 	return !cb.IsReadOnly();
1238*8af74909SZhong Yang }
1239*8af74909SZhong Yang 
1240*8af74909SZhong Yang /**
1241*8af74909SZhong Yang  * Insert a string with a length.
1242*8af74909SZhong Yang  */
InsertString(Sci::Position position,const char * s,Sci::Position insertLength)1243*8af74909SZhong Yang Sci::Position Document::InsertString(Sci::Position position, const char *s, Sci::Position insertLength) {
1244*8af74909SZhong Yang 	if (insertLength <= 0) {
1245*8af74909SZhong Yang 		return 0;
1246*8af74909SZhong Yang 	}
1247*8af74909SZhong Yang 	CheckReadOnly();	// Application may change read only state here
1248*8af74909SZhong Yang 	if (cb.IsReadOnly()) {
1249*8af74909SZhong Yang 		return 0;
1250*8af74909SZhong Yang 	}
1251*8af74909SZhong Yang 	if (enteredModification != 0) {
1252*8af74909SZhong Yang 		return 0;
1253*8af74909SZhong Yang 	}
1254*8af74909SZhong Yang 	enteredModification++;
1255*8af74909SZhong Yang 	insertionSet = false;
1256*8af74909SZhong Yang 	insertion.clear();
1257*8af74909SZhong Yang 	NotifyModified(
1258*8af74909SZhong Yang 		DocModification(
1259*8af74909SZhong Yang 			SC_MOD_INSERTCHECK,
1260*8af74909SZhong Yang 			position, insertLength,
1261*8af74909SZhong Yang 			0, s));
1262*8af74909SZhong Yang 	if (insertionSet) {
1263*8af74909SZhong Yang 		s = insertion.c_str();
1264*8af74909SZhong Yang 		insertLength = insertion.length();
1265*8af74909SZhong Yang 	}
1266*8af74909SZhong Yang 	NotifyModified(
1267*8af74909SZhong Yang 		DocModification(
1268*8af74909SZhong Yang 			SC_MOD_BEFOREINSERT | SC_PERFORMED_USER,
1269*8af74909SZhong Yang 			position, insertLength,
1270*8af74909SZhong Yang 			0, s));
1271*8af74909SZhong Yang 	const Sci::Line prevLinesTotal = LinesTotal();
1272*8af74909SZhong Yang 	const bool startSavePoint = cb.IsSavePoint();
1273*8af74909SZhong Yang 	bool startSequence = false;
1274*8af74909SZhong Yang 	const char *text = cb.InsertString(position, s, insertLength, startSequence);
1275*8af74909SZhong Yang 	if (startSavePoint && cb.IsCollectingUndo())
1276*8af74909SZhong Yang 		NotifySavePoint(false);
1277*8af74909SZhong Yang 	ModifiedAt(position);
1278*8af74909SZhong Yang 	NotifyModified(
1279*8af74909SZhong Yang 		DocModification(
1280*8af74909SZhong Yang 			SC_MOD_INSERTTEXT | SC_PERFORMED_USER | (startSequence?SC_STARTACTION:0),
1281*8af74909SZhong Yang 			position, insertLength,
1282*8af74909SZhong Yang 			LinesTotal() - prevLinesTotal, text));
1283*8af74909SZhong Yang 	if (insertionSet) {	// Free memory as could be large
1284*8af74909SZhong Yang 		std::string().swap(insertion);
1285*8af74909SZhong Yang 	}
1286*8af74909SZhong Yang 	enteredModification--;
1287*8af74909SZhong Yang 	return insertLength;
1288*8af74909SZhong Yang }
1289*8af74909SZhong Yang 
ChangeInsertion(const char * s,Sci::Position length)1290*8af74909SZhong Yang void Document::ChangeInsertion(const char *s, Sci::Position length) {
1291*8af74909SZhong Yang 	insertionSet = true;
1292*8af74909SZhong Yang 	insertion.assign(s, length);
1293*8af74909SZhong Yang }
1294*8af74909SZhong Yang 
AddData(const char * data,Sci_Position length)1295*8af74909SZhong Yang int SCI_METHOD Document::AddData(const char *data, Sci_Position length) {
1296*8af74909SZhong Yang 	try {
1297*8af74909SZhong Yang 		const Sci::Position position = Length();
1298*8af74909SZhong Yang 		InsertString(position, data, length);
1299*8af74909SZhong Yang 	} catch (std::bad_alloc &) {
1300*8af74909SZhong Yang 		return SC_STATUS_BADALLOC;
1301*8af74909SZhong Yang 	} catch (...) {
1302*8af74909SZhong Yang 		return SC_STATUS_FAILURE;
1303*8af74909SZhong Yang 	}
1304*8af74909SZhong Yang 	return 0;
1305*8af74909SZhong Yang }
1306*8af74909SZhong Yang 
ConvertToDocument()1307*8af74909SZhong Yang void * SCI_METHOD Document::ConvertToDocument() {
1308*8af74909SZhong Yang 	return this;
1309*8af74909SZhong Yang }
1310*8af74909SZhong Yang 
Undo()1311*8af74909SZhong Yang Sci::Position Document::Undo() {
1312*8af74909SZhong Yang 	Sci::Position newPos = -1;
1313*8af74909SZhong Yang 	CheckReadOnly();
1314*8af74909SZhong Yang 	if ((enteredModification == 0) && (cb.IsCollectingUndo())) {
1315*8af74909SZhong Yang 		enteredModification++;
1316*8af74909SZhong Yang 		if (!cb.IsReadOnly()) {
1317*8af74909SZhong Yang 			const bool startSavePoint = cb.IsSavePoint();
1318*8af74909SZhong Yang 			bool multiLine = false;
1319*8af74909SZhong Yang 			const int steps = cb.StartUndo();
1320*8af74909SZhong Yang 			//Platform::DebugPrintf("Steps=%d\n", steps);
1321*8af74909SZhong Yang 			Sci::Position coalescedRemovePos = -1;
1322*8af74909SZhong Yang 			Sci::Position coalescedRemoveLen = 0;
1323*8af74909SZhong Yang 			Sci::Position prevRemoveActionPos = -1;
1324*8af74909SZhong Yang 			Sci::Position prevRemoveActionLen = 0;
1325*8af74909SZhong Yang 			for (int step = 0; step < steps; step++) {
1326*8af74909SZhong Yang 				const Sci::Line prevLinesTotal = LinesTotal();
1327*8af74909SZhong Yang 				const Action &action = cb.GetUndoStep();
1328*8af74909SZhong Yang 				if (action.at == removeAction) {
1329*8af74909SZhong Yang 					NotifyModified(DocModification(
1330*8af74909SZhong Yang 									SC_MOD_BEFOREINSERT | SC_PERFORMED_UNDO, action));
1331*8af74909SZhong Yang 				} else if (action.at == containerAction) {
1332*8af74909SZhong Yang 					DocModification dm(SC_MOD_CONTAINER | SC_PERFORMED_UNDO);
1333*8af74909SZhong Yang 					dm.token = action.position;
1334*8af74909SZhong Yang 					NotifyModified(dm);
1335*8af74909SZhong Yang 					if (!action.mayCoalesce) {
1336*8af74909SZhong Yang 						coalescedRemovePos = -1;
1337*8af74909SZhong Yang 						coalescedRemoveLen = 0;
1338*8af74909SZhong Yang 						prevRemoveActionPos = -1;
1339*8af74909SZhong Yang 						prevRemoveActionLen = 0;
1340*8af74909SZhong Yang 					}
1341*8af74909SZhong Yang 				} else {
1342*8af74909SZhong Yang 					NotifyModified(DocModification(
1343*8af74909SZhong Yang 									SC_MOD_BEFOREDELETE | SC_PERFORMED_UNDO, action));
1344*8af74909SZhong Yang 				}
1345*8af74909SZhong Yang 				cb.PerformUndoStep();
1346*8af74909SZhong Yang 				if (action.at != containerAction) {
1347*8af74909SZhong Yang 					ModifiedAt(action.position);
1348*8af74909SZhong Yang 					newPos = action.position;
1349*8af74909SZhong Yang 				}
1350*8af74909SZhong Yang 
1351*8af74909SZhong Yang 				int modFlags = SC_PERFORMED_UNDO;
1352*8af74909SZhong Yang 				// With undo, an insertion action becomes a deletion notification
1353*8af74909SZhong Yang 				if (action.at == removeAction) {
1354*8af74909SZhong Yang 					newPos += action.lenData;
1355*8af74909SZhong Yang 					modFlags |= SC_MOD_INSERTTEXT;
1356*8af74909SZhong Yang 					if ((coalescedRemoveLen > 0) &&
1357*8af74909SZhong Yang 						(action.position == prevRemoveActionPos || action.position == (prevRemoveActionPos + prevRemoveActionLen))) {
1358*8af74909SZhong Yang 						coalescedRemoveLen += action.lenData;
1359*8af74909SZhong Yang 						newPos = coalescedRemovePos + coalescedRemoveLen;
1360*8af74909SZhong Yang 					} else {
1361*8af74909SZhong Yang 						coalescedRemovePos = action.position;
1362*8af74909SZhong Yang 						coalescedRemoveLen = action.lenData;
1363*8af74909SZhong Yang 					}
1364*8af74909SZhong Yang 					prevRemoveActionPos = action.position;
1365*8af74909SZhong Yang 					prevRemoveActionLen = action.lenData;
1366*8af74909SZhong Yang 				} else if (action.at == insertAction) {
1367*8af74909SZhong Yang 					modFlags |= SC_MOD_DELETETEXT;
1368*8af74909SZhong Yang 					coalescedRemovePos = -1;
1369*8af74909SZhong Yang 					coalescedRemoveLen = 0;
1370*8af74909SZhong Yang 					prevRemoveActionPos = -1;
1371*8af74909SZhong Yang 					prevRemoveActionLen = 0;
1372*8af74909SZhong Yang 				}
1373*8af74909SZhong Yang 				if (steps > 1)
1374*8af74909SZhong Yang 					modFlags |= SC_MULTISTEPUNDOREDO;
1375*8af74909SZhong Yang 				const Sci::Line linesAdded = LinesTotal() - prevLinesTotal;
1376*8af74909SZhong Yang 				if (linesAdded != 0)
1377*8af74909SZhong Yang 					multiLine = true;
1378*8af74909SZhong Yang 				if (step == steps - 1) {
1379*8af74909SZhong Yang 					modFlags |= SC_LASTSTEPINUNDOREDO;
1380*8af74909SZhong Yang 					if (multiLine)
1381*8af74909SZhong Yang 						modFlags |= SC_MULTILINEUNDOREDO;
1382*8af74909SZhong Yang 				}
1383*8af74909SZhong Yang 				NotifyModified(DocModification(modFlags, action.position, action.lenData,
1384*8af74909SZhong Yang 											   linesAdded, action.data.get()));
1385*8af74909SZhong Yang 			}
1386*8af74909SZhong Yang 
1387*8af74909SZhong Yang 			const bool endSavePoint = cb.IsSavePoint();
1388*8af74909SZhong Yang 			if (startSavePoint != endSavePoint)
1389*8af74909SZhong Yang 				NotifySavePoint(endSavePoint);
1390*8af74909SZhong Yang 		}
1391*8af74909SZhong Yang 		enteredModification--;
1392*8af74909SZhong Yang 	}
1393*8af74909SZhong Yang 	return newPos;
1394*8af74909SZhong Yang }
1395*8af74909SZhong Yang 
Redo()1396*8af74909SZhong Yang Sci::Position Document::Redo() {
1397*8af74909SZhong Yang 	Sci::Position newPos = -1;
1398*8af74909SZhong Yang 	CheckReadOnly();
1399*8af74909SZhong Yang 	if ((enteredModification == 0) && (cb.IsCollectingUndo())) {
1400*8af74909SZhong Yang 		enteredModification++;
1401*8af74909SZhong Yang 		if (!cb.IsReadOnly()) {
1402*8af74909SZhong Yang 			const bool startSavePoint = cb.IsSavePoint();
1403*8af74909SZhong Yang 			bool multiLine = false;
1404*8af74909SZhong Yang 			const int steps = cb.StartRedo();
1405*8af74909SZhong Yang 			for (int step = 0; step < steps; step++) {
1406*8af74909SZhong Yang 				const Sci::Line prevLinesTotal = LinesTotal();
1407*8af74909SZhong Yang 				const Action &action = cb.GetRedoStep();
1408*8af74909SZhong Yang 				if (action.at == insertAction) {
1409*8af74909SZhong Yang 					NotifyModified(DocModification(
1410*8af74909SZhong Yang 									SC_MOD_BEFOREINSERT | SC_PERFORMED_REDO, action));
1411*8af74909SZhong Yang 				} else if (action.at == containerAction) {
1412*8af74909SZhong Yang 					DocModification dm(SC_MOD_CONTAINER | SC_PERFORMED_REDO);
1413*8af74909SZhong Yang 					dm.token = action.position;
1414*8af74909SZhong Yang 					NotifyModified(dm);
1415*8af74909SZhong Yang 				} else {
1416*8af74909SZhong Yang 					NotifyModified(DocModification(
1417*8af74909SZhong Yang 									SC_MOD_BEFOREDELETE | SC_PERFORMED_REDO, action));
1418*8af74909SZhong Yang 				}
1419*8af74909SZhong Yang 				cb.PerformRedoStep();
1420*8af74909SZhong Yang 				if (action.at != containerAction) {
1421*8af74909SZhong Yang 					ModifiedAt(action.position);
1422*8af74909SZhong Yang 					newPos = action.position;
1423*8af74909SZhong Yang 				}
1424*8af74909SZhong Yang 
1425*8af74909SZhong Yang 				int modFlags = SC_PERFORMED_REDO;
1426*8af74909SZhong Yang 				if (action.at == insertAction) {
1427*8af74909SZhong Yang 					newPos += action.lenData;
1428*8af74909SZhong Yang 					modFlags |= SC_MOD_INSERTTEXT;
1429*8af74909SZhong Yang 				} else if (action.at == removeAction) {
1430*8af74909SZhong Yang 					modFlags |= SC_MOD_DELETETEXT;
1431*8af74909SZhong Yang 				}
1432*8af74909SZhong Yang 				if (steps > 1)
1433*8af74909SZhong Yang 					modFlags |= SC_MULTISTEPUNDOREDO;
1434*8af74909SZhong Yang 				const Sci::Line linesAdded = LinesTotal() - prevLinesTotal;
1435*8af74909SZhong Yang 				if (linesAdded != 0)
1436*8af74909SZhong Yang 					multiLine = true;
1437*8af74909SZhong Yang 				if (step == steps - 1) {
1438*8af74909SZhong Yang 					modFlags |= SC_LASTSTEPINUNDOREDO;
1439*8af74909SZhong Yang 					if (multiLine)
1440*8af74909SZhong Yang 						modFlags |= SC_MULTILINEUNDOREDO;
1441*8af74909SZhong Yang 				}
1442*8af74909SZhong Yang 				NotifyModified(
1443*8af74909SZhong Yang 					DocModification(modFlags, action.position, action.lenData,
1444*8af74909SZhong Yang 									linesAdded, action.data.get()));
1445*8af74909SZhong Yang 			}
1446*8af74909SZhong Yang 
1447*8af74909SZhong Yang 			const bool endSavePoint = cb.IsSavePoint();
1448*8af74909SZhong Yang 			if (startSavePoint != endSavePoint)
1449*8af74909SZhong Yang 				NotifySavePoint(endSavePoint);
1450*8af74909SZhong Yang 		}
1451*8af74909SZhong Yang 		enteredModification--;
1452*8af74909SZhong Yang 	}
1453*8af74909SZhong Yang 	return newPos;
1454*8af74909SZhong Yang }
1455*8af74909SZhong Yang 
DelChar(Sci::Position pos)1456*8af74909SZhong Yang void Document::DelChar(Sci::Position pos) {
1457*8af74909SZhong Yang 	DeleteChars(pos, LenChar(pos));
1458*8af74909SZhong Yang }
1459*8af74909SZhong Yang 
DelCharBack(Sci::Position pos)1460*8af74909SZhong Yang void Document::DelCharBack(Sci::Position pos) {
1461*8af74909SZhong Yang 	if (pos <= 0) {
1462*8af74909SZhong Yang 		return;
1463*8af74909SZhong Yang 	} else if (IsCrLf(pos - 2)) {
1464*8af74909SZhong Yang 		DeleteChars(pos - 2, 2);
1465*8af74909SZhong Yang 	} else if (dbcsCodePage) {
1466*8af74909SZhong Yang 		const Sci::Position startChar = NextPosition(pos, -1);
1467*8af74909SZhong Yang 		DeleteChars(startChar, pos - startChar);
1468*8af74909SZhong Yang 	} else {
1469*8af74909SZhong Yang 		DeleteChars(pos - 1, 1);
1470*8af74909SZhong Yang 	}
1471*8af74909SZhong Yang }
1472*8af74909SZhong Yang 
NextTab(Sci::Position pos,Sci::Position tabSize)1473*8af74909SZhong Yang static constexpr Sci::Position NextTab(Sci::Position pos, Sci::Position tabSize) noexcept {
1474*8af74909SZhong Yang 	return ((pos / tabSize) + 1) * tabSize;
1475*8af74909SZhong Yang }
1476*8af74909SZhong Yang 
CreateIndentation(Sci::Position indent,int tabSize,bool insertSpaces)1477*8af74909SZhong Yang static std::string CreateIndentation(Sci::Position indent, int tabSize, bool insertSpaces) {
1478*8af74909SZhong Yang 	std::string indentation;
1479*8af74909SZhong Yang 	if (!insertSpaces) {
1480*8af74909SZhong Yang 		while (indent >= tabSize) {
1481*8af74909SZhong Yang 			indentation += '\t';
1482*8af74909SZhong Yang 			indent -= tabSize;
1483*8af74909SZhong Yang 		}
1484*8af74909SZhong Yang 	}
1485*8af74909SZhong Yang 	while (indent > 0) {
1486*8af74909SZhong Yang 		indentation += ' ';
1487*8af74909SZhong Yang 		indent--;
1488*8af74909SZhong Yang 	}
1489*8af74909SZhong Yang 	return indentation;
1490*8af74909SZhong Yang }
1491*8af74909SZhong Yang 
GetLineIndentation(Sci_Position line)1492*8af74909SZhong Yang int SCI_METHOD Document::GetLineIndentation(Sci_Position line) {
1493*8af74909SZhong Yang 	int indent = 0;
1494*8af74909SZhong Yang 	if ((line >= 0) && (line < LinesTotal())) {
1495*8af74909SZhong Yang 		const Sci::Position lineStart = LineStart(line);
1496*8af74909SZhong Yang 		const Sci::Position length = Length();
1497*8af74909SZhong Yang 		for (Sci::Position i = lineStart; i < length; i++) {
1498*8af74909SZhong Yang 			const char ch = cb.CharAt(i);
1499*8af74909SZhong Yang 			if (ch == ' ')
1500*8af74909SZhong Yang 				indent++;
1501*8af74909SZhong Yang 			else if (ch == '\t')
1502*8af74909SZhong Yang 				indent = static_cast<int>(NextTab(indent, tabInChars));
1503*8af74909SZhong Yang 			else
1504*8af74909SZhong Yang 				return indent;
1505*8af74909SZhong Yang 		}
1506*8af74909SZhong Yang 	}
1507*8af74909SZhong Yang 	return indent;
1508*8af74909SZhong Yang }
1509*8af74909SZhong Yang 
SetLineIndentation(Sci::Line line,Sci::Position indent)1510*8af74909SZhong Yang Sci::Position Document::SetLineIndentation(Sci::Line line, Sci::Position indent) {
1511*8af74909SZhong Yang 	const int indentOfLine = GetLineIndentation(line);
1512*8af74909SZhong Yang 	if (indent < 0)
1513*8af74909SZhong Yang 		indent = 0;
1514*8af74909SZhong Yang 	if (indent != indentOfLine) {
1515*8af74909SZhong Yang 		std::string linebuf = CreateIndentation(indent, tabInChars, !useTabs);
1516*8af74909SZhong Yang 		const Sci::Position thisLineStart = LineStart(line);
1517*8af74909SZhong Yang 		const Sci::Position indentPos = GetLineIndentPosition(line);
1518*8af74909SZhong Yang 		UndoGroup ug(this);
1519*8af74909SZhong Yang 		DeleteChars(thisLineStart, indentPos - thisLineStart);
1520*8af74909SZhong Yang 		return thisLineStart + InsertString(thisLineStart, linebuf.c_str(),
1521*8af74909SZhong Yang 			linebuf.length());
1522*8af74909SZhong Yang 	} else {
1523*8af74909SZhong Yang 		return GetLineIndentPosition(line);
1524*8af74909SZhong Yang 	}
1525*8af74909SZhong Yang }
1526*8af74909SZhong Yang 
GetLineIndentPosition(Sci::Line line) const1527*8af74909SZhong Yang Sci::Position Document::GetLineIndentPosition(Sci::Line line) const {
1528*8af74909SZhong Yang 	if (line < 0)
1529*8af74909SZhong Yang 		return 0;
1530*8af74909SZhong Yang 	Sci::Position pos = LineStart(line);
1531*8af74909SZhong Yang 	const Sci::Position length = Length();
1532*8af74909SZhong Yang 	while ((pos < length) && IsSpaceOrTab(cb.CharAt(pos))) {
1533*8af74909SZhong Yang 		pos++;
1534*8af74909SZhong Yang 	}
1535*8af74909SZhong Yang 	return pos;
1536*8af74909SZhong Yang }
1537*8af74909SZhong Yang 
GetColumn(Sci::Position pos)1538*8af74909SZhong Yang Sci::Position Document::GetColumn(Sci::Position pos) {
1539*8af74909SZhong Yang 	Sci::Position column = 0;
1540*8af74909SZhong Yang 	const Sci::Line line = SciLineFromPosition(pos);
1541*8af74909SZhong Yang 	if ((line >= 0) && (line < LinesTotal())) {
1542*8af74909SZhong Yang 		for (Sci::Position i = LineStart(line); i < pos;) {
1543*8af74909SZhong Yang 			const char ch = cb.CharAt(i);
1544*8af74909SZhong Yang 			if (ch == '\t') {
1545*8af74909SZhong Yang 				column = NextTab(column, tabInChars);
1546*8af74909SZhong Yang 				i++;
1547*8af74909SZhong Yang 			} else if (ch == '\r') {
1548*8af74909SZhong Yang 				return column;
1549*8af74909SZhong Yang 			} else if (ch == '\n') {
1550*8af74909SZhong Yang 				return column;
1551*8af74909SZhong Yang 			} else if (i >= Length()) {
1552*8af74909SZhong Yang 				return column;
1553*8af74909SZhong Yang 			} else {
1554*8af74909SZhong Yang 				column++;
1555*8af74909SZhong Yang 				i = NextPosition(i, 1);
1556*8af74909SZhong Yang 			}
1557*8af74909SZhong Yang 		}
1558*8af74909SZhong Yang 	}
1559*8af74909SZhong Yang 	return column;
1560*8af74909SZhong Yang }
1561*8af74909SZhong Yang 
CountCharacters(Sci::Position startPos,Sci::Position endPos) const1562*8af74909SZhong Yang Sci::Position Document::CountCharacters(Sci::Position startPos, Sci::Position endPos) const noexcept {
1563*8af74909SZhong Yang 	startPos = MovePositionOutsideChar(startPos, 1, false);
1564*8af74909SZhong Yang 	endPos = MovePositionOutsideChar(endPos, -1, false);
1565*8af74909SZhong Yang 	Sci::Position count = 0;
1566*8af74909SZhong Yang 	Sci::Position i = startPos;
1567*8af74909SZhong Yang 	while (i < endPos) {
1568*8af74909SZhong Yang 		count++;
1569*8af74909SZhong Yang 		i = NextPosition(i, 1);
1570*8af74909SZhong Yang 	}
1571*8af74909SZhong Yang 	return count;
1572*8af74909SZhong Yang }
1573*8af74909SZhong Yang 
CountUTF16(Sci::Position startPos,Sci::Position endPos) const1574*8af74909SZhong Yang Sci::Position Document::CountUTF16(Sci::Position startPos, Sci::Position endPos) const noexcept {
1575*8af74909SZhong Yang 	startPos = MovePositionOutsideChar(startPos, 1, false);
1576*8af74909SZhong Yang 	endPos = MovePositionOutsideChar(endPos, -1, false);
1577*8af74909SZhong Yang 	Sci::Position count = 0;
1578*8af74909SZhong Yang 	Sci::Position i = startPos;
1579*8af74909SZhong Yang 	while (i < endPos) {
1580*8af74909SZhong Yang 		count++;
1581*8af74909SZhong Yang 		const Sci::Position next = NextPosition(i, 1);
1582*8af74909SZhong Yang 		if ((next - i) > 3)
1583*8af74909SZhong Yang 			count++;
1584*8af74909SZhong Yang 		i = next;
1585*8af74909SZhong Yang 	}
1586*8af74909SZhong Yang 	return count;
1587*8af74909SZhong Yang }
1588*8af74909SZhong Yang 
FindColumn(Sci::Line line,Sci::Position column)1589*8af74909SZhong Yang Sci::Position Document::FindColumn(Sci::Line line, Sci::Position column) {
1590*8af74909SZhong Yang 	Sci::Position position = LineStart(line);
1591*8af74909SZhong Yang 	if ((line >= 0) && (line < LinesTotal())) {
1592*8af74909SZhong Yang 		Sci::Position columnCurrent = 0;
1593*8af74909SZhong Yang 		while ((columnCurrent < column) && (position < Length())) {
1594*8af74909SZhong Yang 			const char ch = cb.CharAt(position);
1595*8af74909SZhong Yang 			if (ch == '\t') {
1596*8af74909SZhong Yang 				columnCurrent = NextTab(columnCurrent, tabInChars);
1597*8af74909SZhong Yang 				if (columnCurrent > column)
1598*8af74909SZhong Yang 					return position;
1599*8af74909SZhong Yang 				position++;
1600*8af74909SZhong Yang 			} else if (ch == '\r') {
1601*8af74909SZhong Yang 				return position;
1602*8af74909SZhong Yang 			} else if (ch == '\n') {
1603*8af74909SZhong Yang 				return position;
1604*8af74909SZhong Yang 			} else {
1605*8af74909SZhong Yang 				columnCurrent++;
1606*8af74909SZhong Yang 				position = NextPosition(position, 1);
1607*8af74909SZhong Yang 			}
1608*8af74909SZhong Yang 		}
1609*8af74909SZhong Yang 	}
1610*8af74909SZhong Yang 	return position;
1611*8af74909SZhong Yang }
1612*8af74909SZhong Yang 
Indent(bool forwards,Sci::Line lineBottom,Sci::Line lineTop)1613*8af74909SZhong Yang void Document::Indent(bool forwards, Sci::Line lineBottom, Sci::Line lineTop) {
1614*8af74909SZhong Yang 	// Dedent - suck white space off the front of the line to dedent by equivalent of a tab
1615*8af74909SZhong Yang 	for (Sci::Line line = lineBottom; line >= lineTop; line--) {
1616*8af74909SZhong Yang 		const Sci::Position indentOfLine = GetLineIndentation(line);
1617*8af74909SZhong Yang 		if (forwards) {
1618*8af74909SZhong Yang 			if (LineStart(line) < LineEnd(line)) {
1619*8af74909SZhong Yang 				SetLineIndentation(line, indentOfLine + IndentSize());
1620*8af74909SZhong Yang 			}
1621*8af74909SZhong Yang 		} else {
1622*8af74909SZhong Yang 			SetLineIndentation(line, indentOfLine - IndentSize());
1623*8af74909SZhong Yang 		}
1624*8af74909SZhong Yang 	}
1625*8af74909SZhong Yang }
1626*8af74909SZhong Yang 
1627*8af74909SZhong Yang // Convert line endings for a piece of text to a particular mode.
1628*8af74909SZhong Yang // Stop at len or when a NUL is found.
TransformLineEnds(const char * s,size_t len,int eolModeWanted)1629*8af74909SZhong Yang std::string Document::TransformLineEnds(const char *s, size_t len, int eolModeWanted) {
1630*8af74909SZhong Yang 	std::string dest;
1631*8af74909SZhong Yang 	for (size_t i = 0; (i < len) && (s[i]); i++) {
1632*8af74909SZhong Yang 		if (s[i] == '\n' || s[i] == '\r') {
1633*8af74909SZhong Yang 			if (eolModeWanted == SC_EOL_CR) {
1634*8af74909SZhong Yang 				dest.push_back('\r');
1635*8af74909SZhong Yang 			} else if (eolModeWanted == SC_EOL_LF) {
1636*8af74909SZhong Yang 				dest.push_back('\n');
1637*8af74909SZhong Yang 			} else { // eolModeWanted == SC_EOL_CRLF
1638*8af74909SZhong Yang 				dest.push_back('\r');
1639*8af74909SZhong Yang 				dest.push_back('\n');
1640*8af74909SZhong Yang 			}
1641*8af74909SZhong Yang 			if ((s[i] == '\r') && (i+1 < len) && (s[i+1] == '\n')) {
1642*8af74909SZhong Yang 				i++;
1643*8af74909SZhong Yang 			}
1644*8af74909SZhong Yang 		} else {
1645*8af74909SZhong Yang 			dest.push_back(s[i]);
1646*8af74909SZhong Yang 		}
1647*8af74909SZhong Yang 	}
1648*8af74909SZhong Yang 	return dest;
1649*8af74909SZhong Yang }
1650*8af74909SZhong Yang 
ConvertLineEnds(int eolModeSet)1651*8af74909SZhong Yang void Document::ConvertLineEnds(int eolModeSet) {
1652*8af74909SZhong Yang 	UndoGroup ug(this);
1653*8af74909SZhong Yang 
1654*8af74909SZhong Yang 	for (Sci::Position pos = 0; pos < Length(); pos++) {
1655*8af74909SZhong Yang 		if (cb.CharAt(pos) == '\r') {
1656*8af74909SZhong Yang 			if (cb.CharAt(pos + 1) == '\n') {
1657*8af74909SZhong Yang 				// CRLF
1658*8af74909SZhong Yang 				if (eolModeSet == SC_EOL_CR) {
1659*8af74909SZhong Yang 					DeleteChars(pos + 1, 1); // Delete the LF
1660*8af74909SZhong Yang 				} else if (eolModeSet == SC_EOL_LF) {
1661*8af74909SZhong Yang 					DeleteChars(pos, 1); // Delete the CR
1662*8af74909SZhong Yang 				} else {
1663*8af74909SZhong Yang 					pos++;
1664*8af74909SZhong Yang 				}
1665*8af74909SZhong Yang 			} else {
1666*8af74909SZhong Yang 				// CR
1667*8af74909SZhong Yang 				if (eolModeSet == SC_EOL_CRLF) {
1668*8af74909SZhong Yang 					pos += InsertString(pos + 1, "\n", 1); // Insert LF
1669*8af74909SZhong Yang 				} else if (eolModeSet == SC_EOL_LF) {
1670*8af74909SZhong Yang 					pos += InsertString(pos, "\n", 1); // Insert LF
1671*8af74909SZhong Yang 					DeleteChars(pos, 1); // Delete CR
1672*8af74909SZhong Yang 					pos--;
1673*8af74909SZhong Yang 				}
1674*8af74909SZhong Yang 			}
1675*8af74909SZhong Yang 		} else if (cb.CharAt(pos) == '\n') {
1676*8af74909SZhong Yang 			// LF
1677*8af74909SZhong Yang 			if (eolModeSet == SC_EOL_CRLF) {
1678*8af74909SZhong Yang 				pos += InsertString(pos, "\r", 1); // Insert CR
1679*8af74909SZhong Yang 			} else if (eolModeSet == SC_EOL_CR) {
1680*8af74909SZhong Yang 				pos += InsertString(pos, "\r", 1); // Insert CR
1681*8af74909SZhong Yang 				DeleteChars(pos, 1); // Delete LF
1682*8af74909SZhong Yang 				pos--;
1683*8af74909SZhong Yang 			}
1684*8af74909SZhong Yang 		}
1685*8af74909SZhong Yang 	}
1686*8af74909SZhong Yang 
1687*8af74909SZhong Yang }
1688*8af74909SZhong Yang 
Options() const1689*8af74909SZhong Yang int Document::Options() const noexcept {
1690*8af74909SZhong Yang 	return (IsLarge() ? SC_DOCUMENTOPTION_TEXT_LARGE : 0) |
1691*8af74909SZhong Yang 		(cb.HasStyles() ? 0 : SC_DOCUMENTOPTION_STYLES_NONE);
1692*8af74909SZhong Yang }
1693*8af74909SZhong Yang 
IsWhiteLine(Sci::Line line) const1694*8af74909SZhong Yang bool Document::IsWhiteLine(Sci::Line line) const {
1695*8af74909SZhong Yang 	Sci::Position currentChar = LineStart(line);
1696*8af74909SZhong Yang 	const Sci::Position endLine = LineEnd(line);
1697*8af74909SZhong Yang 	while (currentChar < endLine) {
1698*8af74909SZhong Yang 		if (!IsSpaceOrTab(cb.CharAt(currentChar))) {
1699*8af74909SZhong Yang 			return false;
1700*8af74909SZhong Yang 		}
1701*8af74909SZhong Yang 		++currentChar;
1702*8af74909SZhong Yang 	}
1703*8af74909SZhong Yang 	return true;
1704*8af74909SZhong Yang }
1705*8af74909SZhong Yang 
ParaUp(Sci::Position pos) const1706*8af74909SZhong Yang Sci::Position Document::ParaUp(Sci::Position pos) const {
1707*8af74909SZhong Yang 	Sci::Line line = SciLineFromPosition(pos);
1708*8af74909SZhong Yang 	line--;
1709*8af74909SZhong Yang 	while (line >= 0 && IsWhiteLine(line)) { // skip empty lines
1710*8af74909SZhong Yang 		line--;
1711*8af74909SZhong Yang 	}
1712*8af74909SZhong Yang 	while (line >= 0 && !IsWhiteLine(line)) { // skip non-empty lines
1713*8af74909SZhong Yang 		line--;
1714*8af74909SZhong Yang 	}
1715*8af74909SZhong Yang 	line++;
1716*8af74909SZhong Yang 	return LineStart(line);
1717*8af74909SZhong Yang }
1718*8af74909SZhong Yang 
ParaDown(Sci::Position pos) const1719*8af74909SZhong Yang Sci::Position Document::ParaDown(Sci::Position pos) const {
1720*8af74909SZhong Yang 	Sci::Line line = SciLineFromPosition(pos);
1721*8af74909SZhong Yang 	while (line < LinesTotal() && !IsWhiteLine(line)) { // skip non-empty lines
1722*8af74909SZhong Yang 		line++;
1723*8af74909SZhong Yang 	}
1724*8af74909SZhong Yang 	while (line < LinesTotal() && IsWhiteLine(line)) { // skip empty lines
1725*8af74909SZhong Yang 		line++;
1726*8af74909SZhong Yang 	}
1727*8af74909SZhong Yang 	if (line < LinesTotal())
1728*8af74909SZhong Yang 		return LineStart(line);
1729*8af74909SZhong Yang 	else // end of a document
1730*8af74909SZhong Yang 		return LineEnd(line-1);
1731*8af74909SZhong Yang }
1732*8af74909SZhong Yang 
WordCharacterClass(unsigned int ch) const1733*8af74909SZhong Yang CharClassify::cc Document::WordCharacterClass(unsigned int ch) const {
1734*8af74909SZhong Yang 	if (dbcsCodePage && (!UTF8IsAscii(ch))) {
1735*8af74909SZhong Yang 		if (SC_CP_UTF8 == dbcsCodePage) {
1736*8af74909SZhong Yang 			// Use hard coded Unicode class
1737*8af74909SZhong Yang 			const CharacterCategory cc = charMap.CategoryFor(ch);
1738*8af74909SZhong Yang 			switch (cc) {
1739*8af74909SZhong Yang 
1740*8af74909SZhong Yang 				// Separator, Line/Paragraph
1741*8af74909SZhong Yang 			case ccZl:
1742*8af74909SZhong Yang 			case ccZp:
1743*8af74909SZhong Yang 				return CharClassify::ccNewLine;
1744*8af74909SZhong Yang 
1745*8af74909SZhong Yang 				// Separator, Space
1746*8af74909SZhong Yang 			case ccZs:
1747*8af74909SZhong Yang 				// Other
1748*8af74909SZhong Yang 			case ccCc:
1749*8af74909SZhong Yang 			case ccCf:
1750*8af74909SZhong Yang 			case ccCs:
1751*8af74909SZhong Yang 			case ccCo:
1752*8af74909SZhong Yang 			case ccCn:
1753*8af74909SZhong Yang 				return CharClassify::ccSpace;
1754*8af74909SZhong Yang 
1755*8af74909SZhong Yang 				// Letter
1756*8af74909SZhong Yang 			case ccLu:
1757*8af74909SZhong Yang 			case ccLl:
1758*8af74909SZhong Yang 			case ccLt:
1759*8af74909SZhong Yang 			case ccLm:
1760*8af74909SZhong Yang 			case ccLo:
1761*8af74909SZhong Yang 				// Number
1762*8af74909SZhong Yang 			case ccNd:
1763*8af74909SZhong Yang 			case ccNl:
1764*8af74909SZhong Yang 			case ccNo:
1765*8af74909SZhong Yang 				// Mark - includes combining diacritics
1766*8af74909SZhong Yang 			case ccMn:
1767*8af74909SZhong Yang 			case ccMc:
1768*8af74909SZhong Yang 			case ccMe:
1769*8af74909SZhong Yang 				return CharClassify::ccWord;
1770*8af74909SZhong Yang 
1771*8af74909SZhong Yang 				// Punctuation
1772*8af74909SZhong Yang 			case ccPc:
1773*8af74909SZhong Yang 			case ccPd:
1774*8af74909SZhong Yang 			case ccPs:
1775*8af74909SZhong Yang 			case ccPe:
1776*8af74909SZhong Yang 			case ccPi:
1777*8af74909SZhong Yang 			case ccPf:
1778*8af74909SZhong Yang 			case ccPo:
1779*8af74909SZhong Yang 				// Symbol
1780*8af74909SZhong Yang 			case ccSm:
1781*8af74909SZhong Yang 			case ccSc:
1782*8af74909SZhong Yang 			case ccSk:
1783*8af74909SZhong Yang 			case ccSo:
1784*8af74909SZhong Yang 				return CharClassify::ccPunctuation;
1785*8af74909SZhong Yang 
1786*8af74909SZhong Yang 			}
1787*8af74909SZhong Yang 		} else {
1788*8af74909SZhong Yang 			// Asian DBCS
1789*8af74909SZhong Yang 			return CharClassify::ccWord;
1790*8af74909SZhong Yang 		}
1791*8af74909SZhong Yang 	}
1792*8af74909SZhong Yang 	return charClass.GetClass(static_cast<unsigned char>(ch));
1793*8af74909SZhong Yang }
1794*8af74909SZhong Yang 
1795*8af74909SZhong Yang /**
1796*8af74909SZhong Yang  * Used by commands that want to select whole words.
1797*8af74909SZhong Yang  * Finds the start of word at pos when delta < 0 or the end of the word when delta >= 0.
1798*8af74909SZhong Yang  */
ExtendWordSelect(Sci::Position pos,int delta,bool onlyWordCharacters) const1799*8af74909SZhong Yang Sci::Position Document::ExtendWordSelect(Sci::Position pos, int delta, bool onlyWordCharacters) const {
1800*8af74909SZhong Yang 	CharClassify::cc ccStart = CharClassify::ccWord;
1801*8af74909SZhong Yang 	if (delta < 0) {
1802*8af74909SZhong Yang 		if (!onlyWordCharacters) {
1803*8af74909SZhong Yang 			const CharacterExtracted ce = CharacterBefore(pos);
1804*8af74909SZhong Yang 			ccStart = WordCharacterClass(ce.character);
1805*8af74909SZhong Yang 		}
1806*8af74909SZhong Yang 		while (pos > 0) {
1807*8af74909SZhong Yang 			const CharacterExtracted ce = CharacterBefore(pos);
1808*8af74909SZhong Yang 			if (WordCharacterClass(ce.character) != ccStart)
1809*8af74909SZhong Yang 				break;
1810*8af74909SZhong Yang 			pos -= ce.widthBytes;
1811*8af74909SZhong Yang 		}
1812*8af74909SZhong Yang 	} else {
1813*8af74909SZhong Yang 		if (!onlyWordCharacters && pos < LengthNoExcept()) {
1814*8af74909SZhong Yang 			const CharacterExtracted ce = CharacterAfter(pos);
1815*8af74909SZhong Yang 			ccStart = WordCharacterClass(ce.character);
1816*8af74909SZhong Yang 		}
1817*8af74909SZhong Yang 		while (pos < LengthNoExcept()) {
1818*8af74909SZhong Yang 			const CharacterExtracted ce = CharacterAfter(pos);
1819*8af74909SZhong Yang 			if (WordCharacterClass(ce.character) != ccStart)
1820*8af74909SZhong Yang 				break;
1821*8af74909SZhong Yang 			pos += ce.widthBytes;
1822*8af74909SZhong Yang 		}
1823*8af74909SZhong Yang 	}
1824*8af74909SZhong Yang 	return MovePositionOutsideChar(pos, delta, true);
1825*8af74909SZhong Yang }
1826*8af74909SZhong Yang 
1827*8af74909SZhong Yang /**
1828*8af74909SZhong Yang  * Find the start of the next word in either a forward (delta >= 0) or backwards direction
1829*8af74909SZhong Yang  * (delta < 0).
1830*8af74909SZhong Yang  * This is looking for a transition between character classes although there is also some
1831*8af74909SZhong Yang  * additional movement to transit white space.
1832*8af74909SZhong Yang  * Used by cursor movement by word commands.
1833*8af74909SZhong Yang  */
NextWordStart(Sci::Position pos,int delta) const1834*8af74909SZhong Yang Sci::Position Document::NextWordStart(Sci::Position pos, int delta) const {
1835*8af74909SZhong Yang 	if (delta < 0) {
1836*8af74909SZhong Yang 		while (pos > 0) {
1837*8af74909SZhong Yang 			const CharacterExtracted ce = CharacterBefore(pos);
1838*8af74909SZhong Yang 			if (WordCharacterClass(ce.character) != CharClassify::ccSpace)
1839*8af74909SZhong Yang 				break;
1840*8af74909SZhong Yang 			pos -= ce.widthBytes;
1841*8af74909SZhong Yang 		}
1842*8af74909SZhong Yang 		if (pos > 0) {
1843*8af74909SZhong Yang 			CharacterExtracted ce = CharacterBefore(pos);
1844*8af74909SZhong Yang 			const CharClassify::cc ccStart = WordCharacterClass(ce.character);
1845*8af74909SZhong Yang 			while (pos > 0) {
1846*8af74909SZhong Yang 				ce = CharacterBefore(pos);
1847*8af74909SZhong Yang 				if (WordCharacterClass(ce.character) != ccStart)
1848*8af74909SZhong Yang 					break;
1849*8af74909SZhong Yang 				pos -= ce.widthBytes;
1850*8af74909SZhong Yang 			}
1851*8af74909SZhong Yang 		}
1852*8af74909SZhong Yang 	} else {
1853*8af74909SZhong Yang 		CharacterExtracted ce = CharacterAfter(pos);
1854*8af74909SZhong Yang 		const CharClassify::cc ccStart = WordCharacterClass(ce.character);
1855*8af74909SZhong Yang 		while (pos < LengthNoExcept()) {
1856*8af74909SZhong Yang 			ce = CharacterAfter(pos);
1857*8af74909SZhong Yang 			if (WordCharacterClass(ce.character) != ccStart)
1858*8af74909SZhong Yang 				break;
1859*8af74909SZhong Yang 			pos += ce.widthBytes;
1860*8af74909SZhong Yang 		}
1861*8af74909SZhong Yang 		while (pos < LengthNoExcept()) {
1862*8af74909SZhong Yang 			ce = CharacterAfter(pos);
1863*8af74909SZhong Yang 			if (WordCharacterClass(ce.character) != CharClassify::ccSpace)
1864*8af74909SZhong Yang 				break;
1865*8af74909SZhong Yang 			pos += ce.widthBytes;
1866*8af74909SZhong Yang 		}
1867*8af74909SZhong Yang 	}
1868*8af74909SZhong Yang 	return pos;
1869*8af74909SZhong Yang }
1870*8af74909SZhong Yang 
1871*8af74909SZhong Yang /**
1872*8af74909SZhong Yang  * Find the end of the next word in either a forward (delta >= 0) or backwards direction
1873*8af74909SZhong Yang  * (delta < 0).
1874*8af74909SZhong Yang  * This is looking for a transition between character classes although there is also some
1875*8af74909SZhong Yang  * additional movement to transit white space.
1876*8af74909SZhong Yang  * Used by cursor movement by word commands.
1877*8af74909SZhong Yang  */
NextWordEnd(Sci::Position pos,int delta) const1878*8af74909SZhong Yang Sci::Position Document::NextWordEnd(Sci::Position pos, int delta) const {
1879*8af74909SZhong Yang 	if (delta < 0) {
1880*8af74909SZhong Yang 		if (pos > 0) {
1881*8af74909SZhong Yang 			CharacterExtracted ce = CharacterBefore(pos);
1882*8af74909SZhong Yang 			const CharClassify::cc ccStart = WordCharacterClass(ce.character);
1883*8af74909SZhong Yang 			if (ccStart != CharClassify::ccSpace) {
1884*8af74909SZhong Yang 				while (pos > 0) {
1885*8af74909SZhong Yang 					ce = CharacterBefore(pos);
1886*8af74909SZhong Yang 					if (WordCharacterClass(ce.character) != ccStart)
1887*8af74909SZhong Yang 						break;
1888*8af74909SZhong Yang 					pos -= ce.widthBytes;
1889*8af74909SZhong Yang 				}
1890*8af74909SZhong Yang 			}
1891*8af74909SZhong Yang 			while (pos > 0) {
1892*8af74909SZhong Yang 				ce = CharacterBefore(pos);
1893*8af74909SZhong Yang 				if (WordCharacterClass(ce.character) != CharClassify::ccSpace)
1894*8af74909SZhong Yang 					break;
1895*8af74909SZhong Yang 				pos -= ce.widthBytes;
1896*8af74909SZhong Yang 			}
1897*8af74909SZhong Yang 		}
1898*8af74909SZhong Yang 	} else {
1899*8af74909SZhong Yang 		while (pos < LengthNoExcept()) {
1900*8af74909SZhong Yang 			const CharacterExtracted ce = CharacterAfter(pos);
1901*8af74909SZhong Yang 			if (WordCharacterClass(ce.character) != CharClassify::ccSpace)
1902*8af74909SZhong Yang 				break;
1903*8af74909SZhong Yang 			pos += ce.widthBytes;
1904*8af74909SZhong Yang 		}
1905*8af74909SZhong Yang 		if (pos < LengthNoExcept()) {
1906*8af74909SZhong Yang 			CharacterExtracted ce = CharacterAfter(pos);
1907*8af74909SZhong Yang 			const CharClassify::cc ccStart = WordCharacterClass(ce.character);
1908*8af74909SZhong Yang 			while (pos < LengthNoExcept()) {
1909*8af74909SZhong Yang 				ce = CharacterAfter(pos);
1910*8af74909SZhong Yang 				if (WordCharacterClass(ce.character) != ccStart)
1911*8af74909SZhong Yang 					break;
1912*8af74909SZhong Yang 				pos += ce.widthBytes;
1913*8af74909SZhong Yang 			}
1914*8af74909SZhong Yang 		}
1915*8af74909SZhong Yang 	}
1916*8af74909SZhong Yang 	return pos;
1917*8af74909SZhong Yang }
1918*8af74909SZhong Yang 
1919*8af74909SZhong Yang /**
1920*8af74909SZhong Yang  * Check that the character at the given position is a word or punctuation character and that
1921*8af74909SZhong Yang  * the previous character is of a different character class.
1922*8af74909SZhong Yang  */
IsWordStartAt(Sci::Position pos) const1923*8af74909SZhong Yang bool Document::IsWordStartAt(Sci::Position pos) const {
1924*8af74909SZhong Yang 	if (pos >= LengthNoExcept())
1925*8af74909SZhong Yang 		return false;
1926*8af74909SZhong Yang 	if (pos > 0) {
1927*8af74909SZhong Yang 		const CharacterExtracted cePos = CharacterAfter(pos);
1928*8af74909SZhong Yang 		const CharClassify::cc ccPos = WordCharacterClass(cePos.character);
1929*8af74909SZhong Yang 		const CharacterExtracted cePrev = CharacterBefore(pos);
1930*8af74909SZhong Yang 		const CharClassify::cc ccPrev = WordCharacterClass(cePrev.character);
1931*8af74909SZhong Yang 		return (ccPos == CharClassify::ccWord || ccPos == CharClassify::ccPunctuation) &&
1932*8af74909SZhong Yang 			(ccPos != ccPrev);
1933*8af74909SZhong Yang 	}
1934*8af74909SZhong Yang 	return true;
1935*8af74909SZhong Yang }
1936*8af74909SZhong Yang 
1937*8af74909SZhong Yang /**
1938*8af74909SZhong Yang  * Check that the character at the given position is a word or punctuation character and that
1939*8af74909SZhong Yang  * the next character is of a different character class.
1940*8af74909SZhong Yang  */
IsWordEndAt(Sci::Position pos) const1941*8af74909SZhong Yang bool Document::IsWordEndAt(Sci::Position pos) const {
1942*8af74909SZhong Yang 	if (pos <= 0)
1943*8af74909SZhong Yang 		return false;
1944*8af74909SZhong Yang 	if (pos < LengthNoExcept()) {
1945*8af74909SZhong Yang 		const CharacterExtracted cePos = CharacterAfter(pos);
1946*8af74909SZhong Yang 		const CharClassify::cc ccPos = WordCharacterClass(cePos.character);
1947*8af74909SZhong Yang 		const CharacterExtracted cePrev = CharacterBefore(pos);
1948*8af74909SZhong Yang 		const CharClassify::cc ccPrev = WordCharacterClass(cePrev.character);
1949*8af74909SZhong Yang 		return (ccPrev == CharClassify::ccWord || ccPrev == CharClassify::ccPunctuation) &&
1950*8af74909SZhong Yang 			(ccPrev != ccPos);
1951*8af74909SZhong Yang 	}
1952*8af74909SZhong Yang 	return true;
1953*8af74909SZhong Yang }
1954*8af74909SZhong Yang 
1955*8af74909SZhong Yang /**
1956*8af74909SZhong Yang  * Check that the given range is has transitions between character classes at both
1957*8af74909SZhong Yang  * ends and where the characters on the inside are word or punctuation characters.
1958*8af74909SZhong Yang  */
IsWordAt(Sci::Position start,Sci::Position end) const1959*8af74909SZhong Yang bool Document::IsWordAt(Sci::Position start, Sci::Position end) const {
1960*8af74909SZhong Yang 	return (start < end) && IsWordStartAt(start) && IsWordEndAt(end);
1961*8af74909SZhong Yang }
1962*8af74909SZhong Yang 
MatchesWordOptions(bool word,bool wordStart,Sci::Position pos,Sci::Position length) const1963*8af74909SZhong Yang bool Document::MatchesWordOptions(bool word, bool wordStart, Sci::Position pos, Sci::Position length) const {
1964*8af74909SZhong Yang 	return (!word && !wordStart) ||
1965*8af74909SZhong Yang 			(word && IsWordAt(pos, pos + length)) ||
1966*8af74909SZhong Yang 			(wordStart && IsWordStartAt(pos));
1967*8af74909SZhong Yang }
1968*8af74909SZhong Yang 
HasCaseFolder() const1969*8af74909SZhong Yang bool Document::HasCaseFolder() const noexcept {
1970*8af74909SZhong Yang 	return pcf != nullptr;
1971*8af74909SZhong Yang }
1972*8af74909SZhong Yang 
SetCaseFolder(CaseFolder * pcf_)1973*8af74909SZhong Yang void Document::SetCaseFolder(CaseFolder *pcf_) noexcept {
1974*8af74909SZhong Yang 	pcf.reset(pcf_);
1975*8af74909SZhong Yang }
1976*8af74909SZhong Yang 
ExtractCharacter(Sci::Position position) const1977*8af74909SZhong Yang Document::CharacterExtracted Document::ExtractCharacter(Sci::Position position) const noexcept {
1978*8af74909SZhong Yang 	const unsigned char leadByte = cb.UCharAt(position);
1979*8af74909SZhong Yang 	if (UTF8IsAscii(leadByte)) {
1980*8af74909SZhong Yang 		// Common case: ASCII character
1981*8af74909SZhong Yang 		return CharacterExtracted(leadByte, 1);
1982*8af74909SZhong Yang 	}
1983*8af74909SZhong Yang 	const int widthCharBytes = UTF8BytesOfLead[leadByte];
1984*8af74909SZhong Yang 	unsigned char charBytes[UTF8MaxBytes] = { leadByte, 0, 0, 0 };
1985*8af74909SZhong Yang 	for (int b=1; b<widthCharBytes; b++)
1986*8af74909SZhong Yang 		charBytes[b] = cb.UCharAt(position + b);
1987*8af74909SZhong Yang 	const int utf8status = UTF8Classify(charBytes, widthCharBytes);
1988*8af74909SZhong Yang 	if (utf8status & UTF8MaskInvalid) {
1989*8af74909SZhong Yang 		// Treat as invalid and use up just one byte
1990*8af74909SZhong Yang 		return CharacterExtracted(unicodeReplacementChar, 1);
1991*8af74909SZhong Yang 	} else {
1992*8af74909SZhong Yang 		return CharacterExtracted(UnicodeFromUTF8(charBytes), utf8status & UTF8MaskWidth);
1993*8af74909SZhong Yang 	}
1994*8af74909SZhong Yang }
1995*8af74909SZhong Yang 
1996*8af74909SZhong Yang /**
1997*8af74909SZhong Yang  * Find text in document, supporting both forward and backward
1998*8af74909SZhong Yang  * searches (just pass minPos > maxPos to do a backward search)
1999*8af74909SZhong Yang  * Has not been tested with backwards DBCS searches yet.
2000*8af74909SZhong Yang  */
FindText(Sci::Position minPos,Sci::Position maxPos,const char * search,int flags,Sci::Position * length)2001*8af74909SZhong Yang Sci::Position Document::FindText(Sci::Position minPos, Sci::Position maxPos, const char *search,
2002*8af74909SZhong Yang                         int flags, Sci::Position *length) {
2003*8af74909SZhong Yang 	if (*length <= 0)
2004*8af74909SZhong Yang 		return minPos;
2005*8af74909SZhong Yang 	const bool caseSensitive = (flags & SCFIND_MATCHCASE) != 0;
2006*8af74909SZhong Yang 	const bool word = (flags & SCFIND_WHOLEWORD) != 0;
2007*8af74909SZhong Yang 	const bool wordStart = (flags & SCFIND_WORDSTART) != 0;
2008*8af74909SZhong Yang 	const bool regExp = (flags & SCFIND_REGEXP) != 0;
2009*8af74909SZhong Yang 	if (regExp) {
2010*8af74909SZhong Yang 		if (!regex)
2011*8af74909SZhong Yang 			regex = std::unique_ptr<RegexSearchBase>(CreateRegexSearch(&charClass));
2012*8af74909SZhong Yang 		return regex->FindText(this, minPos, maxPos, search, caseSensitive, word, wordStart, flags, length);
2013*8af74909SZhong Yang 	} else {
2014*8af74909SZhong Yang 
2015*8af74909SZhong Yang 		const bool forward = minPos <= maxPos;
2016*8af74909SZhong Yang 		const int increment = forward ? 1 : -1;
2017*8af74909SZhong Yang 
2018*8af74909SZhong Yang 		// Range endpoints should not be inside DBCS characters, but just in case, move them.
2019*8af74909SZhong Yang 		const Sci::Position startPos = MovePositionOutsideChar(minPos, increment, false);
2020*8af74909SZhong Yang 		const Sci::Position endPos = MovePositionOutsideChar(maxPos, increment, false);
2021*8af74909SZhong Yang 
2022*8af74909SZhong Yang 		// Compute actual search ranges needed
2023*8af74909SZhong Yang 		const Sci::Position lengthFind = *length;
2024*8af74909SZhong Yang 
2025*8af74909SZhong Yang 		//Platform::DebugPrintf("Find %d %d %s %d\n", startPos, endPos, ft->lpstrText, lengthFind);
2026*8af74909SZhong Yang 		const Sci::Position limitPos = std::max(startPos, endPos);
2027*8af74909SZhong Yang 		Sci::Position pos = startPos;
2028*8af74909SZhong Yang 		if (!forward) {
2029*8af74909SZhong Yang 			// Back all of a character
2030*8af74909SZhong Yang 			pos = NextPosition(pos, increment);
2031*8af74909SZhong Yang 		}
2032*8af74909SZhong Yang 		if (caseSensitive) {
2033*8af74909SZhong Yang 			const Sci::Position endSearch = (startPos <= endPos) ? endPos - lengthFind + 1 : endPos;
2034*8af74909SZhong Yang 			const char charStartSearch =  search[0];
2035*8af74909SZhong Yang 			while (forward ? (pos < endSearch) : (pos >= endSearch)) {
2036*8af74909SZhong Yang 				if (CharAt(pos) == charStartSearch) {
2037*8af74909SZhong Yang 					bool found = (pos + lengthFind) <= limitPos;
2038*8af74909SZhong Yang 					for (int indexSearch = 1; (indexSearch < lengthFind) && found; indexSearch++) {
2039*8af74909SZhong Yang 						found = CharAt(pos + indexSearch) == search[indexSearch];
2040*8af74909SZhong Yang 					}
2041*8af74909SZhong Yang 					if (found && MatchesWordOptions(word, wordStart, pos, lengthFind)) {
2042*8af74909SZhong Yang 						return pos;
2043*8af74909SZhong Yang 					}
2044*8af74909SZhong Yang 				}
2045*8af74909SZhong Yang 				if (!NextCharacter(pos, increment))
2046*8af74909SZhong Yang 					break;
2047*8af74909SZhong Yang 			}
2048*8af74909SZhong Yang 		} else if (SC_CP_UTF8 == dbcsCodePage) {
2049*8af74909SZhong Yang 			constexpr size_t maxFoldingExpansion = 4;
2050*8af74909SZhong Yang 			std::vector<char> searchThing((lengthFind+1) * UTF8MaxBytes * maxFoldingExpansion + 1);
2051*8af74909SZhong Yang 			const size_t lenSearch =
2052*8af74909SZhong Yang 				pcf->Fold(&searchThing[0], searchThing.size(), search, lengthFind);
2053*8af74909SZhong Yang 			char bytes[UTF8MaxBytes + 1] = "";
2054*8af74909SZhong Yang 			char folded[UTF8MaxBytes * maxFoldingExpansion + 1] = "";
2055*8af74909SZhong Yang 			while (forward ? (pos < endPos) : (pos >= endPos)) {
2056*8af74909SZhong Yang 				int widthFirstCharacter = 0;
2057*8af74909SZhong Yang 				Sci::Position posIndexDocument = pos;
2058*8af74909SZhong Yang 				size_t indexSearch = 0;
2059*8af74909SZhong Yang 				bool characterMatches = true;
2060*8af74909SZhong Yang 				for (;;) {
2061*8af74909SZhong Yang 					const unsigned char leadByte = cb.UCharAt(posIndexDocument);
2062*8af74909SZhong Yang 					bytes[0] = leadByte;
2063*8af74909SZhong Yang 					int widthChar = 1;
2064*8af74909SZhong Yang 					if (!UTF8IsAscii(leadByte)) {
2065*8af74909SZhong Yang 						const int widthCharBytes = UTF8BytesOfLead[leadByte];
2066*8af74909SZhong Yang 						for (int b=1; b<widthCharBytes; b++) {
2067*8af74909SZhong Yang 							bytes[b] = cb.CharAt(posIndexDocument+b);
2068*8af74909SZhong Yang 						}
2069*8af74909SZhong Yang 						widthChar = UTF8Classify(reinterpret_cast<const unsigned char *>(bytes), widthCharBytes) & UTF8MaskWidth;
2070*8af74909SZhong Yang 					}
2071*8af74909SZhong Yang 					if (!widthFirstCharacter)
2072*8af74909SZhong Yang 						widthFirstCharacter = widthChar;
2073*8af74909SZhong Yang 					if ((posIndexDocument + widthChar) > limitPos)
2074*8af74909SZhong Yang 						break;
2075*8af74909SZhong Yang 					const size_t lenFlat = pcf->Fold(folded, sizeof(folded), bytes, widthChar);
2076*8af74909SZhong Yang 					// memcmp may examine lenFlat bytes in both arguments so assert it doesn't read past end of searchThing
2077*8af74909SZhong Yang 					assert((indexSearch + lenFlat) <= searchThing.size());
2078*8af74909SZhong Yang 					// Does folded match the buffer
2079*8af74909SZhong Yang 					characterMatches = 0 == memcmp(folded, &searchThing[0] + indexSearch, lenFlat);
2080*8af74909SZhong Yang 					if (!characterMatches)
2081*8af74909SZhong Yang 						break;
2082*8af74909SZhong Yang 					posIndexDocument += widthChar;
2083*8af74909SZhong Yang 					indexSearch += lenFlat;
2084*8af74909SZhong Yang 					if (indexSearch >= lenSearch)
2085*8af74909SZhong Yang 						break;
2086*8af74909SZhong Yang 				}
2087*8af74909SZhong Yang 				if (characterMatches && (indexSearch == lenSearch)) {
2088*8af74909SZhong Yang 					if (MatchesWordOptions(word, wordStart, pos, posIndexDocument - pos)) {
2089*8af74909SZhong Yang 						*length = posIndexDocument - pos;
2090*8af74909SZhong Yang 						return pos;
2091*8af74909SZhong Yang 					}
2092*8af74909SZhong Yang 				}
2093*8af74909SZhong Yang 				if (forward) {
2094*8af74909SZhong Yang 					pos += widthFirstCharacter;
2095*8af74909SZhong Yang 				} else {
2096*8af74909SZhong Yang 					if (!NextCharacter(pos, increment))
2097*8af74909SZhong Yang 						break;
2098*8af74909SZhong Yang 				}
2099*8af74909SZhong Yang 			}
2100*8af74909SZhong Yang 		} else if (dbcsCodePage) {
2101*8af74909SZhong Yang 			constexpr size_t maxBytesCharacter = 2;
2102*8af74909SZhong Yang 			constexpr size_t maxFoldingExpansion = 4;
2103*8af74909SZhong Yang 			std::vector<char> searchThing((lengthFind+1) * maxBytesCharacter * maxFoldingExpansion + 1);
2104*8af74909SZhong Yang 			const size_t lenSearch = pcf->Fold(&searchThing[0], searchThing.size(), search, lengthFind);
2105*8af74909SZhong Yang 			while (forward ? (pos < endPos) : (pos >= endPos)) {
2106*8af74909SZhong Yang 				Sci::Position indexDocument = 0;
2107*8af74909SZhong Yang 				size_t indexSearch = 0;
2108*8af74909SZhong Yang 				bool characterMatches = true;
2109*8af74909SZhong Yang 				while (characterMatches &&
2110*8af74909SZhong Yang 					((pos + indexDocument) < limitPos) &&
2111*8af74909SZhong Yang 					(indexSearch < lenSearch)) {
2112*8af74909SZhong Yang 					char bytes[maxBytesCharacter + 1];
2113*8af74909SZhong Yang 					bytes[0] = cb.CharAt(pos + indexDocument);
2114*8af74909SZhong Yang 					const Sci::Position widthChar = IsDBCSLeadByteNoExcept(bytes[0]) ? 2 : 1;
2115*8af74909SZhong Yang 					if (widthChar == 2)
2116*8af74909SZhong Yang 						bytes[1] = cb.CharAt(pos + indexDocument + 1);
2117*8af74909SZhong Yang 					if ((pos + indexDocument + widthChar) > limitPos)
2118*8af74909SZhong Yang 						break;
2119*8af74909SZhong Yang 					char folded[maxBytesCharacter * maxFoldingExpansion + 1];
2120*8af74909SZhong Yang 					const size_t lenFlat = pcf->Fold(folded, sizeof(folded), bytes, widthChar);
2121*8af74909SZhong Yang 					// memcmp may examine lenFlat bytes in both arguments so assert it doesn't read past end of searchThing
2122*8af74909SZhong Yang 					assert((indexSearch + lenFlat) <= searchThing.size());
2123*8af74909SZhong Yang 					// Does folded match the buffer
2124*8af74909SZhong Yang 					characterMatches = 0 == memcmp(folded, &searchThing[0] + indexSearch, lenFlat);
2125*8af74909SZhong Yang 					indexDocument += widthChar;
2126*8af74909SZhong Yang 					indexSearch += lenFlat;
2127*8af74909SZhong Yang 				}
2128*8af74909SZhong Yang 				if (characterMatches && (indexSearch == lenSearch)) {
2129*8af74909SZhong Yang 					if (MatchesWordOptions(word, wordStart, pos, indexDocument)) {
2130*8af74909SZhong Yang 						*length = indexDocument;
2131*8af74909SZhong Yang 						return pos;
2132*8af74909SZhong Yang 					}
2133*8af74909SZhong Yang 				}
2134*8af74909SZhong Yang 				if (!NextCharacter(pos, increment))
2135*8af74909SZhong Yang 					break;
2136*8af74909SZhong Yang 			}
2137*8af74909SZhong Yang 		} else {
2138*8af74909SZhong Yang 			const Sci::Position endSearch = (startPos <= endPos) ? endPos - lengthFind + 1 : endPos;
2139*8af74909SZhong Yang 			std::vector<char> searchThing(lengthFind + 1);
2140*8af74909SZhong Yang 			pcf->Fold(&searchThing[0], searchThing.size(), search, lengthFind);
2141*8af74909SZhong Yang 			while (forward ? (pos < endSearch) : (pos >= endSearch)) {
2142*8af74909SZhong Yang 				bool found = (pos + lengthFind) <= limitPos;
2143*8af74909SZhong Yang 				for (int indexSearch = 0; (indexSearch < lengthFind) && found; indexSearch++) {
2144*8af74909SZhong Yang 					const char ch = CharAt(pos + indexSearch);
2145*8af74909SZhong Yang 					char folded[2];
2146*8af74909SZhong Yang 					pcf->Fold(folded, sizeof(folded), &ch, 1);
2147*8af74909SZhong Yang 					found = folded[0] == searchThing[indexSearch];
2148*8af74909SZhong Yang 				}
2149*8af74909SZhong Yang 				if (found && MatchesWordOptions(word, wordStart, pos, lengthFind)) {
2150*8af74909SZhong Yang 					return pos;
2151*8af74909SZhong Yang 				}
2152*8af74909SZhong Yang 				if (!NextCharacter(pos, increment))
2153*8af74909SZhong Yang 					break;
2154*8af74909SZhong Yang 			}
2155*8af74909SZhong Yang 		}
2156*8af74909SZhong Yang 	}
2157*8af74909SZhong Yang 	//Platform::DebugPrintf("Not found\n");
2158*8af74909SZhong Yang 	return -1;
2159*8af74909SZhong Yang }
2160*8af74909SZhong Yang 
SubstituteByPosition(const char * text,Sci::Position * length)2161*8af74909SZhong Yang const char *Document::SubstituteByPosition(const char *text, Sci::Position *length) {
2162*8af74909SZhong Yang 	if (regex)
2163*8af74909SZhong Yang 		return regex->SubstituteByPosition(this, text, length);
2164*8af74909SZhong Yang 	else
2165*8af74909SZhong Yang 		return nullptr;
2166*8af74909SZhong Yang }
2167*8af74909SZhong Yang 
LineCharacterIndex() const2168*8af74909SZhong Yang int Document::LineCharacterIndex() const noexcept {
2169*8af74909SZhong Yang 	return cb.LineCharacterIndex();
2170*8af74909SZhong Yang }
2171*8af74909SZhong Yang 
AllocateLineCharacterIndex(int lineCharacterIndex)2172*8af74909SZhong Yang void Document::AllocateLineCharacterIndex(int lineCharacterIndex) {
2173*8af74909SZhong Yang 	return cb.AllocateLineCharacterIndex(lineCharacterIndex);
2174*8af74909SZhong Yang }
2175*8af74909SZhong Yang 
ReleaseLineCharacterIndex(int lineCharacterIndex)2176*8af74909SZhong Yang void Document::ReleaseLineCharacterIndex(int lineCharacterIndex) {
2177*8af74909SZhong Yang 	return cb.ReleaseLineCharacterIndex(lineCharacterIndex);
2178*8af74909SZhong Yang }
2179*8af74909SZhong Yang 
LinesTotal() const2180*8af74909SZhong Yang Sci::Line Document::LinesTotal() const noexcept {
2181*8af74909SZhong Yang 	return cb.Lines();
2182*8af74909SZhong Yang }
2183*8af74909SZhong Yang 
SetDefaultCharClasses(bool includeWordClass)2184*8af74909SZhong Yang void Document::SetDefaultCharClasses(bool includeWordClass) {
2185*8af74909SZhong Yang     charClass.SetDefaultCharClasses(includeWordClass);
2186*8af74909SZhong Yang }
2187*8af74909SZhong Yang 
SetCharClasses(const unsigned char * chars,CharClassify::cc newCharClass)2188*8af74909SZhong Yang void Document::SetCharClasses(const unsigned char *chars, CharClassify::cc newCharClass) {
2189*8af74909SZhong Yang     charClass.SetCharClasses(chars, newCharClass);
2190*8af74909SZhong Yang }
2191*8af74909SZhong Yang 
GetCharsOfClass(CharClassify::cc characterClass,unsigned char * buffer) const2192*8af74909SZhong Yang int Document::GetCharsOfClass(CharClassify::cc characterClass, unsigned char *buffer) const {
2193*8af74909SZhong Yang     return charClass.GetCharsOfClass(characterClass, buffer);
2194*8af74909SZhong Yang }
2195*8af74909SZhong Yang 
SetCharacterCategoryOptimization(int countCharacters)2196*8af74909SZhong Yang void Document::SetCharacterCategoryOptimization(int countCharacters) {
2197*8af74909SZhong Yang 	charMap.Optimize(countCharacters);
2198*8af74909SZhong Yang }
2199*8af74909SZhong Yang 
CharacterCategoryOptimization() const2200*8af74909SZhong Yang int Document::CharacterCategoryOptimization() const noexcept {
2201*8af74909SZhong Yang 	return charMap.Size();
2202*8af74909SZhong Yang }
2203*8af74909SZhong Yang 
StartStyling(Sci_Position position)2204*8af74909SZhong Yang void SCI_METHOD Document::StartStyling(Sci_Position position) {
2205*8af74909SZhong Yang 	endStyled = position;
2206*8af74909SZhong Yang }
2207*8af74909SZhong Yang 
SetStyleFor(Sci_Position length,char style)2208*8af74909SZhong Yang bool SCI_METHOD Document::SetStyleFor(Sci_Position length, char style) {
2209*8af74909SZhong Yang 	if (enteredStyling != 0) {
2210*8af74909SZhong Yang 		return false;
2211*8af74909SZhong Yang 	} else {
2212*8af74909SZhong Yang 		enteredStyling++;
2213*8af74909SZhong Yang 		const Sci::Position prevEndStyled = endStyled;
2214*8af74909SZhong Yang 		if (cb.SetStyleFor(endStyled, length, style)) {
2215*8af74909SZhong Yang 			const DocModification mh(SC_MOD_CHANGESTYLE | SC_PERFORMED_USER,
2216*8af74909SZhong Yang 			                   prevEndStyled, length);
2217*8af74909SZhong Yang 			NotifyModified(mh);
2218*8af74909SZhong Yang 		}
2219*8af74909SZhong Yang 		endStyled += length;
2220*8af74909SZhong Yang 		enteredStyling--;
2221*8af74909SZhong Yang 		return true;
2222*8af74909SZhong Yang 	}
2223*8af74909SZhong Yang }
2224*8af74909SZhong Yang 
SetStyles(Sci_Position length,const char * styles)2225*8af74909SZhong Yang bool SCI_METHOD Document::SetStyles(Sci_Position length, const char *styles) {
2226*8af74909SZhong Yang 	if (enteredStyling != 0) {
2227*8af74909SZhong Yang 		return false;
2228*8af74909SZhong Yang 	} else {
2229*8af74909SZhong Yang 		enteredStyling++;
2230*8af74909SZhong Yang 		bool didChange = false;
2231*8af74909SZhong Yang 		Sci::Position startMod = 0;
2232*8af74909SZhong Yang 		Sci::Position endMod = 0;
2233*8af74909SZhong Yang 		for (int iPos = 0; iPos < length; iPos++, endStyled++) {
2234*8af74909SZhong Yang 			PLATFORM_ASSERT(endStyled < Length());
2235*8af74909SZhong Yang 			if (cb.SetStyleAt(endStyled, styles[iPos])) {
2236*8af74909SZhong Yang 				if (!didChange) {
2237*8af74909SZhong Yang 					startMod = endStyled;
2238*8af74909SZhong Yang 				}
2239*8af74909SZhong Yang 				didChange = true;
2240*8af74909SZhong Yang 				endMod = endStyled;
2241*8af74909SZhong Yang 			}
2242*8af74909SZhong Yang 		}
2243*8af74909SZhong Yang 		if (didChange) {
2244*8af74909SZhong Yang 			const DocModification mh(SC_MOD_CHANGESTYLE | SC_PERFORMED_USER,
2245*8af74909SZhong Yang 			                   startMod, endMod - startMod + 1);
2246*8af74909SZhong Yang 			NotifyModified(mh);
2247*8af74909SZhong Yang 		}
2248*8af74909SZhong Yang 		enteredStyling--;
2249*8af74909SZhong Yang 		return true;
2250*8af74909SZhong Yang 	}
2251*8af74909SZhong Yang }
2252*8af74909SZhong Yang 
EnsureStyledTo(Sci::Position pos)2253*8af74909SZhong Yang void Document::EnsureStyledTo(Sci::Position pos) {
2254*8af74909SZhong Yang 	if ((enteredStyling == 0) && (pos > GetEndStyled())) {
2255*8af74909SZhong Yang 		IncrementStyleClock();
2256*8af74909SZhong Yang 		if (pli && !pli->UseContainerLexing()) {
2257*8af74909SZhong Yang 			const Sci::Line lineEndStyled = SciLineFromPosition(GetEndStyled());
2258*8af74909SZhong Yang 			const Sci::Position endStyledTo = LineStart(lineEndStyled);
2259*8af74909SZhong Yang 			pli->Colourise(endStyledTo, pos);
2260*8af74909SZhong Yang 		} else {
2261*8af74909SZhong Yang 			// Ask the watchers to style, and stop as soon as one responds.
2262*8af74909SZhong Yang 			for (std::vector<WatcherWithUserData>::iterator it = watchers.begin();
2263*8af74909SZhong Yang 				(pos > GetEndStyled()) && (it != watchers.end()); ++it) {
2264*8af74909SZhong Yang 				it->watcher->NotifyStyleNeeded(this, it->userData, pos);
2265*8af74909SZhong Yang 			}
2266*8af74909SZhong Yang 		}
2267*8af74909SZhong Yang 	}
2268*8af74909SZhong Yang }
2269*8af74909SZhong Yang 
StyleToAdjustingLineDuration(Sci::Position pos)2270*8af74909SZhong Yang void Document::StyleToAdjustingLineDuration(Sci::Position pos) {
2271*8af74909SZhong Yang 	const Sci::Line lineFirst = SciLineFromPosition(GetEndStyled());
2272*8af74909SZhong Yang 	ElapsedPeriod epStyling;
2273*8af74909SZhong Yang 	EnsureStyledTo(pos);
2274*8af74909SZhong Yang 	const Sci::Line lineLast = SciLineFromPosition(GetEndStyled());
2275*8af74909SZhong Yang 	durationStyleOneLine.AddSample(lineLast - lineFirst, epStyling.Duration());
2276*8af74909SZhong Yang }
2277*8af74909SZhong Yang 
LexerChanged()2278*8af74909SZhong Yang void Document::LexerChanged() {
2279*8af74909SZhong Yang 	// Tell the watchers the lexer has changed.
2280*8af74909SZhong Yang 	for (const WatcherWithUserData &watcher : watchers) {
2281*8af74909SZhong Yang 		watcher.watcher->NotifyLexerChanged(this, watcher.userData);
2282*8af74909SZhong Yang 	}
2283*8af74909SZhong Yang }
2284*8af74909SZhong Yang 
GetLexInterface() const2285*8af74909SZhong Yang LexInterface *Document::GetLexInterface() const noexcept {
2286*8af74909SZhong Yang 	return pli.get();
2287*8af74909SZhong Yang }
2288*8af74909SZhong Yang 
SetLexInterface(std::unique_ptr<LexInterface> pLexInterface)2289*8af74909SZhong Yang void Document::SetLexInterface(std::unique_ptr<LexInterface> pLexInterface) noexcept {
2290*8af74909SZhong Yang 	pli = std::move(pLexInterface);
2291*8af74909SZhong Yang }
2292*8af74909SZhong Yang 
SetLineState(Sci_Position line,int state)2293*8af74909SZhong Yang int SCI_METHOD Document::SetLineState(Sci_Position line, int state) {
2294*8af74909SZhong Yang 	const int statePrevious = States()->SetLineState(line, state);
2295*8af74909SZhong Yang 	if (state != statePrevious) {
2296*8af74909SZhong Yang 		const DocModification mh(SC_MOD_CHANGELINESTATE, LineStart(line), 0, 0, nullptr,
2297*8af74909SZhong Yang 			static_cast<Sci::Line>(line));
2298*8af74909SZhong Yang 		NotifyModified(mh);
2299*8af74909SZhong Yang 	}
2300*8af74909SZhong Yang 	return statePrevious;
2301*8af74909SZhong Yang }
2302*8af74909SZhong Yang 
GetLineState(Sci_Position line) const2303*8af74909SZhong Yang int SCI_METHOD Document::GetLineState(Sci_Position line) const {
2304*8af74909SZhong Yang 	return States()->GetLineState(line);
2305*8af74909SZhong Yang }
2306*8af74909SZhong Yang 
GetMaxLineState() const2307*8af74909SZhong Yang Sci::Line Document::GetMaxLineState() const noexcept {
2308*8af74909SZhong Yang 	return States()->GetMaxLineState();
2309*8af74909SZhong Yang }
2310*8af74909SZhong Yang 
ChangeLexerState(Sci_Position start,Sci_Position end)2311*8af74909SZhong Yang void SCI_METHOD Document::ChangeLexerState(Sci_Position start, Sci_Position end) {
2312*8af74909SZhong Yang 	const DocModification mh(SC_MOD_LEXERSTATE, start,
2313*8af74909SZhong Yang 		end-start, 0, 0, 0);
2314*8af74909SZhong Yang 	NotifyModified(mh);
2315*8af74909SZhong Yang }
2316*8af74909SZhong Yang 
MarginStyledText(Sci::Line line) const2317*8af74909SZhong Yang StyledText Document::MarginStyledText(Sci::Line line) const noexcept {
2318*8af74909SZhong Yang 	const LineAnnotation *pla = Margins();
2319*8af74909SZhong Yang 	return StyledText(pla->Length(line), pla->Text(line),
2320*8af74909SZhong Yang 		pla->MultipleStyles(line), pla->Style(line), pla->Styles(line));
2321*8af74909SZhong Yang }
2322*8af74909SZhong Yang 
MarginSetText(Sci::Line line,const char * text)2323*8af74909SZhong Yang void Document::MarginSetText(Sci::Line line, const char *text) {
2324*8af74909SZhong Yang 	Margins()->SetText(line, text);
2325*8af74909SZhong Yang 	const DocModification mh(SC_MOD_CHANGEMARGIN, LineStart(line),
2326*8af74909SZhong Yang 		0, 0, 0, line);
2327*8af74909SZhong Yang 	NotifyModified(mh);
2328*8af74909SZhong Yang }
2329*8af74909SZhong Yang 
MarginSetStyle(Sci::Line line,int style)2330*8af74909SZhong Yang void Document::MarginSetStyle(Sci::Line line, int style) {
2331*8af74909SZhong Yang 	Margins()->SetStyle(line, style);
2332*8af74909SZhong Yang 	NotifyModified(DocModification(SC_MOD_CHANGEMARGIN, LineStart(line),
2333*8af74909SZhong Yang 		0, 0, 0, line));
2334*8af74909SZhong Yang }
2335*8af74909SZhong Yang 
MarginSetStyles(Sci::Line line,const unsigned char * styles)2336*8af74909SZhong Yang void Document::MarginSetStyles(Sci::Line line, const unsigned char *styles) {
2337*8af74909SZhong Yang 	Margins()->SetStyles(line, styles);
2338*8af74909SZhong Yang 	NotifyModified(DocModification(SC_MOD_CHANGEMARGIN, LineStart(line),
2339*8af74909SZhong Yang 		0, 0, 0, line));
2340*8af74909SZhong Yang }
2341*8af74909SZhong Yang 
MarginClearAll()2342*8af74909SZhong Yang void Document::MarginClearAll() {
2343*8af74909SZhong Yang 	const Sci::Line maxEditorLine = LinesTotal();
2344*8af74909SZhong Yang 	for (Sci::Line l=0; l<maxEditorLine; l++)
2345*8af74909SZhong Yang 		MarginSetText(l, nullptr);
2346*8af74909SZhong Yang 	// Free remaining data
2347*8af74909SZhong Yang 	Margins()->ClearAll();
2348*8af74909SZhong Yang }
2349*8af74909SZhong Yang 
AnnotationStyledText(Sci::Line line) const2350*8af74909SZhong Yang StyledText Document::AnnotationStyledText(Sci::Line line) const noexcept {
2351*8af74909SZhong Yang 	const LineAnnotation *pla = Annotations();
2352*8af74909SZhong Yang 	return StyledText(pla->Length(line), pla->Text(line),
2353*8af74909SZhong Yang 		pla->MultipleStyles(line), pla->Style(line), pla->Styles(line));
2354*8af74909SZhong Yang }
2355*8af74909SZhong Yang 
AnnotationSetText(Sci::Line line,const char * text)2356*8af74909SZhong Yang void Document::AnnotationSetText(Sci::Line line, const char *text) {
2357*8af74909SZhong Yang 	if (line >= 0 && line < LinesTotal()) {
2358*8af74909SZhong Yang 		const Sci::Line linesBefore = AnnotationLines(line);
2359*8af74909SZhong Yang 		Annotations()->SetText(line, text);
2360*8af74909SZhong Yang 		const int linesAfter = AnnotationLines(line);
2361*8af74909SZhong Yang 		DocModification mh(SC_MOD_CHANGEANNOTATION, LineStart(line),
2362*8af74909SZhong Yang 			0, 0, 0, line);
2363*8af74909SZhong Yang 		mh.annotationLinesAdded = linesAfter - linesBefore;
2364*8af74909SZhong Yang 		NotifyModified(mh);
2365*8af74909SZhong Yang 	}
2366*8af74909SZhong Yang }
2367*8af74909SZhong Yang 
AnnotationSetStyle(Sci::Line line,int style)2368*8af74909SZhong Yang void Document::AnnotationSetStyle(Sci::Line line, int style) {
2369*8af74909SZhong Yang 	if (line >= 0 && line < LinesTotal()) {
2370*8af74909SZhong Yang 		Annotations()->SetStyle(line, style);
2371*8af74909SZhong Yang 		const DocModification mh(SC_MOD_CHANGEANNOTATION, LineStart(line),
2372*8af74909SZhong Yang 			0, 0, 0, line);
2373*8af74909SZhong Yang 		NotifyModified(mh);
2374*8af74909SZhong Yang 	}
2375*8af74909SZhong Yang }
2376*8af74909SZhong Yang 
AnnotationSetStyles(Sci::Line line,const unsigned char * styles)2377*8af74909SZhong Yang void Document::AnnotationSetStyles(Sci::Line line, const unsigned char *styles) {
2378*8af74909SZhong Yang 	if (line >= 0 && line < LinesTotal()) {
2379*8af74909SZhong Yang 		Annotations()->SetStyles(line, styles);
2380*8af74909SZhong Yang 	}
2381*8af74909SZhong Yang }
2382*8af74909SZhong Yang 
AnnotationLines(Sci::Line line) const2383*8af74909SZhong Yang int Document::AnnotationLines(Sci::Line line) const noexcept {
2384*8af74909SZhong Yang 	return Annotations()->Lines(line);
2385*8af74909SZhong Yang }
2386*8af74909SZhong Yang 
AnnotationClearAll()2387*8af74909SZhong Yang void Document::AnnotationClearAll() {
2388*8af74909SZhong Yang 	const Sci::Line maxEditorLine = LinesTotal();
2389*8af74909SZhong Yang 	for (Sci::Line l=0; l<maxEditorLine; l++)
2390*8af74909SZhong Yang 		AnnotationSetText(l, nullptr);
2391*8af74909SZhong Yang 	// Free remaining data
2392*8af74909SZhong Yang 	Annotations()->ClearAll();
2393*8af74909SZhong Yang }
2394*8af74909SZhong Yang 
EOLAnnotationStyledText(Sci::Line line) const2395*8af74909SZhong Yang StyledText Document::EOLAnnotationStyledText(Sci::Line line) const noexcept {
2396*8af74909SZhong Yang 	const LineAnnotation *pla = EOLAnnotations();
2397*8af74909SZhong Yang 	return StyledText(pla->Length(line), pla->Text(line),
2398*8af74909SZhong Yang 		pla->MultipleStyles(line), pla->Style(line), pla->Styles(line));
2399*8af74909SZhong Yang }
2400*8af74909SZhong Yang 
EOLAnnotationSetText(Sci::Line line,const char * text)2401*8af74909SZhong Yang void Document::EOLAnnotationSetText(Sci::Line line, const char *text) {
2402*8af74909SZhong Yang 	if (line >= 0 && line < LinesTotal()) {
2403*8af74909SZhong Yang 		EOLAnnotations()->SetText(line, text);
2404*8af74909SZhong Yang 		const DocModification mh(SC_MOD_CHANGEEOLANNOTATION, LineStart(line),
2405*8af74909SZhong Yang 			0, 0, 0, line);
2406*8af74909SZhong Yang 		NotifyModified(mh);
2407*8af74909SZhong Yang 	}
2408*8af74909SZhong Yang }
2409*8af74909SZhong Yang 
EOLAnnotationSetStyle(Sci::Line line,int style)2410*8af74909SZhong Yang void Document::EOLAnnotationSetStyle(Sci::Line line, int style) {
2411*8af74909SZhong Yang 	if (line >= 0 && line < LinesTotal()) {
2412*8af74909SZhong Yang 		EOLAnnotations()->SetStyle(line, style);
2413*8af74909SZhong Yang 		const DocModification mh(SC_MOD_CHANGEEOLANNOTATION, LineStart(line),
2414*8af74909SZhong Yang 			0, 0, 0, line);
2415*8af74909SZhong Yang 		NotifyModified(mh);
2416*8af74909SZhong Yang 	}
2417*8af74909SZhong Yang }
2418*8af74909SZhong Yang 
EOLAnnotationClearAll()2419*8af74909SZhong Yang void Document::EOLAnnotationClearAll() {
2420*8af74909SZhong Yang 	const Sci::Line maxEditorLine = LinesTotal();
2421*8af74909SZhong Yang 	for (Sci::Line l=0; l<maxEditorLine; l++)
2422*8af74909SZhong Yang 		EOLAnnotationSetText(l, nullptr);
2423*8af74909SZhong Yang 	// Free remaining data
2424*8af74909SZhong Yang 	EOLAnnotations()->ClearAll();
2425*8af74909SZhong Yang }
2426*8af74909SZhong Yang 
IncrementStyleClock()2427*8af74909SZhong Yang void Document::IncrementStyleClock() noexcept {
2428*8af74909SZhong Yang 	styleClock = (styleClock + 1) % 0x100000;
2429*8af74909SZhong Yang }
2430*8af74909SZhong Yang 
DecorationSetCurrentIndicator(int indicator)2431*8af74909SZhong Yang void SCI_METHOD Document::DecorationSetCurrentIndicator(int indicator) {
2432*8af74909SZhong Yang 	decorations->SetCurrentIndicator(indicator);
2433*8af74909SZhong Yang }
2434*8af74909SZhong Yang 
DecorationFillRange(Sci_Position position,int value,Sci_Position fillLength)2435*8af74909SZhong Yang void SCI_METHOD Document::DecorationFillRange(Sci_Position position, int value, Sci_Position fillLength) {
2436*8af74909SZhong Yang 	const FillResult<Sci::Position> fr = decorations->FillRange(
2437*8af74909SZhong Yang 		position, value, fillLength);
2438*8af74909SZhong Yang 	if (fr.changed) {
2439*8af74909SZhong Yang 		const DocModification mh(SC_MOD_CHANGEINDICATOR | SC_PERFORMED_USER,
2440*8af74909SZhong Yang 							fr.position, fr.fillLength);
2441*8af74909SZhong Yang 		NotifyModified(mh);
2442*8af74909SZhong Yang 	}
2443*8af74909SZhong Yang }
2444*8af74909SZhong Yang 
AddWatcher(DocWatcher * watcher,void * userData)2445*8af74909SZhong Yang bool Document::AddWatcher(DocWatcher *watcher, void *userData) {
2446*8af74909SZhong Yang 	const WatcherWithUserData wwud(watcher, userData);
2447*8af74909SZhong Yang 	std::vector<WatcherWithUserData>::iterator it =
2448*8af74909SZhong Yang 		std::find(watchers.begin(), watchers.end(), wwud);
2449*8af74909SZhong Yang 	if (it != watchers.end())
2450*8af74909SZhong Yang 		return false;
2451*8af74909SZhong Yang 	watchers.push_back(wwud);
2452*8af74909SZhong Yang 	return true;
2453*8af74909SZhong Yang }
2454*8af74909SZhong Yang 
RemoveWatcher(DocWatcher * watcher,void * userData)2455*8af74909SZhong Yang bool Document::RemoveWatcher(DocWatcher *watcher, void *userData) {
2456*8af74909SZhong Yang 	std::vector<WatcherWithUserData>::iterator it =
2457*8af74909SZhong Yang 		std::find(watchers.begin(), watchers.end(), WatcherWithUserData(watcher, userData));
2458*8af74909SZhong Yang 	if (it != watchers.end()) {
2459*8af74909SZhong Yang 		watchers.erase(it);
2460*8af74909SZhong Yang 		return true;
2461*8af74909SZhong Yang 	}
2462*8af74909SZhong Yang 	return false;
2463*8af74909SZhong Yang }
2464*8af74909SZhong Yang 
NotifyModifyAttempt()2465*8af74909SZhong Yang void Document::NotifyModifyAttempt() {
2466*8af74909SZhong Yang 	for (const WatcherWithUserData &watcher : watchers) {
2467*8af74909SZhong Yang 		watcher.watcher->NotifyModifyAttempt(this, watcher.userData);
2468*8af74909SZhong Yang 	}
2469*8af74909SZhong Yang }
2470*8af74909SZhong Yang 
NotifySavePoint(bool atSavePoint)2471*8af74909SZhong Yang void Document::NotifySavePoint(bool atSavePoint) {
2472*8af74909SZhong Yang 	for (const WatcherWithUserData &watcher : watchers) {
2473*8af74909SZhong Yang 		watcher.watcher->NotifySavePoint(this, watcher.userData, atSavePoint);
2474*8af74909SZhong Yang 	}
2475*8af74909SZhong Yang }
2476*8af74909SZhong Yang 
NotifyModified(DocModification mh)2477*8af74909SZhong Yang void Document::NotifyModified(DocModification mh) {
2478*8af74909SZhong Yang 	if (mh.modificationType & SC_MOD_INSERTTEXT) {
2479*8af74909SZhong Yang 		decorations->InsertSpace(mh.position, mh.length);
2480*8af74909SZhong Yang 	} else if (mh.modificationType & SC_MOD_DELETETEXT) {
2481*8af74909SZhong Yang 		decorations->DeleteRange(mh.position, mh.length);
2482*8af74909SZhong Yang 	}
2483*8af74909SZhong Yang 	for (const WatcherWithUserData &watcher : watchers) {
2484*8af74909SZhong Yang 		watcher.watcher->NotifyModified(this, mh, watcher.userData);
2485*8af74909SZhong Yang 	}
2486*8af74909SZhong Yang }
2487*8af74909SZhong Yang 
2488*8af74909SZhong Yang // Used for word part navigation.
IsASCIIPunctuationCharacter(unsigned int ch)2489*8af74909SZhong Yang static bool IsASCIIPunctuationCharacter(unsigned int ch) noexcept {
2490*8af74909SZhong Yang 	switch (ch) {
2491*8af74909SZhong Yang 	case '!':
2492*8af74909SZhong Yang 	case '"':
2493*8af74909SZhong Yang 	case '#':
2494*8af74909SZhong Yang 	case '$':
2495*8af74909SZhong Yang 	case '%':
2496*8af74909SZhong Yang 	case '&':
2497*8af74909SZhong Yang 	case '\'':
2498*8af74909SZhong Yang 	case '(':
2499*8af74909SZhong Yang 	case ')':
2500*8af74909SZhong Yang 	case '*':
2501*8af74909SZhong Yang 	case '+':
2502*8af74909SZhong Yang 	case ',':
2503*8af74909SZhong Yang 	case '-':
2504*8af74909SZhong Yang 	case '.':
2505*8af74909SZhong Yang 	case '/':
2506*8af74909SZhong Yang 	case ':':
2507*8af74909SZhong Yang 	case ';':
2508*8af74909SZhong Yang 	case '<':
2509*8af74909SZhong Yang 	case '=':
2510*8af74909SZhong Yang 	case '>':
2511*8af74909SZhong Yang 	case '?':
2512*8af74909SZhong Yang 	case '@':
2513*8af74909SZhong Yang 	case '[':
2514*8af74909SZhong Yang 	case '\\':
2515*8af74909SZhong Yang 	case ']':
2516*8af74909SZhong Yang 	case '^':
2517*8af74909SZhong Yang 	case '_':
2518*8af74909SZhong Yang 	case '`':
2519*8af74909SZhong Yang 	case '{':
2520*8af74909SZhong Yang 	case '|':
2521*8af74909SZhong Yang 	case '}':
2522*8af74909SZhong Yang 	case '~':
2523*8af74909SZhong Yang 		return true;
2524*8af74909SZhong Yang 	default:
2525*8af74909SZhong Yang 		return false;
2526*8af74909SZhong Yang 	}
2527*8af74909SZhong Yang }
2528*8af74909SZhong Yang 
IsWordPartSeparator(unsigned int ch) const2529*8af74909SZhong Yang bool Document::IsWordPartSeparator(unsigned int ch) const {
2530*8af74909SZhong Yang 	return (WordCharacterClass(ch) == CharClassify::ccWord) && IsASCIIPunctuationCharacter(ch);
2531*8af74909SZhong Yang }
2532*8af74909SZhong Yang 
WordPartLeft(Sci::Position pos) const2533*8af74909SZhong Yang Sci::Position Document::WordPartLeft(Sci::Position pos) const {
2534*8af74909SZhong Yang 	if (pos > 0) {
2535*8af74909SZhong Yang 		pos -= CharacterBefore(pos).widthBytes;
2536*8af74909SZhong Yang 		CharacterExtracted ceStart = CharacterAfter(pos);
2537*8af74909SZhong Yang 		if (IsWordPartSeparator(ceStart.character)) {
2538*8af74909SZhong Yang 			while (pos > 0 && IsWordPartSeparator(CharacterAfter(pos).character)) {
2539*8af74909SZhong Yang 				pos -= CharacterBefore(pos).widthBytes;
2540*8af74909SZhong Yang 			}
2541*8af74909SZhong Yang 		}
2542*8af74909SZhong Yang 		if (pos > 0) {
2543*8af74909SZhong Yang 			ceStart = CharacterAfter(pos);
2544*8af74909SZhong Yang 			pos -= CharacterBefore(pos).widthBytes;
2545*8af74909SZhong Yang 			if (IsLowerCase(ceStart.character)) {
2546*8af74909SZhong Yang 				while (pos > 0 && IsLowerCase(CharacterAfter(pos).character))
2547*8af74909SZhong Yang 					pos -= CharacterBefore(pos).widthBytes;
2548*8af74909SZhong Yang 				if (!IsUpperCase(CharacterAfter(pos).character) && !IsLowerCase(CharacterAfter(pos).character))
2549*8af74909SZhong Yang 					pos += CharacterAfter(pos).widthBytes;
2550*8af74909SZhong Yang 			} else if (IsUpperCase(ceStart.character)) {
2551*8af74909SZhong Yang 				while (pos > 0 && IsUpperCase(CharacterAfter(pos).character))
2552*8af74909SZhong Yang 					pos -= CharacterBefore(pos).widthBytes;
2553*8af74909SZhong Yang 				if (!IsUpperCase(CharacterAfter(pos).character))
2554*8af74909SZhong Yang 					pos += CharacterAfter(pos).widthBytes;
2555*8af74909SZhong Yang 			} else if (IsADigit(ceStart.character)) {
2556*8af74909SZhong Yang 				while (pos > 0 && IsADigit(CharacterAfter(pos).character))
2557*8af74909SZhong Yang 					pos -= CharacterBefore(pos).widthBytes;
2558*8af74909SZhong Yang 				if (!IsADigit(CharacterAfter(pos).character))
2559*8af74909SZhong Yang 					pos += CharacterAfter(pos).widthBytes;
2560*8af74909SZhong Yang 			} else if (IsASCIIPunctuationCharacter(ceStart.character)) {
2561*8af74909SZhong Yang 				while (pos > 0 && IsASCIIPunctuationCharacter(CharacterAfter(pos).character))
2562*8af74909SZhong Yang 					pos -= CharacterBefore(pos).widthBytes;
2563*8af74909SZhong Yang 				if (!IsASCIIPunctuationCharacter(CharacterAfter(pos).character))
2564*8af74909SZhong Yang 					pos += CharacterAfter(pos).widthBytes;
2565*8af74909SZhong Yang 			} else if (isspacechar(ceStart.character)) {
2566*8af74909SZhong Yang 				while (pos > 0 && isspacechar(CharacterAfter(pos).character))
2567*8af74909SZhong Yang 					pos -= CharacterBefore(pos).widthBytes;
2568*8af74909SZhong Yang 				if (!isspacechar(CharacterAfter(pos).character))
2569*8af74909SZhong Yang 					pos += CharacterAfter(pos).widthBytes;
2570*8af74909SZhong Yang 			} else if (!IsASCII(ceStart.character)) {
2571*8af74909SZhong Yang 				while (pos > 0 && !IsASCII(CharacterAfter(pos).character))
2572*8af74909SZhong Yang 					pos -= CharacterBefore(pos).widthBytes;
2573*8af74909SZhong Yang 				if (IsASCII(CharacterAfter(pos).character))
2574*8af74909SZhong Yang 					pos += CharacterAfter(pos).widthBytes;
2575*8af74909SZhong Yang 			} else {
2576*8af74909SZhong Yang 				pos += CharacterAfter(pos).widthBytes;
2577*8af74909SZhong Yang 			}
2578*8af74909SZhong Yang 		}
2579*8af74909SZhong Yang 	}
2580*8af74909SZhong Yang 	return pos;
2581*8af74909SZhong Yang }
2582*8af74909SZhong Yang 
WordPartRight(Sci::Position pos) const2583*8af74909SZhong Yang Sci::Position Document::WordPartRight(Sci::Position pos) const {
2584*8af74909SZhong Yang 	CharacterExtracted ceStart = CharacterAfter(pos);
2585*8af74909SZhong Yang 	const Sci::Position length = LengthNoExcept();
2586*8af74909SZhong Yang 	if (IsWordPartSeparator(ceStart.character)) {
2587*8af74909SZhong Yang 		while (pos < length && IsWordPartSeparator(CharacterAfter(pos).character))
2588*8af74909SZhong Yang 			pos += CharacterAfter(pos).widthBytes;
2589*8af74909SZhong Yang 		ceStart = CharacterAfter(pos);
2590*8af74909SZhong Yang 	}
2591*8af74909SZhong Yang 	if (!IsASCII(ceStart.character)) {
2592*8af74909SZhong Yang 		while (pos < length && !IsASCII(CharacterAfter(pos).character))
2593*8af74909SZhong Yang 			pos += CharacterAfter(pos).widthBytes;
2594*8af74909SZhong Yang 	} else if (IsLowerCase(ceStart.character)) {
2595*8af74909SZhong Yang 		while (pos < length && IsLowerCase(CharacterAfter(pos).character))
2596*8af74909SZhong Yang 			pos += CharacterAfter(pos).widthBytes;
2597*8af74909SZhong Yang 	} else if (IsUpperCase(ceStart.character)) {
2598*8af74909SZhong Yang 		if (IsLowerCase(CharacterAfter(pos + ceStart.widthBytes).character)) {
2599*8af74909SZhong Yang 			pos += CharacterAfter(pos).widthBytes;
2600*8af74909SZhong Yang 			while (pos < length && IsLowerCase(CharacterAfter(pos).character))
2601*8af74909SZhong Yang 				pos += CharacterAfter(pos).widthBytes;
2602*8af74909SZhong Yang 		} else {
2603*8af74909SZhong Yang 			while (pos < length && IsUpperCase(CharacterAfter(pos).character))
2604*8af74909SZhong Yang 				pos += CharacterAfter(pos).widthBytes;
2605*8af74909SZhong Yang 		}
2606*8af74909SZhong Yang 		if (IsLowerCase(CharacterAfter(pos).character) && IsUpperCase(CharacterBefore(pos).character))
2607*8af74909SZhong Yang 			pos -= CharacterBefore(pos).widthBytes;
2608*8af74909SZhong Yang 	} else if (IsADigit(ceStart.character)) {
2609*8af74909SZhong Yang 		while (pos < length && IsADigit(CharacterAfter(pos).character))
2610*8af74909SZhong Yang 			pos += CharacterAfter(pos).widthBytes;
2611*8af74909SZhong Yang 	} else if (IsASCIIPunctuationCharacter(ceStart.character)) {
2612*8af74909SZhong Yang 		while (pos < length && IsASCIIPunctuationCharacter(CharacterAfter(pos).character))
2613*8af74909SZhong Yang 			pos += CharacterAfter(pos).widthBytes;
2614*8af74909SZhong Yang 	} else if (isspacechar(ceStart.character)) {
2615*8af74909SZhong Yang 		while (pos < length && isspacechar(CharacterAfter(pos).character))
2616*8af74909SZhong Yang 			pos += CharacterAfter(pos).widthBytes;
2617*8af74909SZhong Yang 	} else {
2618*8af74909SZhong Yang 		pos += CharacterAfter(pos).widthBytes;
2619*8af74909SZhong Yang 	}
2620*8af74909SZhong Yang 	return pos;
2621*8af74909SZhong Yang }
2622*8af74909SZhong Yang 
IsLineEndChar(char c)2623*8af74909SZhong Yang static constexpr bool IsLineEndChar(char c) noexcept {
2624*8af74909SZhong Yang 	return (c == '\n' || c == '\r');
2625*8af74909SZhong Yang }
2626*8af74909SZhong Yang 
ExtendStyleRange(Sci::Position pos,int delta,bool singleLine)2627*8af74909SZhong Yang Sci::Position Document::ExtendStyleRange(Sci::Position pos, int delta, bool singleLine) noexcept {
2628*8af74909SZhong Yang 	const int sStart = cb.StyleAt(pos);
2629*8af74909SZhong Yang 	if (delta < 0) {
2630*8af74909SZhong Yang 		while (pos > 0 && (cb.StyleAt(pos) == sStart) && (!singleLine || !IsLineEndChar(cb.CharAt(pos))))
2631*8af74909SZhong Yang 			pos--;
2632*8af74909SZhong Yang 		pos++;
2633*8af74909SZhong Yang 	} else {
2634*8af74909SZhong Yang 		while (pos < (LengthNoExcept()) && (cb.StyleAt(pos) == sStart) && (!singleLine || !IsLineEndChar(cb.CharAt(pos))))
2635*8af74909SZhong Yang 			pos++;
2636*8af74909SZhong Yang 	}
2637*8af74909SZhong Yang 	return pos;
2638*8af74909SZhong Yang }
2639*8af74909SZhong Yang 
BraceOpposite(char ch)2640*8af74909SZhong Yang static char BraceOpposite(char ch) noexcept {
2641*8af74909SZhong Yang 	switch (ch) {
2642*8af74909SZhong Yang 	case '(':
2643*8af74909SZhong Yang 		return ')';
2644*8af74909SZhong Yang 	case ')':
2645*8af74909SZhong Yang 		return '(';
2646*8af74909SZhong Yang 	case '[':
2647*8af74909SZhong Yang 		return ']';
2648*8af74909SZhong Yang 	case ']':
2649*8af74909SZhong Yang 		return '[';
2650*8af74909SZhong Yang 	case '{':
2651*8af74909SZhong Yang 		return '}';
2652*8af74909SZhong Yang 	case '}':
2653*8af74909SZhong Yang 		return '{';
2654*8af74909SZhong Yang 	case '<':
2655*8af74909SZhong Yang 		return '>';
2656*8af74909SZhong Yang 	case '>':
2657*8af74909SZhong Yang 		return '<';
2658*8af74909SZhong Yang 	default:
2659*8af74909SZhong Yang 		return '\0';
2660*8af74909SZhong Yang 	}
2661*8af74909SZhong Yang }
2662*8af74909SZhong Yang 
2663*8af74909SZhong Yang // TODO: should be able to extend styled region to find matching brace
BraceMatch(Sci::Position position,Sci::Position,Sci::Position startPos,bool useStartPos)2664*8af74909SZhong Yang Sci::Position Document::BraceMatch(Sci::Position position, Sci::Position /*maxReStyle*/, Sci::Position startPos, bool useStartPos) noexcept {
2665*8af74909SZhong Yang 	const char chBrace = CharAt(position);
2666*8af74909SZhong Yang 	const char chSeek = BraceOpposite(chBrace);
2667*8af74909SZhong Yang 	if (chSeek == '\0')
2668*8af74909SZhong Yang 		return - 1;
2669*8af74909SZhong Yang 	const int styBrace = StyleIndexAt(position);
2670*8af74909SZhong Yang 	int direction = -1;
2671*8af74909SZhong Yang 	if (chBrace == '(' || chBrace == '[' || chBrace == '{' || chBrace == '<')
2672*8af74909SZhong Yang 		direction = 1;
2673*8af74909SZhong Yang 	int depth = 1;
2674*8af74909SZhong Yang 	position = useStartPos ? startPos : NextPosition(position, direction);
2675*8af74909SZhong Yang 	while ((position >= 0) && (position < LengthNoExcept())) {
2676*8af74909SZhong Yang 		const char chAtPos = CharAt(position);
2677*8af74909SZhong Yang 		const int styAtPos = StyleIndexAt(position);
2678*8af74909SZhong Yang 		if ((position > GetEndStyled()) || (styAtPos == styBrace)) {
2679*8af74909SZhong Yang 			if (chAtPos == chBrace)
2680*8af74909SZhong Yang 				depth++;
2681*8af74909SZhong Yang 			if (chAtPos == chSeek)
2682*8af74909SZhong Yang 				depth--;
2683*8af74909SZhong Yang 			if (depth == 0)
2684*8af74909SZhong Yang 				return position;
2685*8af74909SZhong Yang 		}
2686*8af74909SZhong Yang 		const Sci::Position positionBeforeMove = position;
2687*8af74909SZhong Yang 		position = NextPosition(position, direction);
2688*8af74909SZhong Yang 		if (position == positionBeforeMove)
2689*8af74909SZhong Yang 			break;
2690*8af74909SZhong Yang 	}
2691*8af74909SZhong Yang 	return - 1;
2692*8af74909SZhong Yang }
2693*8af74909SZhong Yang 
2694*8af74909SZhong Yang /**
2695*8af74909SZhong Yang  * Implementation of RegexSearchBase for the default built-in regular expression engine
2696*8af74909SZhong Yang  */
2697*8af74909SZhong Yang class BuiltinRegex : public RegexSearchBase {
2698*8af74909SZhong Yang public:
BuiltinRegex(CharClassify * charClassTable)2699*8af74909SZhong Yang 	explicit BuiltinRegex(CharClassify *charClassTable) : search(charClassTable) {}
2700*8af74909SZhong Yang 	BuiltinRegex(const BuiltinRegex &) = delete;
2701*8af74909SZhong Yang 	BuiltinRegex(BuiltinRegex &&) = delete;
2702*8af74909SZhong Yang 	BuiltinRegex &operator=(const BuiltinRegex &) = delete;
2703*8af74909SZhong Yang 	BuiltinRegex &operator=(BuiltinRegex &&) = delete;
2704*8af74909SZhong Yang 	~BuiltinRegex() override = default;
2705*8af74909SZhong Yang 
2706*8af74909SZhong Yang 	Sci::Position FindText(Document *doc, Sci::Position minPos, Sci::Position maxPos, const char *s,
2707*8af74909SZhong Yang                         bool caseSensitive, bool word, bool wordStart, int flags,
2708*8af74909SZhong Yang                         Sci::Position *length) override;
2709*8af74909SZhong Yang 
2710*8af74909SZhong Yang 	const char *SubstituteByPosition(Document *doc, const char *text, Sci::Position *length) override;
2711*8af74909SZhong Yang 
2712*8af74909SZhong Yang private:
2713*8af74909SZhong Yang 	RESearch search;
2714*8af74909SZhong Yang 	std::string substituted;
2715*8af74909SZhong Yang };
2716*8af74909SZhong Yang 
2717*8af74909SZhong Yang namespace {
2718*8af74909SZhong Yang 
2719*8af74909SZhong Yang /**
2720*8af74909SZhong Yang * RESearchRange keeps track of search range.
2721*8af74909SZhong Yang */
2722*8af74909SZhong Yang class RESearchRange {
2723*8af74909SZhong Yang public:
2724*8af74909SZhong Yang 	const Document *doc;
2725*8af74909SZhong Yang 	int increment;
2726*8af74909SZhong Yang 	Sci::Position startPos;
2727*8af74909SZhong Yang 	Sci::Position endPos;
2728*8af74909SZhong Yang 	Sci::Line lineRangeStart;
2729*8af74909SZhong Yang 	Sci::Line lineRangeEnd;
2730*8af74909SZhong Yang 	Sci::Line lineRangeBreak;
RESearchRange(const Document * doc_,Sci::Position minPos,Sci::Position maxPos)2731*8af74909SZhong Yang 	RESearchRange(const Document *doc_, Sci::Position minPos, Sci::Position maxPos) noexcept : doc(doc_) {
2732*8af74909SZhong Yang 		increment = (minPos <= maxPos) ? 1 : -1;
2733*8af74909SZhong Yang 
2734*8af74909SZhong Yang 		// Range endpoints should not be inside DBCS characters or between a CR and LF,
2735*8af74909SZhong Yang 		// but just in case, move them.
2736*8af74909SZhong Yang 		startPos = doc->MovePositionOutsideChar(minPos, 1, true);
2737*8af74909SZhong Yang 		endPos = doc->MovePositionOutsideChar(maxPos, 1, true);
2738*8af74909SZhong Yang 
2739*8af74909SZhong Yang 		lineRangeStart = doc->SciLineFromPosition(startPos);
2740*8af74909SZhong Yang 		lineRangeEnd = doc->SciLineFromPosition(endPos);
2741*8af74909SZhong Yang 		lineRangeBreak = lineRangeEnd + increment;
2742*8af74909SZhong Yang 	}
LineRange(Sci::Line line) const2743*8af74909SZhong Yang 	Range LineRange(Sci::Line line) const {
2744*8af74909SZhong Yang 		Range range(doc->LineStart(line), doc->LineEnd(line));
2745*8af74909SZhong Yang 		if (increment == 1) {
2746*8af74909SZhong Yang 			if (line == lineRangeStart)
2747*8af74909SZhong Yang 				range.start = startPos;
2748*8af74909SZhong Yang 			if (line == lineRangeEnd)
2749*8af74909SZhong Yang 				range.end = endPos;
2750*8af74909SZhong Yang 		} else {
2751*8af74909SZhong Yang 			if (line == lineRangeEnd)
2752*8af74909SZhong Yang 				range.start = endPos;
2753*8af74909SZhong Yang 			if (line == lineRangeStart)
2754*8af74909SZhong Yang 				range.end = startPos;
2755*8af74909SZhong Yang 		}
2756*8af74909SZhong Yang 		return range;
2757*8af74909SZhong Yang 	}
2758*8af74909SZhong Yang };
2759*8af74909SZhong Yang 
2760*8af74909SZhong Yang // Define a way for the Regular Expression code to access the document
2761*8af74909SZhong Yang class DocumentIndexer : public CharacterIndexer {
2762*8af74909SZhong Yang 	Document *pdoc;
2763*8af74909SZhong Yang 	Sci::Position end;
2764*8af74909SZhong Yang public:
DocumentIndexer(Document * pdoc_,Sci::Position end_)2765*8af74909SZhong Yang 	DocumentIndexer(Document *pdoc_, Sci::Position end_) noexcept :
2766*8af74909SZhong Yang 		pdoc(pdoc_), end(end_) {
2767*8af74909SZhong Yang 	}
2768*8af74909SZhong Yang 
2769*8af74909SZhong Yang 	DocumentIndexer(const DocumentIndexer &) = delete;
2770*8af74909SZhong Yang 	DocumentIndexer(DocumentIndexer &&) = delete;
2771*8af74909SZhong Yang 	DocumentIndexer &operator=(const DocumentIndexer &) = delete;
2772*8af74909SZhong Yang 	DocumentIndexer &operator=(DocumentIndexer &&) = delete;
2773*8af74909SZhong Yang 
2774*8af74909SZhong Yang 	~DocumentIndexer() override = default;
2775*8af74909SZhong Yang 
CharAt(Sci::Position index) const2776*8af74909SZhong Yang 	char CharAt(Sci::Position index) const noexcept override {
2777*8af74909SZhong Yang 		if (index < 0 || index >= end)
2778*8af74909SZhong Yang 			return 0;
2779*8af74909SZhong Yang 		else
2780*8af74909SZhong Yang 			return pdoc->CharAt(index);
2781*8af74909SZhong Yang 	}
2782*8af74909SZhong Yang };
2783*8af74909SZhong Yang 
2784*8af74909SZhong Yang #ifndef NO_CXX11_REGEX
2785*8af74909SZhong Yang 
2786*8af74909SZhong Yang class ByteIterator {
2787*8af74909SZhong Yang public:
2788*8af74909SZhong Yang 	typedef std::bidirectional_iterator_tag iterator_category;
2789*8af74909SZhong Yang 	typedef char value_type;
2790*8af74909SZhong Yang 	typedef ptrdiff_t difference_type;
2791*8af74909SZhong Yang 	typedef char* pointer;
2792*8af74909SZhong Yang 	typedef char& reference;
2793*8af74909SZhong Yang 
2794*8af74909SZhong Yang 	const Document *doc;
2795*8af74909SZhong Yang 	Sci::Position position;
2796*8af74909SZhong Yang 
ByteIterator(const Document * doc_=nullptr,Sci::Position position_=0)2797*8af74909SZhong Yang 	ByteIterator(const Document *doc_=nullptr, Sci::Position position_=0) noexcept :
2798*8af74909SZhong Yang 		doc(doc_), position(position_) {
2799*8af74909SZhong Yang 	}
ByteIterator(const ByteIterator & other)2800*8af74909SZhong Yang 	ByteIterator(const ByteIterator &other) noexcept {
2801*8af74909SZhong Yang 		doc = other.doc;
2802*8af74909SZhong Yang 		position = other.position;
2803*8af74909SZhong Yang 	}
ByteIterator(ByteIterator && other)2804*8af74909SZhong Yang 	ByteIterator(ByteIterator &&other) noexcept {
2805*8af74909SZhong Yang 		doc = other.doc;
2806*8af74909SZhong Yang 		position = other.position;
2807*8af74909SZhong Yang 	}
operator =(const ByteIterator & other)2808*8af74909SZhong Yang 	ByteIterator &operator=(const ByteIterator &other) noexcept {
2809*8af74909SZhong Yang 		if (this != &other) {
2810*8af74909SZhong Yang 			doc = other.doc;
2811*8af74909SZhong Yang 			position = other.position;
2812*8af74909SZhong Yang 		}
2813*8af74909SZhong Yang 		return *this;
2814*8af74909SZhong Yang 	}
2815*8af74909SZhong Yang 	ByteIterator &operator=(ByteIterator &&) noexcept = default;
2816*8af74909SZhong Yang 	~ByteIterator() = default;
operator *() const2817*8af74909SZhong Yang 	char operator*() const noexcept {
2818*8af74909SZhong Yang 		return doc->CharAt(position);
2819*8af74909SZhong Yang 	}
operator ++()2820*8af74909SZhong Yang 	ByteIterator &operator++() noexcept {
2821*8af74909SZhong Yang 		position++;
2822*8af74909SZhong Yang 		return *this;
2823*8af74909SZhong Yang 	}
operator ++(int)2824*8af74909SZhong Yang 	ByteIterator operator++(int) noexcept {
2825*8af74909SZhong Yang 		ByteIterator retVal(*this);
2826*8af74909SZhong Yang 		position++;
2827*8af74909SZhong Yang 		return retVal;
2828*8af74909SZhong Yang 	}
operator --()2829*8af74909SZhong Yang 	ByteIterator &operator--() noexcept {
2830*8af74909SZhong Yang 		position--;
2831*8af74909SZhong Yang 		return *this;
2832*8af74909SZhong Yang 	}
operator ==(const ByteIterator & other) const2833*8af74909SZhong Yang 	bool operator==(const ByteIterator &other) const noexcept {
2834*8af74909SZhong Yang 		return doc == other.doc && position == other.position;
2835*8af74909SZhong Yang 	}
operator !=(const ByteIterator & other) const2836*8af74909SZhong Yang 	bool operator!=(const ByteIterator &other) const noexcept {
2837*8af74909SZhong Yang 		return doc != other.doc || position != other.position;
2838*8af74909SZhong Yang 	}
Pos() const2839*8af74909SZhong Yang 	Sci::Position Pos() const noexcept {
2840*8af74909SZhong Yang 		return position;
2841*8af74909SZhong Yang 	}
PosRoundUp() const2842*8af74909SZhong Yang 	Sci::Position PosRoundUp() const noexcept {
2843*8af74909SZhong Yang 		return position;
2844*8af74909SZhong Yang 	}
2845*8af74909SZhong Yang };
2846*8af74909SZhong Yang 
2847*8af74909SZhong Yang // On Windows, wchar_t is 16 bits wide and on Unix it is 32 bits wide.
2848*8af74909SZhong Yang // Would be better to use sizeof(wchar_t) or similar to differentiate
2849*8af74909SZhong Yang // but easier for now to hard-code platforms.
2850*8af74909SZhong Yang // C++11 has char16_t and char32_t but neither Clang nor Visual C++
2851*8af74909SZhong Yang // appear to allow specializing basic_regex over these.
2852*8af74909SZhong Yang 
2853*8af74909SZhong Yang #ifdef _WIN32
2854*8af74909SZhong Yang #define WCHAR_T_IS_16 1
2855*8af74909SZhong Yang #else
2856*8af74909SZhong Yang #define WCHAR_T_IS_16 0
2857*8af74909SZhong Yang #endif
2858*8af74909SZhong Yang 
2859*8af74909SZhong Yang #if WCHAR_T_IS_16
2860*8af74909SZhong Yang 
2861*8af74909SZhong Yang // On Windows, report non-BMP characters as 2 separate surrogates as that
2862*8af74909SZhong Yang // matches wregex since it is based on wchar_t.
2863*8af74909SZhong Yang class UTF8Iterator {
2864*8af74909SZhong Yang 	// These 3 fields determine the iterator position and are used for comparisons
2865*8af74909SZhong Yang 	const Document *doc;
2866*8af74909SZhong Yang 	Sci::Position position;
2867*8af74909SZhong Yang 	size_t characterIndex;
2868*8af74909SZhong Yang 	// Remaining fields are derived from the determining fields so are excluded in comparisons
2869*8af74909SZhong Yang 	unsigned int lenBytes;
2870*8af74909SZhong Yang 	size_t lenCharacters;
2871*8af74909SZhong Yang 	wchar_t buffered[2];
2872*8af74909SZhong Yang public:
2873*8af74909SZhong Yang 	typedef std::bidirectional_iterator_tag iterator_category;
2874*8af74909SZhong Yang 	typedef wchar_t value_type;
2875*8af74909SZhong Yang 	typedef ptrdiff_t difference_type;
2876*8af74909SZhong Yang 	typedef wchar_t* pointer;
2877*8af74909SZhong Yang 	typedef wchar_t& reference;
2878*8af74909SZhong Yang 
UTF8Iterator(const Document * doc_=nullptr,Sci::Position position_=0)2879*8af74909SZhong Yang 	UTF8Iterator(const Document *doc_=nullptr, Sci::Position position_=0) noexcept :
2880*8af74909SZhong Yang 		doc(doc_), position(position_), characterIndex(0), lenBytes(0), lenCharacters(0), buffered{} {
2881*8af74909SZhong Yang 		buffered[0] = 0;
2882*8af74909SZhong Yang 		buffered[1] = 0;
2883*8af74909SZhong Yang 		if (doc) {
2884*8af74909SZhong Yang 			ReadCharacter();
2885*8af74909SZhong Yang 		}
2886*8af74909SZhong Yang 	}
UTF8Iterator(const UTF8Iterator & other)2887*8af74909SZhong Yang 	UTF8Iterator(const UTF8Iterator &other) noexcept : buffered{} {
2888*8af74909SZhong Yang 		doc = other.doc;
2889*8af74909SZhong Yang 		position = other.position;
2890*8af74909SZhong Yang 		characterIndex = other.characterIndex;
2891*8af74909SZhong Yang 		lenBytes = other.lenBytes;
2892*8af74909SZhong Yang 		lenCharacters = other.lenCharacters;
2893*8af74909SZhong Yang 		buffered[0] = other.buffered[0];
2894*8af74909SZhong Yang 		buffered[1] = other.buffered[1];
2895*8af74909SZhong Yang 	}
2896*8af74909SZhong Yang 	UTF8Iterator(UTF8Iterator &&other) noexcept = default;
operator =(const UTF8Iterator & other)2897*8af74909SZhong Yang 	UTF8Iterator &operator=(const UTF8Iterator &other) noexcept {
2898*8af74909SZhong Yang 		if (this != &other) {
2899*8af74909SZhong Yang 			doc = other.doc;
2900*8af74909SZhong Yang 			position = other.position;
2901*8af74909SZhong Yang 			characterIndex = other.characterIndex;
2902*8af74909SZhong Yang 			lenBytes = other.lenBytes;
2903*8af74909SZhong Yang 			lenCharacters = other.lenCharacters;
2904*8af74909SZhong Yang 			buffered[0] = other.buffered[0];
2905*8af74909SZhong Yang 			buffered[1] = other.buffered[1];
2906*8af74909SZhong Yang 		}
2907*8af74909SZhong Yang 		return *this;
2908*8af74909SZhong Yang 	}
2909*8af74909SZhong Yang 	UTF8Iterator &operator=(UTF8Iterator &&) noexcept = default;
2910*8af74909SZhong Yang 	~UTF8Iterator() = default;
operator *() const2911*8af74909SZhong Yang 	wchar_t operator*() const noexcept {
2912*8af74909SZhong Yang 		assert(lenCharacters != 0);
2913*8af74909SZhong Yang 		return buffered[characterIndex];
2914*8af74909SZhong Yang 	}
operator ++()2915*8af74909SZhong Yang 	UTF8Iterator &operator++() noexcept {
2916*8af74909SZhong Yang 		if ((characterIndex + 1) < (lenCharacters)) {
2917*8af74909SZhong Yang 			characterIndex++;
2918*8af74909SZhong Yang 		} else {
2919*8af74909SZhong Yang 			position += lenBytes;
2920*8af74909SZhong Yang 			ReadCharacter();
2921*8af74909SZhong Yang 			characterIndex = 0;
2922*8af74909SZhong Yang 		}
2923*8af74909SZhong Yang 		return *this;
2924*8af74909SZhong Yang 	}
operator ++(int)2925*8af74909SZhong Yang 	UTF8Iterator operator++(int) noexcept {
2926*8af74909SZhong Yang 		UTF8Iterator retVal(*this);
2927*8af74909SZhong Yang 		if ((characterIndex + 1) < (lenCharacters)) {
2928*8af74909SZhong Yang 			characterIndex++;
2929*8af74909SZhong Yang 		} else {
2930*8af74909SZhong Yang 			position += lenBytes;
2931*8af74909SZhong Yang 			ReadCharacter();
2932*8af74909SZhong Yang 			characterIndex = 0;
2933*8af74909SZhong Yang 		}
2934*8af74909SZhong Yang 		return retVal;
2935*8af74909SZhong Yang 	}
operator --()2936*8af74909SZhong Yang 	UTF8Iterator &operator--() noexcept {
2937*8af74909SZhong Yang 		if (characterIndex) {
2938*8af74909SZhong Yang 			characterIndex--;
2939*8af74909SZhong Yang 		} else {
2940*8af74909SZhong Yang 			position = doc->NextPosition(position, -1);
2941*8af74909SZhong Yang 			ReadCharacter();
2942*8af74909SZhong Yang 			characterIndex = lenCharacters - 1;
2943*8af74909SZhong Yang 		}
2944*8af74909SZhong Yang 		return *this;
2945*8af74909SZhong Yang 	}
operator ==(const UTF8Iterator & other) const2946*8af74909SZhong Yang 	bool operator==(const UTF8Iterator &other) const noexcept {
2947*8af74909SZhong Yang 		// Only test the determining fields, not the character widths and values derived from this
2948*8af74909SZhong Yang 		return doc == other.doc &&
2949*8af74909SZhong Yang 			position == other.position &&
2950*8af74909SZhong Yang 			characterIndex == other.characterIndex;
2951*8af74909SZhong Yang 	}
operator !=(const UTF8Iterator & other) const2952*8af74909SZhong Yang 	bool operator!=(const UTF8Iterator &other) const noexcept {
2953*8af74909SZhong Yang 		// Only test the determining fields, not the character widths and values derived from this
2954*8af74909SZhong Yang 		return doc != other.doc ||
2955*8af74909SZhong Yang 			position != other.position ||
2956*8af74909SZhong Yang 			characterIndex != other.characterIndex;
2957*8af74909SZhong Yang 	}
Pos() const2958*8af74909SZhong Yang 	Sci::Position Pos() const noexcept {
2959*8af74909SZhong Yang 		return position;
2960*8af74909SZhong Yang 	}
PosRoundUp() const2961*8af74909SZhong Yang 	Sci::Position PosRoundUp() const noexcept {
2962*8af74909SZhong Yang 		if (characterIndex)
2963*8af74909SZhong Yang 			return position + lenBytes;	// Force to end of character
2964*8af74909SZhong Yang 		else
2965*8af74909SZhong Yang 			return position;
2966*8af74909SZhong Yang 	}
2967*8af74909SZhong Yang private:
ReadCharacter()2968*8af74909SZhong Yang 	void ReadCharacter() noexcept {
2969*8af74909SZhong Yang 		const Document::CharacterExtracted charExtracted = doc->ExtractCharacter(position);
2970*8af74909SZhong Yang 		lenBytes = charExtracted.widthBytes;
2971*8af74909SZhong Yang 		if (charExtracted.character == unicodeReplacementChar) {
2972*8af74909SZhong Yang 			lenCharacters = 1;
2973*8af74909SZhong Yang 			buffered[0] = static_cast<wchar_t>(charExtracted.character);
2974*8af74909SZhong Yang 		} else {
2975*8af74909SZhong Yang 			lenCharacters = UTF16FromUTF32Character(charExtracted.character, buffered);
2976*8af74909SZhong Yang 		}
2977*8af74909SZhong Yang 	}
2978*8af74909SZhong Yang };
2979*8af74909SZhong Yang 
2980*8af74909SZhong Yang #else
2981*8af74909SZhong Yang 
2982*8af74909SZhong Yang // On Unix, report non-BMP characters as single characters
2983*8af74909SZhong Yang 
2984*8af74909SZhong Yang class UTF8Iterator {
2985*8af74909SZhong Yang 	const Document *doc;
2986*8af74909SZhong Yang 	Sci::Position position;
2987*8af74909SZhong Yang public:
2988*8af74909SZhong Yang 	typedef std::bidirectional_iterator_tag iterator_category;
2989*8af74909SZhong Yang 	typedef wchar_t value_type;
2990*8af74909SZhong Yang 	typedef ptrdiff_t difference_type;
2991*8af74909SZhong Yang 	typedef wchar_t* pointer;
2992*8af74909SZhong Yang 	typedef wchar_t& reference;
2993*8af74909SZhong Yang 
UTF8Iterator(const Document * doc_=nullptr,Sci::Position position_=0)2994*8af74909SZhong Yang 	UTF8Iterator(const Document *doc_=nullptr, Sci::Position position_=0) noexcept :
2995*8af74909SZhong Yang 		doc(doc_), position(position_) {
2996*8af74909SZhong Yang 	}
UTF8Iterator(const UTF8Iterator & other)2997*8af74909SZhong Yang 	UTF8Iterator(const UTF8Iterator &other) noexcept {
2998*8af74909SZhong Yang 		doc = other.doc;
2999*8af74909SZhong Yang 		position = other.position;
3000*8af74909SZhong Yang 	}
3001*8af74909SZhong Yang 	UTF8Iterator(UTF8Iterator &&other) noexcept = default;
operator =(const UTF8Iterator & other)3002*8af74909SZhong Yang 	UTF8Iterator &operator=(const UTF8Iterator &other) noexcept {
3003*8af74909SZhong Yang 		if (this != &other) {
3004*8af74909SZhong Yang 			doc = other.doc;
3005*8af74909SZhong Yang 			position = other.position;
3006*8af74909SZhong Yang 		}
3007*8af74909SZhong Yang 		return *this;
3008*8af74909SZhong Yang 	}
3009*8af74909SZhong Yang 	UTF8Iterator &operator=(UTF8Iterator &&) noexcept = default;
3010*8af74909SZhong Yang 	~UTF8Iterator() = default;
operator *() const3011*8af74909SZhong Yang 	wchar_t operator*() const noexcept {
3012*8af74909SZhong Yang 		const Document::CharacterExtracted charExtracted = doc->ExtractCharacter(position);
3013*8af74909SZhong Yang 		return charExtracted.character;
3014*8af74909SZhong Yang 	}
operator ++()3015*8af74909SZhong Yang 	UTF8Iterator &operator++() noexcept {
3016*8af74909SZhong Yang 		position = doc->NextPosition(position, 1);
3017*8af74909SZhong Yang 		return *this;
3018*8af74909SZhong Yang 	}
operator ++(int)3019*8af74909SZhong Yang 	UTF8Iterator operator++(int) noexcept {
3020*8af74909SZhong Yang 		UTF8Iterator retVal(*this);
3021*8af74909SZhong Yang 		position = doc->NextPosition(position, 1);
3022*8af74909SZhong Yang 		return retVal;
3023*8af74909SZhong Yang 	}
operator --()3024*8af74909SZhong Yang 	UTF8Iterator &operator--() noexcept {
3025*8af74909SZhong Yang 		position = doc->NextPosition(position, -1);
3026*8af74909SZhong Yang 		return *this;
3027*8af74909SZhong Yang 	}
operator ==(const UTF8Iterator & other) const3028*8af74909SZhong Yang 	bool operator==(const UTF8Iterator &other) const noexcept {
3029*8af74909SZhong Yang 		return doc == other.doc && position == other.position;
3030*8af74909SZhong Yang 	}
operator !=(const UTF8Iterator & other) const3031*8af74909SZhong Yang 	bool operator!=(const UTF8Iterator &other) const noexcept {
3032*8af74909SZhong Yang 		return doc != other.doc || position != other.position;
3033*8af74909SZhong Yang 	}
Pos() const3034*8af74909SZhong Yang 	Sci::Position Pos() const noexcept {
3035*8af74909SZhong Yang 		return position;
3036*8af74909SZhong Yang 	}
PosRoundUp() const3037*8af74909SZhong Yang 	Sci::Position PosRoundUp() const noexcept {
3038*8af74909SZhong Yang 		return position;
3039*8af74909SZhong Yang 	}
3040*8af74909SZhong Yang };
3041*8af74909SZhong Yang 
3042*8af74909SZhong Yang #endif
3043*8af74909SZhong Yang 
MatchFlags(const Document * doc,Sci::Position startPos,Sci::Position endPos)3044*8af74909SZhong Yang std::regex_constants::match_flag_type MatchFlags(const Document *doc, Sci::Position startPos, Sci::Position endPos) {
3045*8af74909SZhong Yang 	std::regex_constants::match_flag_type flagsMatch = std::regex_constants::match_default;
3046*8af74909SZhong Yang 	if (!doc->IsLineStartPosition(startPos))
3047*8af74909SZhong Yang 		flagsMatch |= std::regex_constants::match_not_bol;
3048*8af74909SZhong Yang 	if (!doc->IsLineEndPosition(endPos))
3049*8af74909SZhong Yang 		flagsMatch |= std::regex_constants::match_not_eol;
3050*8af74909SZhong Yang 	return flagsMatch;
3051*8af74909SZhong Yang }
3052*8af74909SZhong Yang 
3053*8af74909SZhong Yang template<typename Iterator, typename Regex>
MatchOnLines(const Document * doc,const Regex & regexp,const RESearchRange & resr,RESearch & search)3054*8af74909SZhong Yang bool MatchOnLines(const Document *doc, const Regex &regexp, const RESearchRange &resr, RESearch &search) {
3055*8af74909SZhong Yang 	std::match_results<Iterator> match;
3056*8af74909SZhong Yang 
3057*8af74909SZhong Yang 	// MSVC and libc++ have problems with ^ and $ matching line ends inside a range.
3058*8af74909SZhong Yang 	// CRLF line ends are also a problem as ^ and $ only treat LF as a line end.
3059*8af74909SZhong Yang 	// The std::regex::multiline option was added to C++17 to improve behaviour but
3060*8af74909SZhong Yang 	// has not been implemented by compiler runtimes with MSVC always in multiline
3061*8af74909SZhong Yang 	// mode and libc++ and libstdc++ always in single-line mode.
3062*8af74909SZhong Yang 	// If multiline regex worked well then the line by line iteration could be removed
3063*8af74909SZhong Yang 	// for the forwards case and replaced with the following 4 lines:
3064*8af74909SZhong Yang #ifdef REGEX_MULTILINE
3065*8af74909SZhong Yang 	Iterator itStart(doc, resr.startPos);
3066*8af74909SZhong Yang 	Iterator itEnd(doc, resr.endPos);
3067*8af74909SZhong Yang 	const std::regex_constants::match_flag_type flagsMatch = MatchFlags(doc, resr.startPos, resr.endPos);
3068*8af74909SZhong Yang 	const bool matched = std::regex_search(itStart, itEnd, match, regexp, flagsMatch);
3069*8af74909SZhong Yang #else
3070*8af74909SZhong Yang 	// Line by line.
3071*8af74909SZhong Yang 	bool matched = false;
3072*8af74909SZhong Yang 	for (Sci::Line line = resr.lineRangeStart; line != resr.lineRangeBreak; line += resr.increment) {
3073*8af74909SZhong Yang 		const Range lineRange = resr.LineRange(line);
3074*8af74909SZhong Yang 		Iterator itStart(doc, lineRange.start);
3075*8af74909SZhong Yang 		Iterator itEnd(doc, lineRange.end);
3076*8af74909SZhong Yang 		std::regex_constants::match_flag_type flagsMatch = MatchFlags(doc, lineRange.start, lineRange.end);
3077*8af74909SZhong Yang 		matched = std::regex_search(itStart, itEnd, match, regexp, flagsMatch);
3078*8af74909SZhong Yang 		// Check for the last match on this line.
3079*8af74909SZhong Yang 		if (matched) {
3080*8af74909SZhong Yang 			if (resr.increment == -1) {
3081*8af74909SZhong Yang 				while (matched) {
3082*8af74909SZhong Yang 					Iterator itNext(doc, match[0].second.PosRoundUp());
3083*8af74909SZhong Yang 					flagsMatch = MatchFlags(doc, itNext.Pos(), lineRange.end);
3084*8af74909SZhong Yang 					std::match_results<Iterator> matchNext;
3085*8af74909SZhong Yang 					matched = std::regex_search(itNext, itEnd, matchNext, regexp, flagsMatch);
3086*8af74909SZhong Yang 					if (matched) {
3087*8af74909SZhong Yang 						if (match[0].first == match[0].second) {
3088*8af74909SZhong Yang 							// Empty match means failure so exit
3089*8af74909SZhong Yang 							return false;
3090*8af74909SZhong Yang 						}
3091*8af74909SZhong Yang 						match = matchNext;
3092*8af74909SZhong Yang 					}
3093*8af74909SZhong Yang 				}
3094*8af74909SZhong Yang 				matched = true;
3095*8af74909SZhong Yang 			}
3096*8af74909SZhong Yang 			break;
3097*8af74909SZhong Yang 		}
3098*8af74909SZhong Yang 	}
3099*8af74909SZhong Yang #endif
3100*8af74909SZhong Yang 	if (matched) {
3101*8af74909SZhong Yang 		for (size_t co = 0; co < match.size(); co++) {
3102*8af74909SZhong Yang 			search.bopat[co] = match[co].first.Pos();
3103*8af74909SZhong Yang 			search.eopat[co] = match[co].second.PosRoundUp();
3104*8af74909SZhong Yang 			const Sci::Position lenMatch = search.eopat[co] - search.bopat[co];
3105*8af74909SZhong Yang 			search.pat[co].resize(lenMatch);
3106*8af74909SZhong Yang 			for (Sci::Position iPos = 0; iPos < lenMatch; iPos++) {
3107*8af74909SZhong Yang 				search.pat[co][iPos] = doc->CharAt(iPos + search.bopat[co]);
3108*8af74909SZhong Yang 			}
3109*8af74909SZhong Yang 		}
3110*8af74909SZhong Yang 	}
3111*8af74909SZhong Yang 	return matched;
3112*8af74909SZhong Yang }
3113*8af74909SZhong Yang 
Cxx11RegexFindText(const Document * doc,Sci::Position minPos,Sci::Position maxPos,const char * s,bool caseSensitive,Sci::Position * length,RESearch & search)3114*8af74909SZhong Yang Sci::Position Cxx11RegexFindText(const Document *doc, Sci::Position minPos, Sci::Position maxPos, const char *s,
3115*8af74909SZhong Yang 	bool caseSensitive, Sci::Position *length, RESearch &search) {
3116*8af74909SZhong Yang 	const RESearchRange resr(doc, minPos, maxPos);
3117*8af74909SZhong Yang 	try {
3118*8af74909SZhong Yang 		//ElapsedPeriod ep;
3119*8af74909SZhong Yang 		std::regex::flag_type flagsRe = std::regex::ECMAScript;
3120*8af74909SZhong Yang 		// Flags that appear to have no effect:
3121*8af74909SZhong Yang 		// | std::regex::collate | std::regex::extended;
3122*8af74909SZhong Yang 		if (!caseSensitive)
3123*8af74909SZhong Yang 			flagsRe = flagsRe | std::regex::icase;
3124*8af74909SZhong Yang 
3125*8af74909SZhong Yang 		// Clear the RESearch so can fill in matches
3126*8af74909SZhong Yang 		search.Clear();
3127*8af74909SZhong Yang 
3128*8af74909SZhong Yang 		bool matched = false;
3129*8af74909SZhong Yang 		if (SC_CP_UTF8 == doc->dbcsCodePage) {
3130*8af74909SZhong Yang 			const std::wstring ws = WStringFromUTF8(s);
3131*8af74909SZhong Yang 			std::wregex regexp;
3132*8af74909SZhong Yang 			regexp.assign(ws, flagsRe);
3133*8af74909SZhong Yang 			matched = MatchOnLines<UTF8Iterator>(doc, regexp, resr, search);
3134*8af74909SZhong Yang 
3135*8af74909SZhong Yang 		} else {
3136*8af74909SZhong Yang 			std::regex regexp;
3137*8af74909SZhong Yang 			regexp.assign(s, flagsRe);
3138*8af74909SZhong Yang 			matched = MatchOnLines<ByteIterator>(doc, regexp, resr, search);
3139*8af74909SZhong Yang 		}
3140*8af74909SZhong Yang 
3141*8af74909SZhong Yang 		Sci::Position posMatch = -1;
3142*8af74909SZhong Yang 		if (matched) {
3143*8af74909SZhong Yang 			posMatch = search.bopat[0];
3144*8af74909SZhong Yang 			*length = search.eopat[0] - search.bopat[0];
3145*8af74909SZhong Yang 		}
3146*8af74909SZhong Yang 		// Example - search in doc/ScintillaHistory.html for
3147*8af74909SZhong Yang 		// [[:upper:]]eta[[:space:]]
3148*8af74909SZhong Yang 		// On MacBook, normally around 1 second but with locale imbued -> 14 seconds.
3149*8af74909SZhong Yang 		//const double durSearch = ep.Duration(true);
3150*8af74909SZhong Yang 		//Platform::DebugPrintf("Search:%9.6g \n", durSearch);
3151*8af74909SZhong Yang 		return posMatch;
3152*8af74909SZhong Yang 	} catch (std::regex_error &) {
3153*8af74909SZhong Yang 		// Failed to create regular expression
3154*8af74909SZhong Yang 		throw RegexError();
3155*8af74909SZhong Yang 	} catch (...) {
3156*8af74909SZhong Yang 		// Failed in some other way
3157*8af74909SZhong Yang 		return -1;
3158*8af74909SZhong Yang 	}
3159*8af74909SZhong Yang }
3160*8af74909SZhong Yang 
3161*8af74909SZhong Yang #endif
3162*8af74909SZhong Yang 
3163*8af74909SZhong Yang }
3164*8af74909SZhong Yang 
FindText(Document * doc,Sci::Position minPos,Sci::Position maxPos,const char * s,bool caseSensitive,bool,bool,int flags,Sci::Position * length)3165*8af74909SZhong Yang Sci::Position BuiltinRegex::FindText(Document *doc, Sci::Position minPos, Sci::Position maxPos, const char *s,
3166*8af74909SZhong Yang                         bool caseSensitive, bool, bool, int flags,
3167*8af74909SZhong Yang                         Sci::Position *length) {
3168*8af74909SZhong Yang 
3169*8af74909SZhong Yang #ifndef NO_CXX11_REGEX
3170*8af74909SZhong Yang 	if (flags & SCFIND_CXX11REGEX) {
3171*8af74909SZhong Yang 			return Cxx11RegexFindText(doc, minPos, maxPos, s,
3172*8af74909SZhong Yang 			caseSensitive, length, search);
3173*8af74909SZhong Yang 	}
3174*8af74909SZhong Yang #endif
3175*8af74909SZhong Yang 
3176*8af74909SZhong Yang 	const RESearchRange resr(doc, minPos, maxPos);
3177*8af74909SZhong Yang 
3178*8af74909SZhong Yang 	const bool posix = (flags & SCFIND_POSIX) != 0;
3179*8af74909SZhong Yang 
3180*8af74909SZhong Yang 	const char *errmsg = search.Compile(s, *length, caseSensitive, posix);
3181*8af74909SZhong Yang 	if (errmsg) {
3182*8af74909SZhong Yang 		return -1;
3183*8af74909SZhong Yang 	}
3184*8af74909SZhong Yang 	// Find a variable in a property file: \$(\([A-Za-z0-9_.]+\))
3185*8af74909SZhong Yang 	// Replace first '.' with '-' in each property file variable reference:
3186*8af74909SZhong Yang 	//     Search: \$(\([A-Za-z0-9_-]+\)\.\([A-Za-z0-9_.]+\))
3187*8af74909SZhong Yang 	//     Replace: $(\1-\2)
3188*8af74909SZhong Yang 	Sci::Position pos = -1;
3189*8af74909SZhong Yang 	Sci::Position lenRet = 0;
3190*8af74909SZhong Yang 	const bool searchforLineStart = s[0] == '^';
3191*8af74909SZhong Yang 	const char searchEnd = s[*length - 1];
3192*8af74909SZhong Yang 	const char searchEndPrev = (*length > 1) ? s[*length - 2] : '\0';
3193*8af74909SZhong Yang 	const bool searchforLineEnd = (searchEnd == '$') && (searchEndPrev != '\\');
3194*8af74909SZhong Yang 	for (Sci::Line line = resr.lineRangeStart; line != resr.lineRangeBreak; line += resr.increment) {
3195*8af74909SZhong Yang 		Sci::Position startOfLine = doc->LineStart(line);
3196*8af74909SZhong Yang 		Sci::Position endOfLine = doc->LineEnd(line);
3197*8af74909SZhong Yang 		if (resr.increment == 1) {
3198*8af74909SZhong Yang 			if (line == resr.lineRangeStart) {
3199*8af74909SZhong Yang 				if ((resr.startPos != startOfLine) && searchforLineStart)
3200*8af74909SZhong Yang 					continue;	// Can't match start of line if start position after start of line
3201*8af74909SZhong Yang 				startOfLine = resr.startPos;
3202*8af74909SZhong Yang 			}
3203*8af74909SZhong Yang 			if (line == resr.lineRangeEnd) {
3204*8af74909SZhong Yang 				if ((resr.endPos != endOfLine) && searchforLineEnd)
3205*8af74909SZhong Yang 					continue;	// Can't match end of line if end position before end of line
3206*8af74909SZhong Yang 				endOfLine = resr.endPos;
3207*8af74909SZhong Yang 			}
3208*8af74909SZhong Yang 		} else {
3209*8af74909SZhong Yang 			if (line == resr.lineRangeEnd) {
3210*8af74909SZhong Yang 				if ((resr.endPos != startOfLine) && searchforLineStart)
3211*8af74909SZhong Yang 					continue;	// Can't match start of line if end position after start of line
3212*8af74909SZhong Yang 				startOfLine = resr.endPos;
3213*8af74909SZhong Yang 			}
3214*8af74909SZhong Yang 			if (line == resr.lineRangeStart) {
3215*8af74909SZhong Yang 				if ((resr.startPos != endOfLine) && searchforLineEnd)
3216*8af74909SZhong Yang 					continue;	// Can't match end of line if start position before end of line
3217*8af74909SZhong Yang 				endOfLine = resr.startPos;
3218*8af74909SZhong Yang 			}
3219*8af74909SZhong Yang 		}
3220*8af74909SZhong Yang 
3221*8af74909SZhong Yang 		const DocumentIndexer di(doc, endOfLine);
3222*8af74909SZhong Yang 		int success = search.Execute(di, startOfLine, endOfLine);
3223*8af74909SZhong Yang 		if (success) {
3224*8af74909SZhong Yang 			pos = search.bopat[0];
3225*8af74909SZhong Yang 			// Ensure only whole characters selected
3226*8af74909SZhong Yang 			search.eopat[0] = doc->MovePositionOutsideChar(search.eopat[0], 1, false);
3227*8af74909SZhong Yang 			lenRet = search.eopat[0] - search.bopat[0];
3228*8af74909SZhong Yang 			// There can be only one start of a line, so no need to look for last match in line
3229*8af74909SZhong Yang 			if ((resr.increment == -1) && !searchforLineStart) {
3230*8af74909SZhong Yang 				// Check for the last match on this line.
3231*8af74909SZhong Yang 				int repetitions = 1000;	// Break out of infinite loop
3232*8af74909SZhong Yang 				while (success && (search.eopat[0] <= endOfLine) && (repetitions--)) {
3233*8af74909SZhong Yang 					success = search.Execute(di, pos+1, endOfLine);
3234*8af74909SZhong Yang 					if (success) {
3235*8af74909SZhong Yang 						if (search.eopat[0] <= minPos) {
3236*8af74909SZhong Yang 							pos = search.bopat[0];
3237*8af74909SZhong Yang 							lenRet = search.eopat[0] - search.bopat[0];
3238*8af74909SZhong Yang 						} else {
3239*8af74909SZhong Yang 							success = 0;
3240*8af74909SZhong Yang 						}
3241*8af74909SZhong Yang 					}
3242*8af74909SZhong Yang 				}
3243*8af74909SZhong Yang 			}
3244*8af74909SZhong Yang 			break;
3245*8af74909SZhong Yang 		}
3246*8af74909SZhong Yang 	}
3247*8af74909SZhong Yang 	*length = lenRet;
3248*8af74909SZhong Yang 	return pos;
3249*8af74909SZhong Yang }
3250*8af74909SZhong Yang 
SubstituteByPosition(Document * doc,const char * text,Sci::Position * length)3251*8af74909SZhong Yang const char *BuiltinRegex::SubstituteByPosition(Document *doc, const char *text, Sci::Position *length) {
3252*8af74909SZhong Yang 	substituted.clear();
3253*8af74909SZhong Yang 	const DocumentIndexer di(doc, doc->Length());
3254*8af74909SZhong Yang 	search.GrabMatches(di);
3255*8af74909SZhong Yang 	for (Sci::Position j = 0; j < *length; j++) {
3256*8af74909SZhong Yang 		if (text[j] == '\\') {
3257*8af74909SZhong Yang 			if (text[j + 1] >= '0' && text[j + 1] <= '9') {
3258*8af74909SZhong Yang 				const unsigned int patNum = text[j + 1] - '0';
3259*8af74909SZhong Yang 				const Sci::Position len = search.eopat[patNum] - search.bopat[patNum];
3260*8af74909SZhong Yang 				if (!search.pat[patNum].empty())	// Will be null if try for a match that did not occur
3261*8af74909SZhong Yang 					substituted.append(search.pat[patNum].c_str(), len);
3262*8af74909SZhong Yang 				j++;
3263*8af74909SZhong Yang 			} else {
3264*8af74909SZhong Yang 				j++;
3265*8af74909SZhong Yang 				switch (text[j]) {
3266*8af74909SZhong Yang 				case 'a':
3267*8af74909SZhong Yang 					substituted.push_back('\a');
3268*8af74909SZhong Yang 					break;
3269*8af74909SZhong Yang 				case 'b':
3270*8af74909SZhong Yang 					substituted.push_back('\b');
3271*8af74909SZhong Yang 					break;
3272*8af74909SZhong Yang 				case 'f':
3273*8af74909SZhong Yang 					substituted.push_back('\f');
3274*8af74909SZhong Yang 					break;
3275*8af74909SZhong Yang 				case 'n':
3276*8af74909SZhong Yang 					substituted.push_back('\n');
3277*8af74909SZhong Yang 					break;
3278*8af74909SZhong Yang 				case 'r':
3279*8af74909SZhong Yang 					substituted.push_back('\r');
3280*8af74909SZhong Yang 					break;
3281*8af74909SZhong Yang 				case 't':
3282*8af74909SZhong Yang 					substituted.push_back('\t');
3283*8af74909SZhong Yang 					break;
3284*8af74909SZhong Yang 				case 'v':
3285*8af74909SZhong Yang 					substituted.push_back('\v');
3286*8af74909SZhong Yang 					break;
3287*8af74909SZhong Yang 				case '\\':
3288*8af74909SZhong Yang 					substituted.push_back('\\');
3289*8af74909SZhong Yang 					break;
3290*8af74909SZhong Yang 				default:
3291*8af74909SZhong Yang 					substituted.push_back('\\');
3292*8af74909SZhong Yang 					j--;
3293*8af74909SZhong Yang 				}
3294*8af74909SZhong Yang 			}
3295*8af74909SZhong Yang 		} else {
3296*8af74909SZhong Yang 			substituted.push_back(text[j]);
3297*8af74909SZhong Yang 		}
3298*8af74909SZhong Yang 	}
3299*8af74909SZhong Yang 	*length = substituted.length();
3300*8af74909SZhong Yang 	return substituted.c_str();
3301*8af74909SZhong Yang }
3302*8af74909SZhong Yang 
3303*8af74909SZhong Yang #ifndef SCI_OWNREGEX
3304*8af74909SZhong Yang 
CreateRegexSearch(CharClassify * charClassTable)3305*8af74909SZhong Yang RegexSearchBase *Scintilla::CreateRegexSearch(CharClassify *charClassTable) {
3306*8af74909SZhong Yang 	return new BuiltinRegex(charClassTable);
3307*8af74909SZhong Yang }
3308*8af74909SZhong Yang 
3309*8af74909SZhong Yang #endif
3310