| 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 | |