]>
git.saurik.com Git - wxWidgets.git/blob - src/stc/scintilla/src/Document.cxx
a5d1adbbb14b61701a77064e936f7d6ac7d76c35
1 // Scintilla source code edit control
3 ** Text document that handles notifications, DBCS, styling, words and end of line.
5 // Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
15 #include "Scintilla.h"
16 #include "SplitVector.h"
17 #include "Partitioning.h"
18 #include "RunStyles.h"
19 #include "CellBuffer.h"
21 #include "CharClassify.h"
22 #include "Decoration.h"
27 using namespace Scintilla
;
30 // This is ASCII specific but is safe with chars >= 0x80
31 static inline bool isspacechar(unsigned char ch
) {
32 return (ch
== ' ') || ((ch
>= 0x09) && (ch
<= 0x0d));
35 static inline bool IsPunctuation(char ch
) {
36 return isascii(ch
) && ispunct(ch
);
39 static inline bool IsADigit(char ch
) {
40 return isascii(ch
) && isdigit(ch
);
43 static inline bool IsLowerCase(char ch
) {
44 return isascii(ch
) && islower(ch
);
47 static inline bool IsUpperCase(char ch
) {
48 return isascii(ch
) && isupper(ch
);
51 Document::Document() {
56 eolMode
= SC_EOL_CRLF
;
60 stylingBitsMask
= 0x1F;
64 enteredModification
= 0;
66 enteredReadOnlyCount
= 0;
69 actualIndentInChars
= 8;
72 backspaceUnindents
= false;
79 perLineData
[ldMarkers
] = new LineMarkers();
80 perLineData
[ldLevels
] = new LineLevels();
81 perLineData
[ldState
] = new LineState();
82 perLineData
[ldMargin
] = new LineAnnotation();
83 perLineData
[ldAnnotation
] = new LineAnnotation();
88 Document::~Document() {
89 for (int i
= 0; i
< lenWatchers
; i
++) {
90 watchers
[i
].watcher
->NotifyDeleted(this, watchers
[i
].userData
);
93 for (int j
=0; j
<ldSize
; j
++) {
94 delete perLineData
[j
];
103 void Document::Init() {
104 for (int j
=0; j
<ldSize
; j
++) {
106 perLineData
[j
]->Init();
110 void Document::InsertLine(int line
) {
111 for (int j
=0; j
<ldSize
; j
++) {
113 perLineData
[j
]->InsertLine(line
);
117 void Document::RemoveLine(int line
) {
118 for (int j
=0; j
<ldSize
; j
++) {
120 perLineData
[j
]->RemoveLine(line
);
124 // Increase reference count and return its previous value.
125 int Document::AddRef() {
129 // Decrease reference count and return its previous value.
130 // Delete the document if reference count reaches zero.
131 int Document::Release() {
132 int curRefCount
= --refCount
;
133 if (curRefCount
== 0)
138 void Document::SetSavePoint() {
140 NotifySavePoint(true);
143 int Document::GetMark(int line
) {
144 return static_cast<LineMarkers
*>(perLineData
[ldMarkers
])->MarkValue(line
);
147 int Document::AddMark(int line
, int markerNum
) {
148 if (line
<= LinesTotal()) {
149 int prev
= static_cast<LineMarkers
*>(perLineData
[ldMarkers
])->
150 AddMark(line
, markerNum
, LinesTotal());
151 DocModification
mh(SC_MOD_CHANGEMARKER
, LineStart(line
), 0, 0, 0, line
);
159 void Document::AddMarkSet(int line
, int valueSet
) {
160 unsigned int m
= valueSet
;
161 for (int i
= 0; m
; i
++, m
>>= 1)
163 static_cast<LineMarkers
*>(perLineData
[ldMarkers
])->
164 AddMark(line
, i
, LinesTotal());
165 DocModification
mh(SC_MOD_CHANGEMARKER
, LineStart(line
), 0, 0, 0, line
);
169 void Document::DeleteMark(int line
, int markerNum
) {
170 static_cast<LineMarkers
*>(perLineData
[ldMarkers
])->DeleteMark(line
, markerNum
, false);
171 DocModification
mh(SC_MOD_CHANGEMARKER
, LineStart(line
), 0, 0, 0, line
);
175 void Document::DeleteMarkFromHandle(int markerHandle
) {
176 static_cast<LineMarkers
*>(perLineData
[ldMarkers
])->DeleteMarkFromHandle(markerHandle
);
177 DocModification
mh(SC_MOD_CHANGEMARKER
, 0, 0, 0, 0);
182 void Document::DeleteAllMarks(int markerNum
) {
183 for (int line
= 0; line
< LinesTotal(); line
++) {
184 static_cast<LineMarkers
*>(perLineData
[ldMarkers
])->DeleteMark(line
, markerNum
, true);
186 DocModification
mh(SC_MOD_CHANGEMARKER
, 0, 0, 0, 0);
191 int Document::LineFromHandle(int markerHandle
) {
192 return static_cast<LineMarkers
*>(perLineData
[ldMarkers
])->LineFromHandle(markerHandle
);
195 int Document::LineStart(int line
) const {
196 return cb
.LineStart(line
);
199 int Document::LineEnd(int line
) const {
200 if (line
== LinesTotal() - 1) {
201 return LineStart(line
+ 1);
203 int position
= LineStart(line
+ 1) - 1;
204 // When line terminator is CR+LF, may need to go back one more
205 if ((position
> LineStart(line
)) && (cb
.CharAt(position
- 1) == '\r')) {
212 int Document::LineFromPosition(int pos
) const {
213 return cb
.LineFromPosition(pos
);
216 int Document::LineEndPosition(int position
) const {
217 return LineEnd(LineFromPosition(position
));
220 bool Document::IsLineEndPosition(int position
) const {
221 return LineEnd(LineFromPosition(position
)) == position
;
224 int Document::VCHomePosition(int position
) const {
225 int line
= LineFromPosition(position
);
226 int startPosition
= LineStart(line
);
227 int endLine
= LineEnd(line
);
228 int startText
= startPosition
;
229 while (startText
< endLine
&& (cb
.CharAt(startText
) == ' ' || cb
.CharAt(startText
) == '\t' ) )
231 if (position
== startText
)
232 return startPosition
;
237 int Document::SetLevel(int line
, int level
) {
238 int prev
= static_cast<LineLevels
*>(perLineData
[ldLevels
])->SetLevel(line
, level
, LinesTotal());
240 DocModification
mh(SC_MOD_CHANGEFOLD
| SC_MOD_CHANGEMARKER
,
241 LineStart(line
), 0, 0, 0, line
);
242 mh
.foldLevelNow
= level
;
243 mh
.foldLevelPrev
= prev
;
249 int Document::GetLevel(int line
) {
250 return static_cast<LineLevels
*>(perLineData
[ldLevels
])->GetLevel(line
);
253 void Document::ClearLevels() {
254 static_cast<LineLevels
*>(perLineData
[ldLevels
])->ClearLevels();
257 static bool IsSubordinate(int levelStart
, int levelTry
) {
258 if (levelTry
& SC_FOLDLEVELWHITEFLAG
)
261 return (levelStart
& SC_FOLDLEVELNUMBERMASK
) < (levelTry
& SC_FOLDLEVELNUMBERMASK
);
264 int Document::GetLastChild(int lineParent
, int level
) {
266 level
= GetLevel(lineParent
) & SC_FOLDLEVELNUMBERMASK
;
267 int maxLine
= LinesTotal();
268 int lineMaxSubord
= lineParent
;
269 while (lineMaxSubord
< maxLine
- 1) {
270 EnsureStyledTo(LineStart(lineMaxSubord
+ 2));
271 if (!IsSubordinate(level
, GetLevel(lineMaxSubord
+ 1)))
275 if (lineMaxSubord
> lineParent
) {
276 if (level
> (GetLevel(lineMaxSubord
+ 1) & SC_FOLDLEVELNUMBERMASK
)) {
277 // Have chewed up some whitespace that belongs to a parent so seek back
278 if (GetLevel(lineMaxSubord
) & SC_FOLDLEVELWHITEFLAG
) {
283 return lineMaxSubord
;
286 int Document::GetFoldParent(int line
) {
287 int level
= GetLevel(line
) & SC_FOLDLEVELNUMBERMASK
;
288 int lineLook
= line
- 1;
289 while ((lineLook
> 0) && (
290 (!(GetLevel(lineLook
) & SC_FOLDLEVELHEADERFLAG
)) ||
291 ((GetLevel(lineLook
) & SC_FOLDLEVELNUMBERMASK
) >= level
))
295 if ((GetLevel(lineLook
) & SC_FOLDLEVELHEADERFLAG
) &&
296 ((GetLevel(lineLook
) & SC_FOLDLEVELNUMBERMASK
) < level
)) {
303 int Document::ClampPositionIntoDocument(int pos
) {
304 return Platform::Clamp(pos
, 0, Length());
307 bool Document::IsCrLf(int pos
) {
310 if (pos
>= (Length() - 1))
312 return (cb
.CharAt(pos
) == '\r') && (cb
.CharAt(pos
+ 1) == '\n');
315 static const int maxBytesInDBCSCharacter
=5;
317 int Document::LenChar(int pos
) {
320 } else if (IsCrLf(pos
)) {
322 } else if (SC_CP_UTF8
== dbcsCodePage
) {
323 unsigned char ch
= static_cast<unsigned char>(cb
.CharAt(pos
));
327 if (ch
>= (0x80 + 0x40 + 0x20 + 0x10))
329 else if (ch
>= (0x80 + 0x40 + 0x20))
331 int lengthDoc
= Length();
332 if ((pos
+ len
) > lengthDoc
)
333 return lengthDoc
-pos
;
336 } else if (dbcsCodePage
) {
337 char mbstr
[maxBytesInDBCSCharacter
+1];
339 for (i
=0; i
<Platform::DBCSCharMaxLength(); i
++) {
340 mbstr
[i
] = cb
.CharAt(pos
+i
);
343 return Platform::DBCSCharLength(dbcsCodePage
, mbstr
);
349 static bool IsTrailByte(int ch
) {
350 return (ch
>= 0x80) && (ch
< (0x80 + 0x40));
353 static int BytesFromLead(int leadByte
) {
354 if (leadByte
> 0xF4) {
355 // Characters longer than 4 bytes not possible in current UTF-8
357 } else if (leadByte
>= 0xF0) {
359 } else if (leadByte
>= 0xE0) {
361 } else if (leadByte
>= 0xC2) {
367 bool Document::InGoodUTF8(int pos
, int &start
, int &end
) {
369 while ((lead
>0) && (pos
-lead
< 4) && IsTrailByte(static_cast<unsigned char>(cb
.CharAt(lead
-1))))
375 int leadByte
= static_cast<unsigned char>(cb
.CharAt(start
));
376 int bytes
= BytesFromLead(leadByte
);
380 int trailBytes
= bytes
- 1;
381 int len
= pos
- lead
+ 1;
382 if (len
> trailBytes
)
383 // pos too far from lead
385 // Check that there are enough trails for this lead
387 while ((trail
-lead
<trailBytes
) && (trail
< Length())) {
388 if (!IsTrailByte(static_cast<unsigned char>(cb
.CharAt(trail
)))) {
398 // Normalise a position so that it is not halfway through a two byte character.
399 // This can occur in two situations -
400 // When lines are terminated with \r\n pairs which should be treated as one character.
401 // When displaying DBCS text such as Japanese.
402 // If moving, move the position in the indicated direction.
403 int Document::MovePositionOutsideChar(int pos
, int moveDir
, bool checkLineEnd
) {
404 //Platform::DebugPrintf("NoCRLF %d %d\n", pos, moveDir);
405 // If out of range, just return minimum/maximum value.
411 // PLATFORM_ASSERT(pos > 0 && pos < Length());
412 if (checkLineEnd
&& IsCrLf(pos
- 1)) {
419 // Not between CR and LF
422 if (SC_CP_UTF8
== dbcsCodePage
) {
423 unsigned char ch
= static_cast<unsigned char>(cb
.CharAt(pos
));
426 if (IsTrailByte(ch
) && InGoodUTF8(pos
, startUTF
, endUTF
)) {
427 // ch is a trail byte within a UTF-8 character
434 // Anchor DBCS calculations at start of line because start of line can
435 // not be a DBCS trail byte.
436 int posCheck
= LineStart(LineFromPosition(pos
));
437 while (posCheck
< pos
) {
438 char mbstr
[maxBytesInDBCSCharacter
+1];
440 for(i
=0;i
<Platform::DBCSCharMaxLength();i
++) {
441 mbstr
[i
] = cb
.CharAt(posCheck
+i
);
445 int mbsize
= Platform::DBCSCharLength(dbcsCodePage
, mbstr
);
446 if (posCheck
+ mbsize
== pos
) {
448 } else if (posCheck
+ mbsize
> pos
) {
450 return posCheck
+ mbsize
;
463 void Document::ModifiedAt(int pos
) {
468 void Document::CheckReadOnly() {
469 if (cb
.IsReadOnly() && enteredReadOnlyCount
== 0) {
470 enteredReadOnlyCount
++;
471 NotifyModifyAttempt();
472 enteredReadOnlyCount
--;
476 // Document only modified by gateways DeleteChars, InsertString, Undo, Redo, and SetStyleAt.
477 // SetStyleAt does not change the persistent state of a document
479 bool Document::DeleteChars(int pos
, int len
) {
482 if ((pos
+ len
) > Length())
485 if (enteredModification
!= 0) {
488 enteredModification
++;
489 if (!cb
.IsReadOnly()) {
492 SC_MOD_BEFOREDELETE
| SC_PERFORMED_USER
,
495 int prevLinesTotal
= LinesTotal();
496 bool startSavePoint
= cb
.IsSavePoint();
497 bool startSequence
= false;
498 const char *text
= cb
.DeleteChars(pos
, len
, startSequence
);
499 if (startSavePoint
&& cb
.IsCollectingUndo())
500 NotifySavePoint(!startSavePoint
);
501 if ((pos
< Length()) || (pos
== 0))
507 SC_MOD_DELETETEXT
| SC_PERFORMED_USER
| (startSequence
?SC_STARTACTION
:0),
509 LinesTotal() - prevLinesTotal
, text
));
511 enteredModification
--;
513 return !cb
.IsReadOnly();
517 * Insert a string with a length.
519 bool Document::InsertString(int position
, const char *s
, int insertLength
) {
520 if (insertLength
<= 0) {
524 if (enteredModification
!= 0) {
527 enteredModification
++;
528 if (!cb
.IsReadOnly()) {
531 SC_MOD_BEFOREINSERT
| SC_PERFORMED_USER
,
532 position
, insertLength
,
534 int prevLinesTotal
= LinesTotal();
535 bool startSavePoint
= cb
.IsSavePoint();
536 bool startSequence
= false;
537 const char *text
= cb
.InsertString(position
, s
, insertLength
, startSequence
);
538 if (startSavePoint
&& cb
.IsCollectingUndo())
539 NotifySavePoint(!startSavePoint
);
540 ModifiedAt(position
);
543 SC_MOD_INSERTTEXT
| SC_PERFORMED_USER
| (startSequence
?SC_STARTACTION
:0),
544 position
, insertLength
,
545 LinesTotal() - prevLinesTotal
, text
));
547 enteredModification
--;
549 return !cb
.IsReadOnly();
552 int Document::Undo() {
555 if (enteredModification
== 0) {
556 enteredModification
++;
557 if (!cb
.IsReadOnly()) {
558 bool startSavePoint
= cb
.IsSavePoint();
559 bool multiLine
= false;
560 int steps
= cb
.StartUndo();
561 //Platform::DebugPrintf("Steps=%d\n", steps);
562 for (int step
= 0; step
< steps
; step
++) {
563 const int prevLinesTotal
= LinesTotal();
564 const Action
&action
= cb
.GetUndoStep();
565 if (action
.at
== removeAction
) {
566 NotifyModified(DocModification(
567 SC_MOD_BEFOREINSERT
| SC_PERFORMED_UNDO
, action
));
568 } else if (action
.at
== containerAction
) {
569 DocModification
dm(SC_MOD_CONTAINER
| SC_PERFORMED_UNDO
);
570 dm
.token
= action
.position
;
573 NotifyModified(DocModification(
574 SC_MOD_BEFOREDELETE
| SC_PERFORMED_UNDO
, action
));
576 cb
.PerformUndoStep();
577 int cellPosition
= action
.position
;
578 if (action
.at
!= containerAction
) {
579 ModifiedAt(cellPosition
);
580 newPos
= cellPosition
;
583 int modFlags
= SC_PERFORMED_UNDO
;
584 // With undo, an insertion action becomes a deletion notification
585 if (action
.at
== removeAction
) {
586 newPos
+= action
.lenData
;
587 modFlags
|= SC_MOD_INSERTTEXT
;
588 } else if (action
.at
== insertAction
) {
589 modFlags
|= SC_MOD_DELETETEXT
;
592 modFlags
|= SC_MULTISTEPUNDOREDO
;
593 const int linesAdded
= LinesTotal() - prevLinesTotal
;
596 if (step
== steps
- 1) {
597 modFlags
|= SC_LASTSTEPINUNDOREDO
;
599 modFlags
|= SC_MULTILINEUNDOREDO
;
601 NotifyModified(DocModification(modFlags
, cellPosition
, action
.lenData
,
602 linesAdded
, action
.data
));
605 bool endSavePoint
= cb
.IsSavePoint();
606 if (startSavePoint
!= endSavePoint
)
607 NotifySavePoint(endSavePoint
);
609 enteredModification
--;
614 int Document::Redo() {
617 if (enteredModification
== 0) {
618 enteredModification
++;
619 if (!cb
.IsReadOnly()) {
620 bool startSavePoint
= cb
.IsSavePoint();
621 bool multiLine
= false;
622 int steps
= cb
.StartRedo();
623 for (int step
= 0; step
< steps
; step
++) {
624 const int prevLinesTotal
= LinesTotal();
625 const Action
&action
= cb
.GetRedoStep();
626 if (action
.at
== insertAction
) {
627 NotifyModified(DocModification(
628 SC_MOD_BEFOREINSERT
| SC_PERFORMED_REDO
, action
));
629 } else if (action
.at
== containerAction
) {
630 DocModification
dm(SC_MOD_CONTAINER
| SC_PERFORMED_REDO
);
631 dm
.token
= action
.position
;
634 NotifyModified(DocModification(
635 SC_MOD_BEFOREDELETE
| SC_PERFORMED_REDO
, action
));
637 cb
.PerformRedoStep();
638 if (action
.at
!= containerAction
) {
639 ModifiedAt(action
.position
);
640 newPos
= action
.position
;
643 int modFlags
= SC_PERFORMED_REDO
;
644 if (action
.at
== insertAction
) {
645 newPos
+= action
.lenData
;
646 modFlags
|= SC_MOD_INSERTTEXT
;
647 } else if (action
.at
== removeAction
) {
648 modFlags
|= SC_MOD_DELETETEXT
;
651 modFlags
|= SC_MULTISTEPUNDOREDO
;
652 const int linesAdded
= LinesTotal() - prevLinesTotal
;
655 if (step
== steps
- 1) {
656 modFlags
|= SC_LASTSTEPINUNDOREDO
;
658 modFlags
|= SC_MULTILINEUNDOREDO
;
661 DocModification(modFlags
, action
.position
, action
.lenData
,
662 linesAdded
, action
.data
));
665 bool endSavePoint
= cb
.IsSavePoint();
666 if (startSavePoint
!= endSavePoint
)
667 NotifySavePoint(endSavePoint
);
669 enteredModification
--;
675 * Insert a single character.
677 bool Document::InsertChar(int pos
, char ch
) {
680 return InsertString(pos
, chs
, 1);
684 * Insert a null terminated string.
686 bool Document::InsertCString(int position
, const char *s
) {
687 return InsertString(position
, s
, strlen(s
));
690 void Document::ChangeChar(int pos
, char ch
) {
695 void Document::DelChar(int pos
) {
696 DeleteChars(pos
, LenChar(pos
));
699 void Document::DelCharBack(int pos
) {
702 } else if (IsCrLf(pos
- 2)) {
703 DeleteChars(pos
- 2, 2);
704 } else if (dbcsCodePage
) {
705 int startChar
= MovePositionOutsideChar(pos
- 1, -1, false);
706 DeleteChars(startChar
, pos
- startChar
);
708 DeleteChars(pos
- 1, 1);
712 static bool isindentchar(char ch
) {
713 return (ch
== ' ') || (ch
== '\t');
716 static int NextTab(int pos
, int tabSize
) {
717 return ((pos
/ tabSize
) + 1) * tabSize
;
720 static void CreateIndentation(char *linebuf
, int length
, int indent
, int tabSize
, bool insertSpaces
) {
721 length
--; // ensure space for \0
723 while ((indent
>= tabSize
) && (length
> 0)) {
729 while ((indent
> 0) && (length
> 0)) {
737 int Document::GetLineIndentation(int line
) {
739 if ((line
>= 0) && (line
< LinesTotal())) {
740 int lineStart
= LineStart(line
);
741 int length
= Length();
742 for (int i
= lineStart
;i
< length
;i
++) {
743 char ch
= cb
.CharAt(i
);
747 indent
= NextTab(indent
, tabInChars
);
755 void Document::SetLineIndentation(int line
, int indent
) {
756 int indentOfLine
= GetLineIndentation(line
);
759 if (indent
!= indentOfLine
) {
761 CreateIndentation(linebuf
, sizeof(linebuf
), indent
, tabInChars
, !useTabs
);
762 int thisLineStart
= LineStart(line
);
763 int indentPos
= GetLineIndentPosition(line
);
765 DeleteChars(thisLineStart
, indentPos
- thisLineStart
);
766 InsertCString(thisLineStart
, linebuf
);
770 int Document::GetLineIndentPosition(int line
) const {
773 int pos
= LineStart(line
);
774 int length
= Length();
775 while ((pos
< length
) && isindentchar(cb
.CharAt(pos
))) {
781 int Document::GetColumn(int pos
) {
783 int line
= LineFromPosition(pos
);
784 if ((line
>= 0) && (line
< LinesTotal())) {
785 for (int i
= LineStart(line
);i
< pos
;) {
786 char ch
= cb
.CharAt(i
);
788 column
= NextTab(column
, tabInChars
);
790 } else if (ch
== '\r') {
792 } else if (ch
== '\n') {
794 } else if (i
>= Length()) {
798 i
= MovePositionOutsideChar(i
+ 1, 1, false);
805 int Document::FindColumn(int line
, int column
) {
806 int position
= LineStart(line
);
807 if ((line
>= 0) && (line
< LinesTotal())) {
808 int columnCurrent
= 0;
809 while ((columnCurrent
< column
) && (position
< Length())) {
810 char ch
= cb
.CharAt(position
);
812 columnCurrent
= NextTab(columnCurrent
, tabInChars
);
814 } else if (ch
== '\r') {
816 } else if (ch
== '\n') {
820 position
= MovePositionOutsideChar(position
+ 1, 1, false);
827 void Document::Indent(bool forwards
, int lineBottom
, int lineTop
) {
828 // Dedent - suck white space off the front of the line to dedent by equivalent of a tab
829 for (int line
= lineBottom
; line
>= lineTop
; line
--) {
830 int indentOfLine
= GetLineIndentation(line
);
832 if (LineStart(line
) < LineEnd(line
)) {
833 SetLineIndentation(line
, indentOfLine
+ IndentSize());
836 SetLineIndentation(line
, indentOfLine
- IndentSize());
841 // Convert line endings for a piece of text to a particular mode.
842 // Stop at len or when a NUL is found.
843 // Caller must delete the returned pointer.
844 char *Document::TransformLineEnds(int *pLenOut
, const char *s
, size_t len
, int eolMode
) {
845 char *dest
= new char[2 * len
+ 1];
846 const char *sptr
= s
;
848 for (size_t i
= 0; (i
< len
) && (*sptr
!= '\0'); i
++) {
849 if (*sptr
== '\n' || *sptr
== '\r') {
850 if (eolMode
== SC_EOL_CR
) {
852 } else if (eolMode
== SC_EOL_LF
) {
854 } else { // eolMode == SC_EOL_CRLF
858 if ((*sptr
== '\r') && (i
+1 < len
) && (*(sptr
+1) == '\n')) {
868 *pLenOut
= (dptr
- dest
) - 1;
872 void Document::ConvertLineEnds(int eolModeSet
) {
875 for (int pos
= 0; pos
< Length(); pos
++) {
876 if (cb
.CharAt(pos
) == '\r') {
877 if (cb
.CharAt(pos
+ 1) == '\n') {
879 if (eolModeSet
== SC_EOL_CR
) {
880 DeleteChars(pos
+ 1, 1); // Delete the LF
881 } else if (eolModeSet
== SC_EOL_LF
) {
882 DeleteChars(pos
, 1); // Delete the CR
888 if (eolModeSet
== SC_EOL_CRLF
) {
889 InsertString(pos
+ 1, "\n", 1); // Insert LF
891 } else if (eolModeSet
== SC_EOL_LF
) {
892 InsertString(pos
, "\n", 1); // Insert LF
893 DeleteChars(pos
+ 1, 1); // Delete CR
896 } else if (cb
.CharAt(pos
) == '\n') {
898 if (eolModeSet
== SC_EOL_CRLF
) {
899 InsertString(pos
, "\r", 1); // Insert CR
901 } else if (eolModeSet
== SC_EOL_CR
) {
902 InsertString(pos
, "\r", 1); // Insert CR
903 DeleteChars(pos
+ 1, 1); // Delete LF
910 bool Document::IsWhiteLine(int line
) const {
911 int currentChar
= LineStart(line
);
912 int endLine
= LineEnd(line
);
913 while (currentChar
< endLine
) {
914 if (cb
.CharAt(currentChar
) != ' ' && cb
.CharAt(currentChar
) != '\t') {
922 int Document::ParaUp(int pos
) {
923 int line
= LineFromPosition(pos
);
925 while (line
>= 0 && IsWhiteLine(line
)) { // skip empty lines
928 while (line
>= 0 && !IsWhiteLine(line
)) { // skip non-empty lines
932 return LineStart(line
);
935 int Document::ParaDown(int pos
) {
936 int line
= LineFromPosition(pos
);
937 while (line
< LinesTotal() && !IsWhiteLine(line
)) { // skip non-empty lines
940 while (line
< LinesTotal() && IsWhiteLine(line
)) { // skip empty lines
943 if (line
< LinesTotal())
944 return LineStart(line
);
945 else // end of a document
946 return LineEnd(line
-1);
949 CharClassify::cc
Document::WordCharClass(unsigned char ch
) {
950 if ((SC_CP_UTF8
== dbcsCodePage
) && (ch
>= 0x80))
951 return CharClassify::ccWord
;
952 return charClass
.GetClass(ch
);
956 * Used by commmands that want to select whole words.
957 * Finds the start of word at pos when delta < 0 or the end of the word when delta >= 0.
959 int Document::ExtendWordSelect(int pos
, int delta
, bool onlyWordCharacters
) {
960 CharClassify::cc ccStart
= CharClassify::ccWord
;
962 if (!onlyWordCharacters
)
963 ccStart
= WordCharClass(cb
.CharAt(pos
-1));
964 while (pos
> 0 && (WordCharClass(cb
.CharAt(pos
- 1)) == ccStart
))
967 if (!onlyWordCharacters
&& pos
< Length())
968 ccStart
= WordCharClass(cb
.CharAt(pos
));
969 while (pos
< (Length()) && (WordCharClass(cb
.CharAt(pos
)) == ccStart
))
972 return MovePositionOutsideChar(pos
, delta
);
976 * Find the start of the next word in either a forward (delta >= 0) or backwards direction
978 * This is looking for a transition between character classes although there is also some
979 * additional movement to transit white space.
980 * Used by cursor movement by word commands.
982 int Document::NextWordStart(int pos
, int delta
) {
984 while (pos
> 0 && (WordCharClass(cb
.CharAt(pos
- 1)) == CharClassify::ccSpace
))
987 CharClassify::cc ccStart
= WordCharClass(cb
.CharAt(pos
-1));
988 while (pos
> 0 && (WordCharClass(cb
.CharAt(pos
- 1)) == ccStart
)) {
993 CharClassify::cc ccStart
= WordCharClass(cb
.CharAt(pos
));
994 while (pos
< (Length()) && (WordCharClass(cb
.CharAt(pos
)) == ccStart
))
996 while (pos
< (Length()) && (WordCharClass(cb
.CharAt(pos
)) == CharClassify::ccSpace
))
1003 * Find the end of the next word in either a forward (delta >= 0) or backwards direction
1005 * This is looking for a transition between character classes although there is also some
1006 * additional movement to transit white space.
1007 * Used by cursor movement by word commands.
1009 int Document::NextWordEnd(int pos
, int delta
) {
1012 CharClassify::cc ccStart
= WordCharClass(cb
.CharAt(pos
-1));
1013 if (ccStart
!= CharClassify::ccSpace
) {
1014 while (pos
> 0 && WordCharClass(cb
.CharAt(pos
- 1)) == ccStart
) {
1018 while (pos
> 0 && WordCharClass(cb
.CharAt(pos
- 1)) == CharClassify::ccSpace
) {
1023 while (pos
< Length() && WordCharClass(cb
.CharAt(pos
)) == CharClassify::ccSpace
) {
1026 if (pos
< Length()) {
1027 CharClassify::cc ccStart
= WordCharClass(cb
.CharAt(pos
));
1028 while (pos
< Length() && WordCharClass(cb
.CharAt(pos
)) == ccStart
) {
1037 * Check that the character at the given position is a word or punctuation character and that
1038 * the previous character is of a different character class.
1040 bool Document::IsWordStartAt(int pos
) {
1042 CharClassify::cc ccPos
= WordCharClass(CharAt(pos
));
1043 return (ccPos
== CharClassify::ccWord
|| ccPos
== CharClassify::ccPunctuation
) &&
1044 (ccPos
!= WordCharClass(CharAt(pos
- 1)));
1050 * Check that the character at the given position is a word or punctuation character and that
1051 * the next character is of a different character class.
1053 bool Document::IsWordEndAt(int pos
) {
1054 if (pos
< Length()) {
1055 CharClassify::cc ccPrev
= WordCharClass(CharAt(pos
-1));
1056 return (ccPrev
== CharClassify::ccWord
|| ccPrev
== CharClassify::ccPunctuation
) &&
1057 (ccPrev
!= WordCharClass(CharAt(pos
)));
1063 * Check that the given range is has transitions between character classes at both
1064 * ends and where the characters on the inside are word or punctuation characters.
1066 bool Document::IsWordAt(int start
, int end
) {
1067 return IsWordStartAt(start
) && IsWordEndAt(end
);
1070 static inline char MakeLowerCase(char ch
) {
1071 if (ch
< 'A' || ch
> 'Z')
1074 return static_cast<char>(ch
- 'A' + 'a');
1078 * Find text in document, supporting both forward and backward
1079 * searches (just pass minPos > maxPos to do a backward search)
1080 * Has not been tested with backwards DBCS searches yet.
1082 long Document::FindText(int minPos
, int maxPos
, const char *s
,
1083 bool caseSensitive
, bool word
, bool wordStart
, bool regExp
, int flags
,
1087 regex
= CreateRegexSearch(&charClass
);
1088 return regex
->FindText(this, minPos
, maxPos
, s
, caseSensitive
, word
, wordStart
, flags
, length
);
1091 bool forward
= minPos
<= maxPos
;
1092 int increment
= forward
? 1 : -1;
1094 // Range endpoints should not be inside DBCS characters, but just in case, move them.
1095 int startPos
= MovePositionOutsideChar(minPos
, increment
, false);
1096 int endPos
= MovePositionOutsideChar(maxPos
, increment
, false);
1098 // Compute actual search ranges needed
1099 int lengthFind
= *length
;
1100 if (lengthFind
== -1)
1101 lengthFind
= static_cast<int>(strlen(s
));
1102 int endSearch
= endPos
;
1103 if (startPos
<= endPos
) {
1104 endSearch
= endPos
- lengthFind
+ 1;
1106 //Platform::DebugPrintf("Find %d %d %s %d\n", startPos, endPos, ft->lpstrText, lengthFind);
1107 char firstChar
= s
[0];
1109 firstChar
= static_cast<char>(MakeUpperCase(firstChar
));
1110 int pos
= forward
? startPos
: (startPos
- 1);
1111 while (forward
? (pos
< endSearch
) : (pos
>= endSearch
)) {
1112 char ch
= CharAt(pos
);
1113 if (caseSensitive
) {
1114 if (ch
== firstChar
) {
1116 if (pos
+ lengthFind
> Platform::Maximum(startPos
, endPos
)) found
= false;
1117 for (int posMatch
= 1; posMatch
< lengthFind
&& found
; posMatch
++) {
1118 ch
= CharAt(pos
+ posMatch
);
1119 if (ch
!= s
[posMatch
])
1123 if ((!word
&& !wordStart
) ||
1124 (word
&& IsWordAt(pos
, pos
+ lengthFind
)) ||
1125 (wordStart
&& IsWordStartAt(pos
)))
1130 if (MakeUpperCase(ch
) == firstChar
) {
1132 if (pos
+ lengthFind
> Platform::Maximum(startPos
, endPos
)) found
= false;
1133 for (int posMatch
= 1; posMatch
< lengthFind
&& found
; posMatch
++) {
1134 ch
= CharAt(pos
+ posMatch
);
1135 if (MakeUpperCase(ch
) != MakeUpperCase(s
[posMatch
]))
1139 if ((!word
&& !wordStart
) ||
1140 (word
&& IsWordAt(pos
, pos
+ lengthFind
)) ||
1141 (wordStart
&& IsWordStartAt(pos
)))
1147 if (dbcsCodePage
&& (pos
>= 0)) {
1148 // Ensure trying to match from start of character
1149 pos
= MovePositionOutsideChar(pos
, increment
, false);
1153 //Platform::DebugPrintf("Not found\n");
1157 const char *Document::SubstituteByPosition(const char *text
, int *length
) {
1158 return regex
->SubstituteByPosition(this, text
, length
);
1161 int Document::LinesTotal() const {
1165 void Document::ChangeCase(Range r
, bool makeUpperCase
) {
1166 for (int pos
= r
.start
; pos
< r
.end
;) {
1167 int len
= LenChar(pos
);
1169 char ch
= CharAt(pos
);
1170 if (makeUpperCase
) {
1171 if (IsLowerCase(ch
)) {
1172 ChangeChar(pos
, static_cast<char>(MakeUpperCase(ch
)));
1175 if (IsUpperCase(ch
)) {
1176 ChangeChar(pos
, static_cast<char>(MakeLowerCase(ch
)));
1184 void Document::SetDefaultCharClasses(bool includeWordClass
) {
1185 charClass
.SetDefaultCharClasses(includeWordClass
);
1188 void Document::SetCharClasses(const unsigned char *chars
, CharClassify::cc newCharClass
) {
1189 charClass
.SetCharClasses(chars
, newCharClass
);
1192 void Document::SetStylingBits(int bits
) {
1194 stylingBitsMask
= (1 << stylingBits
) - 1;
1197 void Document::StartStyling(int position
, char mask
) {
1199 endStyled
= position
;
1202 bool Document::SetStyleFor(int length
, char style
) {
1203 if (enteredStyling
!= 0) {
1207 style
&= stylingMask
;
1208 int prevEndStyled
= endStyled
;
1209 if (cb
.SetStyleFor(endStyled
, length
, style
, stylingMask
)) {
1210 DocModification
mh(SC_MOD_CHANGESTYLE
| SC_PERFORMED_USER
,
1211 prevEndStyled
, length
);
1214 endStyled
+= length
;
1220 bool Document::SetStyles(int length
, const char *styles
) {
1221 if (enteredStyling
!= 0) {
1225 bool didChange
= false;
1228 for (int iPos
= 0; iPos
< length
; iPos
++, endStyled
++) {
1229 PLATFORM_ASSERT(endStyled
< Length());
1230 if (cb
.SetStyleAt(endStyled
, styles
[iPos
], stylingMask
)) {
1232 startMod
= endStyled
;
1239 DocModification
mh(SC_MOD_CHANGESTYLE
| SC_PERFORMED_USER
,
1240 startMod
, endMod
- startMod
+ 1);
1248 void Document::EnsureStyledTo(int pos
) {
1249 if ((enteredStyling
== 0) && (pos
> GetEndStyled())) {
1250 IncrementStyleClock();
1251 // Ask the watchers to style, and stop as soon as one responds.
1252 for (int i
= 0; pos
> GetEndStyled() && i
< lenWatchers
; i
++) {
1253 watchers
[i
].watcher
->NotifyStyleNeeded(this, watchers
[i
].userData
, pos
);
1258 int Document::SetLineState(int line
, int state
) {
1259 int statePrevious
= static_cast<LineState
*>(perLineData
[ldState
])->SetLineState(line
, state
);
1260 if (state
!= statePrevious
) {
1261 DocModification
mh(SC_MOD_CHANGELINESTATE
, 0, 0, 0, 0, line
);
1264 return statePrevious
;
1267 int Document::GetLineState(int line
) {
1268 return static_cast<LineState
*>(perLineData
[ldState
])->GetLineState(line
);
1271 int Document::GetMaxLineState() {
1272 return static_cast<LineState
*>(perLineData
[ldState
])->GetMaxLineState();
1275 StyledText
Document::MarginStyledText(int line
) {
1276 LineAnnotation
*pla
= static_cast<LineAnnotation
*>(perLineData
[ldMargin
]);
1277 return StyledText(pla
->Length(line
), pla
->Text(line
),
1278 pla
->MultipleStyles(line
), pla
->Style(line
), pla
->Styles(line
));
1281 void Document::MarginSetText(int line
, const char *text
) {
1282 static_cast<LineAnnotation
*>(perLineData
[ldMargin
])->SetText(line
, text
);
1283 DocModification
mh(SC_MOD_CHANGEMARGIN
, LineStart(line
), 0, 0, 0, line
);
1287 void Document::MarginSetStyle(int line
, int style
) {
1288 static_cast<LineAnnotation
*>(perLineData
[ldMargin
])->SetStyle(line
, style
);
1291 void Document::MarginSetStyles(int line
, const unsigned char *styles
) {
1292 static_cast<LineAnnotation
*>(perLineData
[ldMargin
])->SetStyles(line
, styles
);
1295 int Document::MarginLength(int line
) const {
1296 return static_cast<LineAnnotation
*>(perLineData
[ldMargin
])->Length(line
);
1299 void Document::MarginClearAll() {
1300 int maxEditorLine
= LinesTotal();
1301 for (int l
=0;l
<maxEditorLine
;l
++)
1302 MarginSetText(l
, 0);
1303 // Free remaining data
1304 static_cast<LineAnnotation
*>(perLineData
[ldMargin
])->ClearAll();
1307 bool Document::AnnotationAny() const {
1308 return static_cast<LineAnnotation
*>(perLineData
[ldAnnotation
])->AnySet();
1311 StyledText
Document::AnnotationStyledText(int line
) {
1312 LineAnnotation
*pla
= static_cast<LineAnnotation
*>(perLineData
[ldAnnotation
]);
1313 return StyledText(pla
->Length(line
), pla
->Text(line
),
1314 pla
->MultipleStyles(line
), pla
->Style(line
), pla
->Styles(line
));
1317 void Document::AnnotationSetText(int line
, const char *text
) {
1318 const int linesBefore
= AnnotationLines(line
);
1319 static_cast<LineAnnotation
*>(perLineData
[ldAnnotation
])->SetText(line
, text
);
1320 const int linesAfter
= AnnotationLines(line
);
1321 DocModification
mh(SC_MOD_CHANGEANNOTATION
, LineStart(line
), 0, 0, 0, line
);
1322 mh
.annotationLinesAdded
= linesAfter
- linesBefore
;
1326 void Document::AnnotationSetStyle(int line
, int style
) {
1327 static_cast<LineAnnotation
*>(perLineData
[ldAnnotation
])->SetStyle(line
, style
);
1330 void Document::AnnotationSetStyles(int line
, const unsigned char *styles
) {
1331 static_cast<LineAnnotation
*>(perLineData
[ldAnnotation
])->SetStyles(line
, styles
);
1334 int Document::AnnotationLength(int line
) const {
1335 return static_cast<LineAnnotation
*>(perLineData
[ldAnnotation
])->Length(line
);
1338 int Document::AnnotationLines(int line
) const {
1339 return static_cast<LineAnnotation
*>(perLineData
[ldAnnotation
])->Lines(line
);
1342 void Document::AnnotationClearAll() {
1343 int maxEditorLine
= LinesTotal();
1344 for (int l
=0;l
<maxEditorLine
;l
++)
1345 AnnotationSetText(l
, 0);
1346 // Free remaining data
1347 static_cast<LineAnnotation
*>(perLineData
[ldAnnotation
])->ClearAll();
1350 void Document::IncrementStyleClock() {
1351 styleClock
= (styleClock
+ 1) % 0x100000;
1354 void Document::DecorationFillRange(int position
, int value
, int fillLength
) {
1355 if (decorations
.FillRange(position
, value
, fillLength
)) {
1356 DocModification
mh(SC_MOD_CHANGEINDICATOR
| SC_PERFORMED_USER
,
1357 position
, fillLength
);
1362 bool Document::AddWatcher(DocWatcher
*watcher
, void *userData
) {
1363 for (int i
= 0; i
< lenWatchers
; i
++) {
1364 if ((watchers
[i
].watcher
== watcher
) &&
1365 (watchers
[i
].userData
== userData
))
1368 WatcherWithUserData
*pwNew
= new WatcherWithUserData
[lenWatchers
+ 1];
1369 for (int j
= 0; j
< lenWatchers
; j
++)
1370 pwNew
[j
] = watchers
[j
];
1371 pwNew
[lenWatchers
].watcher
= watcher
;
1372 pwNew
[lenWatchers
].userData
= userData
;
1379 bool Document::RemoveWatcher(DocWatcher
*watcher
, void *userData
) {
1380 for (int i
= 0; i
< lenWatchers
; i
++) {
1381 if ((watchers
[i
].watcher
== watcher
) &&
1382 (watchers
[i
].userData
== userData
)) {
1383 if (lenWatchers
== 1) {
1388 WatcherWithUserData
*pwNew
= new WatcherWithUserData
[lenWatchers
];
1389 for (int j
= 0; j
< lenWatchers
- 1; j
++) {
1390 pwNew
[j
] = (j
< i
) ? watchers
[j
] : watchers
[j
+ 1];
1402 void Document::NotifyModifyAttempt() {
1403 for (int i
= 0; i
< lenWatchers
; i
++) {
1404 watchers
[i
].watcher
->NotifyModifyAttempt(this, watchers
[i
].userData
);
1408 void Document::NotifySavePoint(bool atSavePoint
) {
1409 for (int i
= 0; i
< lenWatchers
; i
++) {
1410 watchers
[i
].watcher
->NotifySavePoint(this, watchers
[i
].userData
, atSavePoint
);
1414 void Document::NotifyModified(DocModification mh
) {
1415 if (mh
.modificationType
& SC_MOD_INSERTTEXT
) {
1416 decorations
.InsertSpace(mh
.position
, mh
.length
);
1417 } else if (mh
.modificationType
& SC_MOD_DELETETEXT
) {
1418 decorations
.DeleteRange(mh
.position
, mh
.length
);
1420 for (int i
= 0; i
< lenWatchers
; i
++) {
1421 watchers
[i
].watcher
->NotifyModified(this, mh
, watchers
[i
].userData
);
1425 bool Document::IsWordPartSeparator(char ch
) {
1426 return (WordCharClass(ch
) == CharClassify::ccWord
) && IsPunctuation(ch
);
1429 int Document::WordPartLeft(int pos
) {
1432 char startChar
= cb
.CharAt(pos
);
1433 if (IsWordPartSeparator(startChar
)) {
1434 while (pos
> 0 && IsWordPartSeparator(cb
.CharAt(pos
))) {
1439 startChar
= cb
.CharAt(pos
);
1441 if (IsLowerCase(startChar
)) {
1442 while (pos
> 0 && IsLowerCase(cb
.CharAt(pos
)))
1444 if (!IsUpperCase(cb
.CharAt(pos
)) && !IsLowerCase(cb
.CharAt(pos
)))
1446 } else if (IsUpperCase(startChar
)) {
1447 while (pos
> 0 && IsUpperCase(cb
.CharAt(pos
)))
1449 if (!IsUpperCase(cb
.CharAt(pos
)))
1451 } else if (IsADigit(startChar
)) {
1452 while (pos
> 0 && IsADigit(cb
.CharAt(pos
)))
1454 if (!IsADigit(cb
.CharAt(pos
)))
1456 } else if (IsPunctuation(startChar
)) {
1457 while (pos
> 0 && IsPunctuation(cb
.CharAt(pos
)))
1459 if (!IsPunctuation(cb
.CharAt(pos
)))
1461 } else if (isspacechar(startChar
)) {
1462 while (pos
> 0 && isspacechar(cb
.CharAt(pos
)))
1464 if (!isspacechar(cb
.CharAt(pos
)))
1466 } else if (!isascii(startChar
)) {
1467 while (pos
> 0 && !isascii(cb
.CharAt(pos
)))
1469 if (isascii(cb
.CharAt(pos
)))
1479 int Document::WordPartRight(int pos
) {
1480 char startChar
= cb
.CharAt(pos
);
1481 int length
= Length();
1482 if (IsWordPartSeparator(startChar
)) {
1483 while (pos
< length
&& IsWordPartSeparator(cb
.CharAt(pos
)))
1485 startChar
= cb
.CharAt(pos
);
1487 if (!isascii(startChar
)) {
1488 while (pos
< length
&& !isascii(cb
.CharAt(pos
)))
1490 } else if (IsLowerCase(startChar
)) {
1491 while (pos
< length
&& IsLowerCase(cb
.CharAt(pos
)))
1493 } else if (IsUpperCase(startChar
)) {
1494 if (IsLowerCase(cb
.CharAt(pos
+ 1))) {
1496 while (pos
< length
&& IsLowerCase(cb
.CharAt(pos
)))
1499 while (pos
< length
&& IsUpperCase(cb
.CharAt(pos
)))
1502 if (IsLowerCase(cb
.CharAt(pos
)) && IsUpperCase(cb
.CharAt(pos
- 1)))
1504 } else if (IsADigit(startChar
)) {
1505 while (pos
< length
&& IsADigit(cb
.CharAt(pos
)))
1507 } else if (IsPunctuation(startChar
)) {
1508 while (pos
< length
&& IsPunctuation(cb
.CharAt(pos
)))
1510 } else if (isspacechar(startChar
)) {
1511 while (pos
< length
&& isspacechar(cb
.CharAt(pos
)))
1519 bool IsLineEndChar(char c
) {
1520 return (c
== '\n' || c
== '\r');
1523 int Document::ExtendStyleRange(int pos
, int delta
, bool singleLine
) {
1524 int sStart
= cb
.StyleAt(pos
);
1526 while (pos
> 0 && (cb
.StyleAt(pos
) == sStart
) && (!singleLine
|| !IsLineEndChar(cb
.CharAt(pos
))) )
1530 while (pos
< (Length()) && (cb
.StyleAt(pos
) == sStart
) && (!singleLine
|| !IsLineEndChar(cb
.CharAt(pos
))) )
1536 static char BraceOpposite(char ch
) {
1559 // TODO: should be able to extend styled region to find matching brace
1560 int Document::BraceMatch(int position
, int /*maxReStyle*/) {
1561 char chBrace
= CharAt(position
);
1562 char chSeek
= BraceOpposite(chBrace
);
1565 char styBrace
= static_cast<char>(StyleAt(position
) & stylingBitsMask
);
1567 if (chBrace
== '(' || chBrace
== '[' || chBrace
== '{' || chBrace
== '<')
1570 position
= position
+ direction
;
1571 while ((position
>= 0) && (position
< Length())) {
1572 position
= MovePositionOutsideChar(position
, direction
);
1573 char chAtPos
= CharAt(position
);
1574 char styAtPos
= static_cast<char>(StyleAt(position
) & stylingBitsMask
);
1575 if ((position
> GetEndStyled()) || (styAtPos
== styBrace
)) {
1576 if (chAtPos
== chBrace
)
1578 if (chAtPos
== chSeek
)
1583 position
= position
+ direction
;
1589 * Implementation of RegexSearchBase for the default built-in regular expression engine
1591 class BuiltinRegex
: public RegexSearchBase
{
1593 BuiltinRegex(CharClassify
*charClassTable
) : search(charClassTable
), substituted(NULL
) {}
1595 virtual ~BuiltinRegex() {
1599 virtual long FindText(Document
*doc
, int minPos
, int maxPos
, const char *s
,
1600 bool caseSensitive
, bool word
, bool wordStart
, int flags
,
1603 virtual const char *SubstituteByPosition(Document
* doc
, const char *text
, int *length
);
1610 // Define a way for the Regular Expression code to access the document
1611 class DocumentIndexer
: public CharacterIndexer
{
1615 DocumentIndexer(Document
*pdoc_
, int end_
) :
1616 pdoc(pdoc_
), end(end_
) {
1619 virtual ~DocumentIndexer() {
1622 virtual char CharAt(int index
) {
1623 if (index
< 0 || index
>= end
)
1626 return pdoc
->CharAt(index
);
1630 long BuiltinRegex::FindText(Document
*doc
, int minPos
, int maxPos
, const char *s
,
1631 bool caseSensitive
, bool, bool, int flags
,
1633 bool posix
= (flags
& SCFIND_POSIX
) != 0;
1634 int increment
= (minPos
<= maxPos
) ? 1 : -1;
1636 int startPos
= minPos
;
1637 int endPos
= maxPos
;
1639 // Range endpoints should not be inside DBCS characters, but just in case, move them.
1640 startPos
= doc
->MovePositionOutsideChar(startPos
, 1, false);
1641 endPos
= doc
->MovePositionOutsideChar(endPos
, 1, false);
1643 const char *errmsg
= search
.Compile(s
, *length
, caseSensitive
, posix
);
1647 // Find a variable in a property file: \$(\([A-Za-z0-9_.]+\))
1648 // Replace first '.' with '-' in each property file variable reference:
1649 // Search: \$(\([A-Za-z0-9_-]+\)\.\([A-Za-z0-9_.]+\))
1650 // Replace: $(\1-\2)
1651 int lineRangeStart
= doc
->LineFromPosition(startPos
);
1652 int lineRangeEnd
= doc
->LineFromPosition(endPos
);
1653 if ((increment
== 1) &&
1654 (startPos
>= doc
->LineEnd(lineRangeStart
)) &&
1655 (lineRangeStart
< lineRangeEnd
)) {
1656 // the start position is at end of line or between line end characters.
1658 startPos
= doc
->LineStart(lineRangeStart
);
1662 char searchEnd
= s
[*length
- 1];
1663 int lineRangeBreak
= lineRangeEnd
+ increment
;
1664 for (int line
= lineRangeStart
; line
!= lineRangeBreak
; line
+= increment
) {
1665 int startOfLine
= doc
->LineStart(line
);
1666 int endOfLine
= doc
->LineEnd(line
);
1667 if (increment
== 1) {
1668 if (line
== lineRangeStart
) {
1669 if ((startPos
!= startOfLine
) && (s
[0] == '^'))
1670 continue; // Can't match start of line if start position after start of line
1671 startOfLine
= startPos
;
1673 if (line
== lineRangeEnd
) {
1674 if ((endPos
!= endOfLine
) && (searchEnd
== '$'))
1675 continue; // Can't match end of line if end position before end of line
1679 if (line
== lineRangeEnd
) {
1680 if ((endPos
!= startOfLine
) && (s
[0] == '^'))
1681 continue; // Can't match start of line if end position after start of line
1682 startOfLine
= endPos
;
1684 if (line
== lineRangeStart
) {
1685 if ((startPos
!= endOfLine
) && (searchEnd
== '$'))
1686 continue; // Can't match end of line if start position before end of line
1687 endOfLine
= startPos
;
1691 DocumentIndexer
di(doc
, endOfLine
);
1692 int success
= search
.Execute(di
, startOfLine
, endOfLine
);
1694 pos
= search
.bopat
[0];
1695 lenRet
= search
.eopat
[0] - search
.bopat
[0];
1696 if (increment
== -1) {
1697 // Check for the last match on this line.
1698 int repetitions
= 1000; // Break out of infinite loop
1699 while (success
&& (search
.eopat
[0] <= endOfLine
) && (repetitions
--)) {
1700 success
= search
.Execute(di
, pos
+1, endOfLine
);
1702 if (search
.eopat
[0] <= minPos
) {
1703 pos
= search
.bopat
[0];
1704 lenRet
= search
.eopat
[0] - search
.bopat
[0];
1718 const char *BuiltinRegex::SubstituteByPosition(Document
* doc
, const char *text
, int *length
) {
1719 delete []substituted
;
1721 DocumentIndexer
di(doc
, doc
->Length());
1722 if (!search
.GrabMatches(di
))
1724 unsigned int lenResult
= 0;
1725 for (int i
= 0; i
< *length
; i
++) {
1726 if (text
[i
] == '\\') {
1727 if (text
[i
+ 1] >= '1' && text
[i
+ 1] <= '9') {
1728 unsigned int patNum
= text
[i
+ 1] - '0';
1729 lenResult
+= search
.eopat
[patNum
] - search
.bopat
[patNum
];
1732 switch (text
[i
+ 1]) {
1748 substituted
= new char[lenResult
+ 1];
1749 char *o
= substituted
;
1750 for (int j
= 0; j
< *length
; j
++) {
1751 if (text
[j
] == '\\') {
1752 if (text
[j
+ 1] >= '1' && text
[j
+ 1] <= '9') {
1753 unsigned int patNum
= text
[j
+ 1] - '0';
1754 unsigned int len
= search
.eopat
[patNum
] - search
.bopat
[patNum
];
1755 if (search
.pat
[patNum
]) // Will be null if try for a match that did not occur
1756 memcpy(o
, search
.pat
[patNum
], len
);
1793 *length
= lenResult
;
1797 #ifndef SCI_OWNREGEX
1799 #ifdef SCI_NAMESPACE
1801 RegexSearchBase
*Scintilla::CreateRegexSearch(CharClassify
*charClassTable
) {
1802 return new BuiltinRegex(charClassTable
);
1807 RegexSearchBase
*CreateRegexSearch(CharClassify
*charClassTable
) {
1808 return new BuiltinRegex(charClassTable
);