1// Scintilla source code edit control
2/** @file Document.h
3 ** Text document that handles notifications, DBCS, styling, words and end of line.
4 **/
5// Copyright 1998-2011 by Neil Hodgson <neilh@scintilla.org>
6// The License.txt file describes the conditions under which this software may be distributed.
7
8#ifndef DOCUMENT_H
9#define DOCUMENT_H
10
11#include "ILexer.h"
12#include "ILoader.h"
13#include "PerLine.h"
14#include "CaseFolder.h"
15#include "Position.h"
16#include "Decoration.h"
17#include "CharClassify.h"
18#include "ScintillaTypes.h"
19#include "CharacterCategoryMap.h"
20
21#include <optional>
22#include <vector>
23#include <memory>
24
25namespace Scintilla::Internal {
26
27class DocWatcher;
28class DocModification;
29class Document;
30class LineMarkers;
31class LineLevels;
32class LineState;
33class LineAnnotation;
34
35enum class EncodingFamily { eightBit, unicode, dbcs };
36
37/**
38 * The range class represents a range of text in a document.
39 * The two values are not sorted as one end may be more significant than the other
40 * as is the case for the selection where the end position is the position of the caret.
41 * If either position is invalidPosition then the range is invalid and most operations will fail.
42 */
43class Range {
44public:
45 Sci::Position start;
46 Sci::Position end;
47
48 explicit Range(Sci::Position pos=0) noexcept :
49 start(pos), end(pos) {
50 }
51 Range(Sci::Position start_, Sci::Position end_) noexcept :
52 start(start_), end(end_) {
53 }
54
55 bool operator==(const Range &other) const noexcept {
56 return (start == other.start) && (end == other.end);
57 }
58
59 bool Valid() const noexcept {
60 return (start != Sci::invalidPosition) && (end != Sci::invalidPosition);
61 }
62
63 Sci::Position First() const noexcept {
64 return (start <= end) ? start : end;
65 }
66
67 Sci::Position Last() const noexcept {
68 return (start > end) ? start : end;
69 }
70
71 // Is the position within the range?
72 bool Contains(Sci::Position pos) const noexcept {
73 if (start < end) {
74 return (pos >= start && pos <= end);
75 } else {
76 return (pos <= start && pos >= end);
77 }
78 }
79
80 // Is the character after pos within the range?
81 bool ContainsCharacter(Sci::Position pos) const noexcept {
82 if (start < end) {
83 return (pos >= start && pos < end);
84 } else {
85 return (pos < start && pos >= end);
86 }
87 }
88
89 bool Contains(Range other) const noexcept {
90 return Contains(other.start) && Contains(other.end);
91 }
92
93 bool Overlaps(Range other) const noexcept {
94 return
95 Contains(other.start) ||
96 Contains(other.end) ||
97 other.Contains(start) ||
98 other.Contains(end);
99 }
100};
101
102/**
103 * Interface class for regular expression searching
104 */
105class RegexSearchBase {
106public:
107 virtual ~RegexSearchBase() = default;
108
109 virtual Sci::Position FindText(Document *doc, Sci::Position minPos, Sci::Position maxPos, const char *s,
110 bool caseSensitive, bool word, bool wordStart, Scintilla::FindOption flags, Sci::Position *length) = 0;
111
112 ///@return String with the substitutions, must remain valid until the next call or destruction
113 virtual const char *SubstituteByPosition(Document *doc, const char *text, Sci::Position *length) = 0;
114};
115
116/// Factory function for RegexSearchBase
117extern RegexSearchBase *CreateRegexSearch(CharClassify *charClassTable);
118
119struct StyledText {
120 size_t length;
121 const char *text;
122 bool multipleStyles;
123 size_t style;
124 const unsigned char *styles;
125 StyledText(size_t length_, const char *text_, bool multipleStyles_, int style_, const unsigned char *styles_) noexcept :
126 length(length_), text(text_), multipleStyles(multipleStyles_), style(style_), styles(styles_) {
127 }
128 // Return number of bytes from start to before '\n' or end of text.
129 // Return 1 when start is outside text
130 size_t LineLength(size_t start) const noexcept {
131 size_t cur = start;
132 while ((cur < length) && (text[cur] != '\n'))
133 cur++;
134 return cur-start;
135 }
136 size_t StyleAt(size_t i) const noexcept {
137 return multipleStyles ? styles[i] : style;
138 }
139};
140
141class HighlightDelimiter {
142public:
143 HighlightDelimiter() noexcept : isEnabled(false) {
144 Clear();
145 }
146
147 void Clear() noexcept {
148 beginFoldBlock = -1;
149 endFoldBlock = -1;
150 firstChangeableLineBefore = -1;
151 firstChangeableLineAfter = -1;
152 }
153
154 bool NeedsDrawing(Sci::Line line) const noexcept {
155 return isEnabled && (line <= firstChangeableLineBefore || line >= firstChangeableLineAfter);
156 }
157
158 bool IsFoldBlockHighlighted(Sci::Line line) const noexcept {
159 return isEnabled && beginFoldBlock != -1 && beginFoldBlock <= line && line <= endFoldBlock;
160 }
161
162 bool IsHeadOfFoldBlock(Sci::Line line) const noexcept {
163 return beginFoldBlock == line && line < endFoldBlock;
164 }
165
166 bool IsBodyOfFoldBlock(Sci::Line line) const noexcept {
167 return beginFoldBlock != -1 && beginFoldBlock < line && line < endFoldBlock;
168 }
169
170 bool IsTailOfFoldBlock(Sci::Line line) const noexcept {
171 return beginFoldBlock != -1 && beginFoldBlock < line && line == endFoldBlock;
172 }
173
174 Sci::Line beginFoldBlock; // Begin of current fold block
175 Sci::Line endFoldBlock; // End of current fold block
176 Sci::Line firstChangeableLineBefore; // First line that triggers repaint before starting line that determined current fold block
177 Sci::Line firstChangeableLineAfter; // First line that triggers repaint after starting line that determined current fold block
178 bool isEnabled;
179};
180
181struct LexerReleaser {
182 // Called by unique_ptr to destroy/free the Resource
183 void operator()(Scintilla::ILexer5 *pLexer) noexcept {
184 if (pLexer) {
185 try {
186 pLexer->Release();
187 } catch (...) {
188 // ILexer5::Release must not throw, ignore if it does.
189 }
190 }
191 }
192};
193
194using LexerInstance = std::unique_ptr<Scintilla::ILexer5, LexerReleaser>;
195
196// LexInterface defines the interface to ILexer used in Document.
197// The LexState subclass is actually created and that is used within ScintillaBase
198// to provide more methods that are exposed through Scintilla's external API.
199class LexInterface {
200protected:
201 Document *pdoc;
202 LexerInstance instance;
203 bool performingStyle; ///< Prevent reentrance
204public:
205 explicit LexInterface(Document *pdoc_) noexcept;
206 // Deleted so LexInterface objects can not be copied.
207 LexInterface(const LexInterface &) = delete;
208 LexInterface(LexInterface &&) = delete;
209 LexInterface &operator=(const LexInterface &) = delete;
210 LexInterface &operator=(LexInterface &&) = delete;
211 virtual ~LexInterface() noexcept;
212 void SetInstance(ILexer5 *instance_);
213 void Colourise(Sci::Position start, Sci::Position end);
214 virtual Scintilla::LineEndType LineEndTypesSupported();
215 bool UseContainerLexing() const noexcept;
216};
217
218struct RegexError : public std::runtime_error {
219 RegexError() : std::runtime_error("regex failure") {}
220};
221
222/**
223 * The ActionDuration class stores the average time taken for some action such as styling or
224 * wrapping a line. It is used to decide how many repetitions of that action can be performed
225 * on idle to maximize efficiency without affecting application responsiveness.
226 * The duration changes if the time for the action changes. For example, if a simple lexer is
227 * changed to a complex lexer. Changes are damped and clamped to avoid short periods of easy
228 * or difficult processing moving the value too far leading to inefficiency or poor user
229 * experience.
230 */
231
232class ActionDuration {
233 double duration;
234 const double minDuration;
235 const double maxDuration;
236public:
237 ActionDuration(double duration_, double minDuration_, double maxDuration_) noexcept;
238 void AddSample(size_t numberActions, double durationOfActions) noexcept;
239 double Duration() const noexcept;
240 size_t ActionsInAllowedTime(double secondsAllowed) const noexcept;
241};
242
243/**
244 */
245class Document : PerLine, public Scintilla::IDocument, public Scintilla::ILoader {
246
247public:
248 /** Used to pair watcher pointer with user data. */
249 struct WatcherWithUserData {
250 DocWatcher *watcher;
251 void *userData;
252 WatcherWithUserData(DocWatcher *watcher_=nullptr, void *userData_=nullptr) noexcept :
253 watcher(watcher_), userData(userData_) {
254 }
255 bool operator==(const WatcherWithUserData &other) const noexcept {
256 return (watcher == other.watcher) && (userData == other.userData);
257 }
258 };
259
260private:
261 int refCount;
262 CellBuffer cb;
263 CharClassify charClass;
264 CharacterCategoryMap charMap;
265 std::unique_ptr<CaseFolder> pcf;
266 Sci::Position endStyled;
267 int styleClock;
268 int enteredModification;
269 int enteredStyling;
270 int enteredReadOnlyCount;
271
272 bool insertionSet;
273 std::string insertion;
274
275 std::vector<WatcherWithUserData> watchers;
276
277 // ldSize is not real data - it is for dimensions and loops
278 enum lineData { ldMarkers, ldLevels, ldState, ldMargin, ldAnnotation, ldEOLAnnotation, ldSize };
279 std::unique_ptr<PerLine> perLineData[ldSize];
280 LineMarkers *Markers() const noexcept;
281 LineLevels *Levels() const noexcept;
282 LineState *States() const noexcept;
283 LineAnnotation *Margins() const noexcept;
284 LineAnnotation *Annotations() const noexcept;
285 LineAnnotation *EOLAnnotations() const noexcept;
286
287 bool matchesValid;
288 std::unique_ptr<RegexSearchBase> regex;
289 std::unique_ptr<LexInterface> pli;
290
291public:
292
293 struct CharacterExtracted {
294 unsigned int character;
295 unsigned int widthBytes;
296 CharacterExtracted(unsigned int character_, unsigned int widthBytes_) noexcept :
297 character(character_), widthBytes(widthBytes_) {
298 }
299 // For DBCS characters turn 2 bytes into an int
300 static CharacterExtracted DBCS(unsigned char lead, unsigned char trail) noexcept {
301 return CharacterExtracted((lead << 8) | trail, 2);
302 }
303 };
304
305 Scintilla::EndOfLine eolMode;
306 /// Can also be SC_CP_UTF8 to enable UTF-8 mode
307 int dbcsCodePage;
308 Scintilla::LineEndType lineEndBitSet;
309 int tabInChars;
310 int indentInChars;
311 int actualIndentInChars;
312 bool useTabs;
313 bool tabIndents;
314 bool backspaceUnindents;
315 ActionDuration durationStyleOneByte;
316
317 std::unique_ptr<IDecorationList> decorations;
318
319 Document(Scintilla::DocumentOption options);
320 // Deleted so Document objects can not be copied.
321 Document(const Document &) = delete;
322 Document(Document &&) = delete;
323 void operator=(const Document &) = delete;
324 Document &operator=(Document &&) = delete;
325 ~Document() override;
326
327 int AddRef();
328 int SCI_METHOD Release() override;
329
330 // From PerLine
331 void Init() override;
332 void InsertLine(Sci::Line line) override;
333 void InsertLines(Sci::Line line, Sci::Line lines) override;
334 void RemoveLine(Sci::Line line) override;
335
336 Scintilla::LineEndType LineEndTypesSupported() const;
337 bool SetDBCSCodePage(int dbcsCodePage_);
338 Scintilla::LineEndType GetLineEndTypesAllowed() const noexcept { return cb.GetLineEndTypes(); }
339 bool SetLineEndTypesAllowed(Scintilla::LineEndType lineEndBitSet_);
340 Scintilla::LineEndType GetLineEndTypesActive() const noexcept { return cb.GetLineEndTypes(); }
341
342 int SCI_METHOD Version() const override {
343 return Scintilla::dvRelease4;
344 }
345
346 void SCI_METHOD SetErrorStatus(int status) override;
347
348 Sci_Position SCI_METHOD LineFromPosition(Sci_Position pos) const override;
349 Sci::Line SciLineFromPosition(Sci::Position pos) const noexcept; // Avoids casting LineFromPosition
350 Sci::Position ClampPositionIntoDocument(Sci::Position pos) const noexcept;
351 bool ContainsLineEnd(const char *s, Sci::Position length) const noexcept { return cb.ContainsLineEnd(s, length); }
352 bool IsCrLf(Sci::Position pos) const noexcept;
353 int LenChar(Sci::Position pos) const noexcept;
354 bool InGoodUTF8(Sci::Position pos, Sci::Position &start, Sci::Position &end) const noexcept;
355 Sci::Position MovePositionOutsideChar(Sci::Position pos, Sci::Position moveDir, bool checkLineEnd=true) const noexcept;
356 Sci::Position NextPosition(Sci::Position pos, int moveDir) const noexcept;
357 bool NextCharacter(Sci::Position &pos, int moveDir) const noexcept; // Returns true if pos changed
358 Document::CharacterExtracted CharacterAfter(Sci::Position position) const noexcept;
359 Document::CharacterExtracted CharacterBefore(Sci::Position position) const noexcept;
360 Sci_Position SCI_METHOD GetRelativePosition(Sci_Position positionStart, Sci_Position characterOffset) const override;
361 Sci::Position GetRelativePositionUTF16(Sci::Position positionStart, Sci::Position characterOffset) const noexcept;
362 int SCI_METHOD GetCharacterAndWidth(Sci_Position position, Sci_Position *pWidth) const override;
363 int SCI_METHOD CodePage() const override;
364 bool SCI_METHOD IsDBCSLeadByte(char ch) const override;
365 bool IsDBCSLeadByteNoExcept(char ch) const noexcept;
366 bool IsDBCSTrailByteNoExcept(char ch) const noexcept;
367 int DBCSDrawBytes(std::string_view text) const noexcept;
368 bool IsDBCSDualByteAt(Sci::Position pos) const noexcept;
369 size_t SafeSegment(std::string_view text) const noexcept;
370 EncodingFamily CodePageFamily() const noexcept;
371
372 // Gateways to modifying document
373 void ModifiedAt(Sci::Position pos) noexcept;
374 void CheckReadOnly();
375 bool DeleteChars(Sci::Position pos, Sci::Position len);
376 Sci::Position InsertString(Sci::Position position, const char *s, Sci::Position insertLength);
377 void ChangeInsertion(const char *s, Sci::Position length);
378 int SCI_METHOD AddData(const char *data, Sci_Position length) override;
379 void * SCI_METHOD ConvertToDocument() override;
380 Sci::Position Undo();
381 Sci::Position Redo();
382 bool CanUndo() const noexcept { return cb.CanUndo(); }
383 bool CanRedo() const noexcept { return cb.CanRedo(); }
384 void DeleteUndoHistory() { cb.DeleteUndoHistory(); }
385 bool SetUndoCollection(bool collectUndo) {
386 return cb.SetUndoCollection(collectUndo);
387 }
388 bool IsCollectingUndo() const noexcept { return cb.IsCollectingUndo(); }
389 void BeginUndoAction() { cb.BeginUndoAction(); }
390 void EndUndoAction() { cb.EndUndoAction(); }
391 void AddUndoAction(Sci::Position token, bool mayCoalesce) { cb.AddUndoAction(token, mayCoalesce); }
392 void SetSavePoint();
393 bool IsSavePoint() const noexcept { return cb.IsSavePoint(); }
394
395 void TentativeStart() { cb.TentativeStart(); }
396 void TentativeCommit() { cb.TentativeCommit(); }
397 void TentativeUndo();
398 bool TentativeActive() const noexcept { return cb.TentativeActive(); }
399
400 const char * SCI_METHOD BufferPointer() override { return cb.BufferPointer(); }
401 const char *RangePointer(Sci::Position position, Sci::Position rangeLength) noexcept { return cb.RangePointer(position, rangeLength); }
402 Sci::Position GapPosition() const noexcept { return cb.GapPosition(); }
403
404 int SCI_METHOD GetLineIndentation(Sci_Position line) override;
405 Sci::Position SetLineIndentation(Sci::Line line, Sci::Position indent);
406 Sci::Position GetLineIndentPosition(Sci::Line line) const;
407 Sci::Position GetColumn(Sci::Position pos);
408 Sci::Position CountCharacters(Sci::Position startPos, Sci::Position endPos) const noexcept;
409 Sci::Position CountUTF16(Sci::Position startPos, Sci::Position endPos) const noexcept;
410 Sci::Position FindColumn(Sci::Line line, Sci::Position column);
411 void Indent(bool forwards, Sci::Line lineBottom, Sci::Line lineTop);
412 static std::string TransformLineEnds(const char *s, size_t len, Scintilla::EndOfLine eolModeWanted);
413 void ConvertLineEnds(Scintilla::EndOfLine eolModeSet);
414 void SetReadOnly(bool set) { cb.SetReadOnly(set); }
415 bool IsReadOnly() const noexcept { return cb.IsReadOnly(); }
416 bool IsLarge() const noexcept { return cb.IsLarge(); }
417 Scintilla::DocumentOption Options() const noexcept;
418
419 void DelChar(Sci::Position pos);
420 void DelCharBack(Sci::Position pos);
421
422 char CharAt(Sci::Position position) const noexcept { return cb.CharAt(position); }
423 void SCI_METHOD GetCharRange(char *buffer, Sci_Position position, Sci_Position lengthRetrieve) const override {
424 cb.GetCharRange(buffer, position, lengthRetrieve);
425 }
426 char SCI_METHOD StyleAt(Sci_Position position) const override { return cb.StyleAt(position); }
427 int StyleIndexAt(Sci_Position position) const noexcept { return static_cast<unsigned char>(cb.StyleAt(position)); }
428 void GetStyleRange(unsigned char *buffer, Sci::Position position, Sci::Position lengthRetrieve) const {
429 cb.GetStyleRange(buffer, position, lengthRetrieve);
430 }
431 int GetMark(Sci::Line line) const noexcept;
432 Sci::Line MarkerNext(Sci::Line lineStart, int mask) const noexcept;
433 int AddMark(Sci::Line line, int markerNum);
434 void AddMarkSet(Sci::Line line, int valueSet);
435 void DeleteMark(Sci::Line line, int markerNum);
436 void DeleteMarkFromHandle(int markerHandle);
437 void DeleteAllMarks(int markerNum);
438 Sci::Line LineFromHandle(int markerHandle) const noexcept;
439 int MarkerNumberFromLine(Sci::Line line, int which) const noexcept;
440 int MarkerHandleFromLine(Sci::Line line, int which) const noexcept;
441 Sci_Position SCI_METHOD LineStart(Sci_Position line) const override;
442 bool IsLineStartPosition(Sci::Position position) const;
443 Sci_Position SCI_METHOD LineEnd(Sci_Position line) const override;
444 Sci::Position LineEndPosition(Sci::Position position) const;
445 bool IsLineEndPosition(Sci::Position position) const;
446 bool IsPositionInLineEnd(Sci::Position position) const;
447 Sci::Position VCHomePosition(Sci::Position position) const;
448 Sci::Position IndexLineStart(Sci::Line line, Scintilla::LineCharacterIndexType lineCharacterIndex) const noexcept;
449 Sci::Line LineFromPositionIndex(Sci::Position pos, Scintilla::LineCharacterIndexType lineCharacterIndex) const noexcept;
450 Sci::Line LineFromPositionAfter(Sci::Line line, Sci::Position length) const noexcept;
451
452 int SCI_METHOD SetLevel(Sci_Position line, int level) override;
453 int SCI_METHOD GetLevel(Sci_Position line) const override;
454 Scintilla::FoldLevel GetFoldLevel(Sci_Position line) const;
455 void ClearLevels();
456 Sci::Line GetLastChild(Sci::Line lineParent, std::optional<Scintilla::FoldLevel> level = {}, Sci::Line lastLine = -1);
457 Sci::Line GetFoldParent(Sci::Line line) const;
458 void GetHighlightDelimiters(HighlightDelimiter &highlightDelimiter, Sci::Line line, Sci::Line lastLine);
459
460 Sci::Position ExtendWordSelect(Sci::Position pos, int delta, bool onlyWordCharacters=false) const;
461 Sci::Position NextWordStart(Sci::Position pos, int delta) const;
462 Sci::Position NextWordEnd(Sci::Position pos, int delta) const;
463 Sci_Position SCI_METHOD Length() const override { return cb.Length(); }
464 Sci::Position LengthNoExcept() const noexcept { return cb.Length(); }
465 void Allocate(Sci::Position newSize) { cb.Allocate(newSize); }
466
467 CharacterExtracted ExtractCharacter(Sci::Position position) const noexcept;
468
469 bool IsWordStartAt(Sci::Position pos) const;
470 bool IsWordEndAt(Sci::Position pos) const;
471 bool IsWordAt(Sci::Position start, Sci::Position end) const;
472
473 bool MatchesWordOptions(bool word, bool wordStart, Sci::Position pos, Sci::Position length) const;
474 bool HasCaseFolder() const noexcept;
475 void SetCaseFolder(std::unique_ptr<CaseFolder> pcf_) noexcept;
476 Sci::Position FindText(Sci::Position minPos, Sci::Position maxPos, const char *search, Scintilla::FindOption flags, Sci::Position *length);
477 const char *SubstituteByPosition(const char *text, Sci::Position *length);
478 Scintilla::LineCharacterIndexType LineCharacterIndex() const noexcept;
479 void AllocateLineCharacterIndex(Scintilla::LineCharacterIndexType lineCharacterIndex);
480 void ReleaseLineCharacterIndex(Scintilla::LineCharacterIndexType lineCharacterIndex);
481 Sci::Line LinesTotal() const noexcept;
482 void AllocateLines(Sci::Line lines);
483
484 void SetDefaultCharClasses(bool includeWordClass);
485 void SetCharClasses(const unsigned char *chars, CharacterClass newCharClass);
486 int GetCharsOfClass(CharacterClass characterClass, unsigned char *buffer) const;
487 void SetCharacterCategoryOptimization(int countCharacters);
488 int CharacterCategoryOptimization() const noexcept;
489 void SCI_METHOD StartStyling(Sci_Position position) override;
490 bool SCI_METHOD SetStyleFor(Sci_Position length, char style) override;
491 bool SCI_METHOD SetStyles(Sci_Position length, const char *styles) override;
492 Sci::Position GetEndStyled() const noexcept { return endStyled; }
493 void EnsureStyledTo(Sci::Position pos);
494 void StyleToAdjustingLineDuration(Sci::Position pos);
495 void LexerChanged();
496 int GetStyleClock() const noexcept { return styleClock; }
497 void IncrementStyleClock() noexcept;
498 void SCI_METHOD DecorationSetCurrentIndicator(int indicator) override;
499 void SCI_METHOD DecorationFillRange(Sci_Position position, int value, Sci_Position fillLength) override;
500 LexInterface *GetLexInterface() const noexcept;
501 void SetLexInterface(std::unique_ptr<LexInterface> pLexInterface) noexcept;
502
503 int SCI_METHOD SetLineState(Sci_Position line, int state) override;
504 int SCI_METHOD GetLineState(Sci_Position line) const override;
505 Sci::Line GetMaxLineState() const noexcept;
506 void SCI_METHOD ChangeLexerState(Sci_Position start, Sci_Position end) override;
507
508 StyledText MarginStyledText(Sci::Line line) const noexcept;
509 void MarginSetStyle(Sci::Line line, int style);
510 void MarginSetStyles(Sci::Line line, const unsigned char *styles);
511 void MarginSetText(Sci::Line line, const char *text);
512 void MarginClearAll();
513
514 StyledText AnnotationStyledText(Sci::Line line) const noexcept;
515 void AnnotationSetText(Sci::Line line, const char *text);
516 void AnnotationSetStyle(Sci::Line line, int style);
517 void AnnotationSetStyles(Sci::Line line, const unsigned char *styles);
518 int AnnotationLines(Sci::Line line) const noexcept;
519 void AnnotationClearAll();
520
521 StyledText EOLAnnotationStyledText(Sci::Line line) const noexcept;
522 void EOLAnnotationSetStyle(Sci::Line line, int style);
523 void EOLAnnotationSetText(Sci::Line line, const char *text);
524 void EOLAnnotationClearAll();
525
526 bool AddWatcher(DocWatcher *watcher, void *userData);
527 bool RemoveWatcher(DocWatcher *watcher, void *userData) noexcept;
528
529 CharacterClass WordCharacterClass(unsigned int ch) const;
530 bool IsWordPartSeparator(unsigned int ch) const;
531 Sci::Position WordPartLeft(Sci::Position pos) const;
532 Sci::Position WordPartRight(Sci::Position pos) const;
533 Sci::Position ExtendStyleRange(Sci::Position pos, int delta, bool singleLine) noexcept;
534 bool IsWhiteLine(Sci::Line line) const;
535 Sci::Position ParaUp(Sci::Position pos) const;
536 Sci::Position ParaDown(Sci::Position pos) const;
537 int IndentSize() const noexcept { return actualIndentInChars; }
538 Sci::Position BraceMatch(Sci::Position position, Sci::Position maxReStyle, Sci::Position startPos, bool useStartPos) noexcept;
539
540private:
541 void NotifyModifyAttempt();
542 void NotifySavePoint(bool atSavePoint);
543 void NotifyModified(DocModification mh);
544};
545
546class UndoGroup {
547 Document *pdoc;
548 bool groupNeeded;
549public:
550 UndoGroup(Document *pdoc_, bool groupNeeded_=true) :
551 pdoc(pdoc_), groupNeeded(groupNeeded_) {
552 if (groupNeeded) {
553 pdoc->BeginUndoAction();
554 }
555 }
556 // Deleted so UndoGroup objects can not be copied.
557 UndoGroup(const UndoGroup &) = delete;
558 UndoGroup(UndoGroup &&) = delete;
559 void operator=(const UndoGroup &) = delete;
560 UndoGroup &operator=(UndoGroup &&) = delete;
561 ~UndoGroup() {
562 if (groupNeeded) {
563 // EndUndoAction can throw as it allocates but throw in destructor is fatal.
564 // To fix this UndoHistory should allocate any memory needed by EndUndoAction
565 // beforehand or change EndUndoAction to not require allocation.
566 pdoc->EndUndoAction();
567 }
568 }
569 bool Needed() const noexcept {
570 return groupNeeded;
571 }
572};
573
574
575/**
576 * To optimise processing of document modifications by DocWatchers, a hint is passed indicating the
577 * scope of the change.
578 * If the DocWatcher is a document view then this can be used to optimise screen updating.
579 */
580class DocModification {
581public:
582 Scintilla::ModificationFlags modificationType;
583 Sci::Position position;
584 Sci::Position length;
585 Sci::Line linesAdded; /**< Negative if lines deleted. */
586 const char *text; /**< Only valid for changes to text, not for changes to style. */
587 Sci::Line line;
588 Scintilla::FoldLevel foldLevelNow;
589 Scintilla::FoldLevel foldLevelPrev;
590 Sci::Line annotationLinesAdded;
591 Sci::Position token;
592
593 DocModification(Scintilla::ModificationFlags modificationType_, Sci::Position position_=0, Sci::Position length_=0,
594 Sci::Line linesAdded_=0, const char *text_=nullptr, Sci::Line line_=0) noexcept :
595 modificationType(modificationType_),
596 position(position_),
597 length(length_),
598 linesAdded(linesAdded_),
599 text(text_),
600 line(line_),
601 foldLevelNow(Scintilla::FoldLevel::None),
602 foldLevelPrev(Scintilla::FoldLevel::None),
603 annotationLinesAdded(0),
604 token(0) {}
605
606 DocModification(Scintilla::ModificationFlags modificationType_, const Action &act, Sci::Line linesAdded_=0) noexcept :
607 modificationType(modificationType_),
608 position(act.position),
609 length(act.lenData),
610 linesAdded(linesAdded_),
611 text(act.data.get()),
612 line(0),
613 foldLevelNow(Scintilla::FoldLevel::None),
614 foldLevelPrev(Scintilla::FoldLevel::None),
615 annotationLinesAdded(0),
616 token(0) {}
617};
618
619/**
620 * A class that wants to receive notifications from a Document must be derived from DocWatcher
621 * and implement the notification methods. It can then be added to the watcher list with AddWatcher.
622 */
623class DocWatcher {
624public:
625 virtual ~DocWatcher() {}
626
627 virtual void NotifyModifyAttempt(Document *doc, void *userData) = 0;
628 virtual void NotifySavePoint(Document *doc, void *userData, bool atSavePoint) = 0;
629 virtual void NotifyModified(Document *doc, DocModification mh, void *userData) = 0;
630 virtual void NotifyDeleted(Document *doc, void *userData) noexcept = 0;
631 virtual void NotifyStyleNeeded(Document *doc, void *userData, Sci::Position endPos) = 0;
632 virtual void NotifyLexerChanged(Document *doc, void *userData) = 0;
633 virtual void NotifyErrorOccurred(Document *doc, void *userData, Scintilla::Status status) = 0;
634};
635
636}
637
638#endif
639