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 | |
25 | namespace Scintilla::Internal { |
26 | |
27 | class DocWatcher; |
28 | class DocModification; |
29 | class Document; |
30 | class LineMarkers; |
31 | class LineLevels; |
32 | class LineState; |
33 | class LineAnnotation; |
34 | |
35 | enum 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 | */ |
43 | class Range { |
44 | public: |
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 | */ |
105 | class RegexSearchBase { |
106 | public: |
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 |
117 | extern RegexSearchBase *CreateRegexSearch(CharClassify *charClassTable); |
118 | |
119 | struct 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 | |
141 | class HighlightDelimiter { |
142 | public: |
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 | |
181 | struct 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 | |
194 | using 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. |
199 | class LexInterface { |
200 | protected: |
201 | Document *pdoc; |
202 | LexerInstance instance; |
203 | bool performingStyle; ///< Prevent reentrance |
204 | public: |
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 | |
218 | struct 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 | |
232 | class ActionDuration { |
233 | double duration; |
234 | const double minDuration; |
235 | const double maxDuration; |
236 | public: |
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 | */ |
245 | class Document : PerLine, public Scintilla::IDocument, public Scintilla::ILoader { |
246 | |
247 | public: |
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 | |
260 | private: |
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 | |
291 | public: |
292 | |
293 | struct { |
294 | unsigned int ; |
295 | unsigned int ; |
296 | (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 (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 (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 | |
540 | private: |
541 | void NotifyModifyAttempt(); |
542 | void NotifySavePoint(bool atSavePoint); |
543 | void NotifyModified(DocModification mh); |
544 | }; |
545 | |
546 | class UndoGroup { |
547 | Document *pdoc; |
548 | bool groupNeeded; |
549 | public: |
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 | */ |
580 | class DocModification { |
581 | public: |
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 | */ |
623 | class DocWatcher { |
624 | public: |
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 | |