~brian-sidebotham/wxwidgets-cmake/wxpython-2.9.4

« back to all changes in this revision

Viewing changes to src/stc/scintilla/src/Document.cxx

  • Committer: Brian Sidebotham
  • Date: 2013-08-03 14:30:08 UTC
  • Revision ID: brian.sidebotham@gmail.com-20130803143008-c7806tkych1tp6fc
Initial import into Bazaar

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Scintilla source code edit control
 
2
/** @file Document.cxx
 
3
 ** Text document that handles notifications, DBCS, styling, words and end of line.
 
4
 **/
 
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.
 
7
 
 
8
#include <stdlib.h>
 
9
#include <string.h>
 
10
#include <stdio.h>
 
11
#include <ctype.h>
 
12
 
 
13
#include "Platform.h"
 
14
 
 
15
#include "Scintilla.h"
 
16
#include "SplitVector.h"
 
17
#include "Partitioning.h"
 
18
#include "RunStyles.h"
 
19
#include "CellBuffer.h"
 
20
#include "PerLine.h"
 
21
#include "CharClassify.h"
 
22
#include "Decoration.h"
 
23
#include "Document.h"
 
24
#include "RESearch.h"
 
25
 
 
26
#ifdef SCI_NAMESPACE
 
27
using namespace Scintilla;
 
28
#endif
 
29
 
 
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));
 
33
}
 
34
 
 
35
static inline bool IsPunctuation(char ch) {
 
36
        return isascii(ch) && ispunct(ch);
 
37
}
 
38
 
 
39
static inline bool IsADigit(char ch) {
 
40
        return isascii(ch) && isdigit(ch);
 
41
}
 
42
 
 
43
static inline bool IsLowerCase(char ch) {
 
44
        return isascii(ch) && islower(ch);
 
45
}
 
46
 
 
47
static inline bool IsUpperCase(char ch) {
 
48
        return isascii(ch) && isupper(ch);
 
49
}
 
50
 
 
51
Document::Document() {
 
52
        refCount = 0;
 
53
#ifdef unix
 
54
        eolMode = SC_EOL_LF;
 
55
#else
 
56
        eolMode = SC_EOL_CRLF;
 
57
#endif
 
58
        dbcsCodePage = 0;
 
59
        stylingBits = 5;
 
60
        stylingBitsMask = 0x1F;
 
61
        stylingMask = 0;
 
62
        endStyled = 0;
 
63
        styleClock = 0;
 
64
        enteredModification = 0;
 
65
        enteredStyling = 0;
 
66
        enteredReadOnlyCount = 0;
 
67
        tabInChars = 8;
 
68
        indentInChars = 0;
 
69
        actualIndentInChars = 8;
 
70
        useTabs = true;
 
71
        tabIndents = true;
 
72
        backspaceUnindents = false;
 
73
        watchers = 0;
 
74
        lenWatchers = 0;
 
75
 
 
76
        matchesValid = false;
 
77
        regex = 0;
 
78
 
 
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();
 
84
 
 
85
        cb.SetPerLine(this);
 
86
}
 
87
 
 
88
Document::~Document() {
 
89
        for (int i = 0; i < lenWatchers; i++) {
 
90
                watchers[i].watcher->NotifyDeleted(this, watchers[i].userData);
 
91
        }
 
92
        delete []watchers;
 
93
        for (int j=0; j<ldSize; j++) {
 
94
                delete perLineData[j];
 
95
                perLineData[j] = 0;
 
96
        }
 
97
        watchers = 0;
 
98
        lenWatchers = 0;
 
99
        delete regex;
 
100
        regex = 0;
 
101
}
 
102
 
 
103
void Document::Init() {
 
104
        for (int j=0; j<ldSize; j++) {
 
105
                if (perLineData[j])
 
106
                        perLineData[j]->Init();
 
107
        }
 
108
}
 
109
 
 
110
void Document::InsertLine(int line) {
 
111
        for (int j=0; j<ldSize; j++) {
 
112
                if (perLineData[j])
 
113
                        perLineData[j]->InsertLine(line);
 
114
        }
 
115
}
 
116
 
 
117
void Document::RemoveLine(int line) {
 
118
        for (int j=0; j<ldSize; j++) {
 
119
                if (perLineData[j])
 
120
                        perLineData[j]->RemoveLine(line);
 
121
        }
 
122
}
 
123
 
 
124
// Increase reference count and return its previous value.
 
125
int Document::AddRef() {
 
126
        return refCount++;
 
127
}
 
128
 
 
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)
 
134
                delete this;
 
135
        return curRefCount;
 
136
}
 
137
 
 
138
void Document::SetSavePoint() {
 
139
        cb.SetSavePoint();
 
140
        NotifySavePoint(true);
 
141
}
 
142
 
 
143
int Document::GetMark(int line) { 
 
144
        return static_cast<LineMarkers*>(perLineData[ldMarkers])->MarkValue(line); 
 
145
}
 
146
 
 
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);
 
152
                NotifyModified(mh);
 
153
                return prev;
 
154
        } else {
 
155
                return 0;
 
156
        }
 
157
}
 
158
 
 
159
void Document::AddMarkSet(int line, int valueSet) {
 
160
        unsigned int m = valueSet;
 
161
        for (int i = 0; m; i++, m >>= 1)
 
162
                if (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);
 
166
        NotifyModified(mh);
 
167
}
 
168
 
 
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);
 
172
        NotifyModified(mh);
 
173
}
 
174
 
 
175
void Document::DeleteMarkFromHandle(int markerHandle) {
 
176
        static_cast<LineMarkers*>(perLineData[ldMarkers])->DeleteMarkFromHandle(markerHandle);
 
177
        DocModification mh(SC_MOD_CHANGEMARKER, 0, 0, 0, 0);
 
178
        mh.line = -1;
 
179
        NotifyModified(mh);
 
180
}
 
181
 
 
182
void Document::DeleteAllMarks(int markerNum) {
 
183
        for (int line = 0; line < LinesTotal(); line++) {
 
184
                static_cast<LineMarkers*>(perLineData[ldMarkers])->DeleteMark(line, markerNum, true);
 
185
        }
 
186
        DocModification mh(SC_MOD_CHANGEMARKER, 0, 0, 0, 0);
 
187
        mh.line = -1;
 
188
        NotifyModified(mh);
 
189
}
 
190
 
 
191
int Document::LineFromHandle(int markerHandle) { 
 
192
        return static_cast<LineMarkers*>(perLineData[ldMarkers])->LineFromHandle(markerHandle); 
 
193
}
 
194
 
 
195
int Document::LineStart(int line) const {
 
196
        return cb.LineStart(line);
 
197
}
 
198
 
 
199
int Document::LineEnd(int line) const {
 
200
        if (line == LinesTotal() - 1) {
 
201
                return LineStart(line + 1);
 
202
        } else {
 
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')) {
 
206
                        position--;
 
207
                }
 
208
                return position;
 
209
        }
 
210
}
 
211
 
 
212
int Document::LineFromPosition(int pos) const {
 
213
        return cb.LineFromPosition(pos);
 
214
}
 
215
 
 
216
int Document::LineEndPosition(int position) const {
 
217
        return LineEnd(LineFromPosition(position));
 
218
}
 
219
 
 
220
bool Document::IsLineEndPosition(int position) const {
 
221
        return LineEnd(LineFromPosition(position)) == position;
 
222
}
 
223
 
 
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' ) )
 
230
                startText++;
 
231
        if (position == startText)
 
232
                return startPosition;
 
233
        else
 
234
                return startText;
 
235
}
 
236
 
 
237
int Document::SetLevel(int line, int level) {
 
238
        int prev = static_cast<LineLevels*>(perLineData[ldLevels])->SetLevel(line, level, LinesTotal());
 
239
        if (prev != level) {
 
240
                DocModification mh(SC_MOD_CHANGEFOLD | SC_MOD_CHANGEMARKER,
 
241
                                   LineStart(line), 0, 0, 0, line);
 
242
                mh.foldLevelNow = level;
 
243
                mh.foldLevelPrev = prev;
 
244
                NotifyModified(mh);
 
245
        }
 
246
        return prev;
 
247
}
 
248
 
 
249
int Document::GetLevel(int line) { 
 
250
        return static_cast<LineLevels*>(perLineData[ldLevels])->GetLevel(line); 
 
251
}
 
252
 
 
253
void Document::ClearLevels() { 
 
254
        static_cast<LineLevels*>(perLineData[ldLevels])->ClearLevels(); 
 
255
}
 
256
 
 
257
static bool IsSubordinate(int levelStart, int levelTry) {
 
258
        if (levelTry & SC_FOLDLEVELWHITEFLAG)
 
259
                return true;
 
260
        else
 
261
                return (levelStart & SC_FOLDLEVELNUMBERMASK) < (levelTry & SC_FOLDLEVELNUMBERMASK);
 
262
}
 
263
 
 
264
int Document::GetLastChild(int lineParent, int level) {
 
265
        if (level == -1)
 
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)))
 
272
                        break;
 
273
                lineMaxSubord++;
 
274
        }
 
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) {
 
279
                                lineMaxSubord--;
 
280
                        }
 
281
                }
 
282
        }
 
283
        return lineMaxSubord;
 
284
}
 
285
 
 
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))
 
292
              ) {
 
293
                lineLook--;
 
294
        }
 
295
        if ((GetLevel(lineLook) & SC_FOLDLEVELHEADERFLAG) &&
 
296
                ((GetLevel(lineLook) & SC_FOLDLEVELNUMBERMASK) < level)) {
 
297
                return lineLook;
 
298
        } else {
 
299
                return -1;
 
300
        }
 
301
}
 
302
 
 
303
int Document::ClampPositionIntoDocument(int pos) {
 
304
        return Platform::Clamp(pos, 0, Length());
 
305
}
 
306
 
 
307
bool Document::IsCrLf(int pos) {
 
308
        if (pos < 0)
 
309
                return false;
 
310
        if (pos >= (Length() - 1))
 
311
                return false;
 
312
        return (cb.CharAt(pos) == '\r') && (cb.CharAt(pos + 1) == '\n');
 
313
}
 
314
 
 
315
static const int maxBytesInDBCSCharacter=5;
 
316
 
 
317
int Document::LenChar(int pos) {
 
318
        if (pos < 0) {
 
319
                return 1;
 
320
        } else if (IsCrLf(pos)) {
 
321
                return 2;
 
322
        } else if (SC_CP_UTF8 == dbcsCodePage) {
 
323
                unsigned char ch = static_cast<unsigned char>(cb.CharAt(pos));
 
324
                if (ch < 0x80)
 
325
                        return 1;
 
326
                int len = 2;
 
327
                if (ch >= (0x80 + 0x40 + 0x20 + 0x10))
 
328
                        len = 4;
 
329
                else if (ch >= (0x80 + 0x40 + 0x20))
 
330
                        len = 3;
 
331
                int lengthDoc = Length();
 
332
                if ((pos + len) > lengthDoc)
 
333
                        return lengthDoc -pos;
 
334
                else
 
335
                        return len;
 
336
        } else if (dbcsCodePage) {
 
337
                char mbstr[maxBytesInDBCSCharacter+1];
 
338
                int i;
 
339
                for (i=0; i<Platform::DBCSCharMaxLength(); i++) {
 
340
                        mbstr[i] = cb.CharAt(pos+i);
 
341
                }
 
342
                mbstr[i] = '\0';
 
343
                return Platform::DBCSCharLength(dbcsCodePage, mbstr);
 
344
        } else {
 
345
                return 1;
 
346
        }
 
347
}
 
348
 
 
349
static bool IsTrailByte(int ch) {
 
350
        return (ch >= 0x80) && (ch < (0x80 + 0x40));
 
351
}
 
352
 
 
353
static int BytesFromLead(int leadByte) {
 
354
        if (leadByte > 0xF4) {
 
355
                // Characters longer than 4 bytes not possible in current UTF-8
 
356
                return 0;
 
357
        } else if (leadByte >= 0xF0) {
 
358
                return 4;
 
359
        } else if (leadByte >= 0xE0) {
 
360
                return 3;
 
361
        } else if (leadByte >= 0xC2) {
 
362
                return 2;
 
363
        }
 
364
        return 0;
 
365
}
 
366
 
 
367
bool Document::InGoodUTF8(int pos, int &start, int &end) {
 
368
        int lead = pos;
 
369
        while ((lead>0) && (pos-lead < 4) && IsTrailByte(static_cast<unsigned char>(cb.CharAt(lead-1))))
 
370
                lead--;
 
371
        start = 0;
 
372
        if (lead > 0) {
 
373
                start = lead-1;
 
374
        }
 
375
        int leadByte = static_cast<unsigned char>(cb.CharAt(start));
 
376
        int bytes = BytesFromLead(leadByte);
 
377
        if (bytes == 0) {
 
378
                return false;
 
379
        } else {
 
380
                int trailBytes = bytes - 1;
 
381
                int len = pos - lead + 1;
 
382
                if (len > trailBytes)
 
383
                        // pos too far from lead
 
384
                        return false;
 
385
                // Check that there are enough trails for this lead
 
386
                int trail = pos + 1;
 
387
                while ((trail-lead<trailBytes) && (trail < Length())) {
 
388
                        if (!IsTrailByte(static_cast<unsigned char>(cb.CharAt(trail)))) {
 
389
                                return false;
 
390
                        }
 
391
                        trail++;
 
392
                }
 
393
                end = start + bytes;
 
394
                return true;
 
395
        }
 
396
}
 
397
 
 
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.
 
406
        if (pos <= 0)
 
407
                return 0;
 
408
        if (pos >= Length())
 
409
                return Length();
 
410
 
 
411
        // PLATFORM_ASSERT(pos > 0 && pos < Length());
 
412
        if (checkLineEnd && IsCrLf(pos - 1)) {
 
413
                if (moveDir > 0)
 
414
                        return pos + 1;
 
415
                else
 
416
                        return pos - 1;
 
417
        }
 
418
 
 
419
        // Not between CR and LF
 
420
 
 
421
        if (dbcsCodePage) {
 
422
                if (SC_CP_UTF8 == dbcsCodePage) {
 
423
                        unsigned char ch = static_cast<unsigned char>(cb.CharAt(pos));
 
424
                        int startUTF = pos;
 
425
                        int endUTF = pos;
 
426
                        if (IsTrailByte(ch) && InGoodUTF8(pos, startUTF, endUTF)) {
 
427
                                // ch is a trail byte within a UTF-8 character
 
428
                                if (moveDir > 0)
 
429
                                        pos = endUTF;
 
430
                                else
 
431
                                        pos = startUTF;
 
432
                        }
 
433
                } else {
 
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];
 
439
                                int i;
 
440
                                for(i=0;i<Platform::DBCSCharMaxLength();i++) {
 
441
                                        mbstr[i] = cb.CharAt(posCheck+i);
 
442
                                }
 
443
                                mbstr[i] = '\0';
 
444
 
 
445
                                int mbsize = Platform::DBCSCharLength(dbcsCodePage, mbstr);
 
446
                                if (posCheck + mbsize == pos) {
 
447
                                        return pos;
 
448
                                } else if (posCheck + mbsize > pos) {
 
449
                                        if (moveDir > 0) {
 
450
                                                return posCheck + mbsize;
 
451
                                        } else {
 
452
                                                return posCheck;
 
453
                                        }
 
454
                                }
 
455
                                posCheck += mbsize;
 
456
                        }
 
457
                }
 
458
        }
 
459
 
 
460
        return pos;
 
461
}
 
462
 
 
463
void Document::ModifiedAt(int pos) {
 
464
        if (endStyled > pos)
 
465
                endStyled = pos;
 
466
}
 
467
 
 
468
void Document::CheckReadOnly() {
 
469
        if (cb.IsReadOnly() && enteredReadOnlyCount == 0) {
 
470
                enteredReadOnlyCount++;
 
471
                NotifyModifyAttempt();
 
472
                enteredReadOnlyCount--;
 
473
        }
 
474
}
 
475
 
 
476
// Document only modified by gateways DeleteChars, InsertString, Undo, Redo, and SetStyleAt.
 
477
// SetStyleAt does not change the persistent state of a document
 
478
 
 
479
bool Document::DeleteChars(int pos, int len) {
 
480
        if (len == 0)
 
481
                return false;
 
482
        if ((pos + len) > Length())
 
483
                return false;
 
484
        CheckReadOnly();
 
485
        if (enteredModification != 0) {
 
486
                return false;
 
487
        } else {
 
488
                enteredModification++;
 
489
                if (!cb.IsReadOnly()) {
 
490
                        NotifyModified(
 
491
                            DocModification(
 
492
                                SC_MOD_BEFOREDELETE | SC_PERFORMED_USER,
 
493
                                pos, len,
 
494
                                0, 0));
 
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))
 
502
                                ModifiedAt(pos);
 
503
                        else
 
504
                                ModifiedAt(pos-1);
 
505
                        NotifyModified(
 
506
                            DocModification(
 
507
                                SC_MOD_DELETETEXT | SC_PERFORMED_USER | (startSequence?SC_STARTACTION:0),
 
508
                                pos, len,
 
509
                                LinesTotal() - prevLinesTotal, text));
 
510
                }
 
511
                enteredModification--;
 
512
        }
 
513
        return !cb.IsReadOnly();
 
514
}
 
515
 
 
516
/**
 
517
 * Insert a string with a length.
 
518
 */
 
519
bool Document::InsertString(int position, const char *s, int insertLength) {
 
520
        if (insertLength <= 0) {
 
521
                return false;
 
522
        }
 
523
        CheckReadOnly();
 
524
        if (enteredModification != 0) {
 
525
                return false;
 
526
        } else {
 
527
                enteredModification++;
 
528
                if (!cb.IsReadOnly()) {
 
529
                        NotifyModified(
 
530
                            DocModification(
 
531
                                SC_MOD_BEFOREINSERT | SC_PERFORMED_USER,
 
532
                                position, insertLength,
 
533
                                0, s));
 
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);
 
541
                        NotifyModified(
 
542
                            DocModification(
 
543
                                SC_MOD_INSERTTEXT | SC_PERFORMED_USER | (startSequence?SC_STARTACTION:0),
 
544
                                position, insertLength,
 
545
                                LinesTotal() - prevLinesTotal, text));
 
546
                }
 
547
                enteredModification--;
 
548
        }
 
549
        return !cb.IsReadOnly();
 
550
}
 
551
 
 
552
int Document::Undo() {
 
553
        int newPos = -1;
 
554
        CheckReadOnly();
 
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;
 
571
                                        NotifyModified(dm);
 
572
                                } else {
 
573
                                        NotifyModified(DocModification(
 
574
                                                                        SC_MOD_BEFOREDELETE | SC_PERFORMED_UNDO, action));
 
575
                                }
 
576
                                cb.PerformUndoStep();
 
577
                                int cellPosition = action.position;
 
578
                                if (action.at != containerAction) {
 
579
                                        ModifiedAt(cellPosition);
 
580
                                        newPos = cellPosition;
 
581
                                }
 
582
 
 
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;
 
590
                                }
 
591
                                if (steps > 1)
 
592
                                        modFlags |= SC_MULTISTEPUNDOREDO;
 
593
                                const int linesAdded = LinesTotal() - prevLinesTotal;
 
594
                                if (linesAdded != 0)
 
595
                                        multiLine = true;
 
596
                                if (step == steps - 1) {
 
597
                                        modFlags |= SC_LASTSTEPINUNDOREDO;
 
598
                                        if (multiLine)
 
599
                                                modFlags |= SC_MULTILINEUNDOREDO;
 
600
                                }
 
601
                                NotifyModified(DocModification(modFlags, cellPosition, action.lenData,
 
602
                                                                                           linesAdded, action.data));
 
603
                        }
 
604
 
 
605
                        bool endSavePoint = cb.IsSavePoint();
 
606
                        if (startSavePoint != endSavePoint)
 
607
                                NotifySavePoint(endSavePoint);
 
608
                }
 
609
                enteredModification--;
 
610
        }
 
611
        return newPos;
 
612
}
 
613
 
 
614
int Document::Redo() {
 
615
        int newPos = -1;
 
616
        CheckReadOnly();
 
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;
 
632
                                        NotifyModified(dm);
 
633
                                } else {
 
634
                                        NotifyModified(DocModification(
 
635
                                                                        SC_MOD_BEFOREDELETE | SC_PERFORMED_REDO, action));
 
636
                                }
 
637
                                cb.PerformRedoStep();
 
638
                                if (action.at != containerAction) {
 
639
                                        ModifiedAt(action.position);
 
640
                                        newPos = action.position;
 
641
                                }
 
642
 
 
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;
 
649
                                }
 
650
                                if (steps > 1)
 
651
                                        modFlags |= SC_MULTISTEPUNDOREDO;
 
652
                                const int linesAdded = LinesTotal() - prevLinesTotal;
 
653
                                if (linesAdded != 0)
 
654
                                        multiLine = true;
 
655
                                if (step == steps - 1) {
 
656
                                        modFlags |= SC_LASTSTEPINUNDOREDO;
 
657
                                        if (multiLine)
 
658
                                                modFlags |= SC_MULTILINEUNDOREDO;
 
659
                                }
 
660
                                NotifyModified(
 
661
                                        DocModification(modFlags, action.position, action.lenData,
 
662
                                                                        linesAdded, action.data));
 
663
                        }
 
664
 
 
665
                        bool endSavePoint = cb.IsSavePoint();
 
666
                        if (startSavePoint != endSavePoint)
 
667
                                NotifySavePoint(endSavePoint);
 
668
                }
 
669
                enteredModification--;
 
670
        }
 
671
        return newPos;
 
672
}
 
673
 
 
674
/**
 
675
 * Insert a single character.
 
676
 */
 
677
bool Document::InsertChar(int pos, char ch) {
 
678
        char chs[1];
 
679
        chs[0] = ch;
 
680
        return InsertString(pos, chs, 1);
 
681
}
 
682
 
 
683
/**
 
684
 * Insert a null terminated string.
 
685
 */
 
686
bool Document::InsertCString(int position, const char *s) {
 
687
        return InsertString(position, s, strlen(s));
 
688
}
 
689
 
 
690
void Document::ChangeChar(int pos, char ch) {
 
691
        DeleteChars(pos, 1);
 
692
        InsertChar(pos, ch);
 
693
}
 
694
 
 
695
void Document::DelChar(int pos) {
 
696
        DeleteChars(pos, LenChar(pos));
 
697
}
 
698
 
 
699
void Document::DelCharBack(int pos) {
 
700
        if (pos <= 0) {
 
701
                return;
 
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);
 
707
        } else {
 
708
                DeleteChars(pos - 1, 1);
 
709
        }
 
710
}
 
711
 
 
712
static bool isindentchar(char ch) {
 
713
        return (ch == ' ') || (ch == '\t');
 
714
}
 
715
 
 
716
static int NextTab(int pos, int tabSize) {
 
717
        return ((pos / tabSize) + 1) * tabSize;
 
718
}
 
719
 
 
720
static void CreateIndentation(char *linebuf, int length, int indent, int tabSize, bool insertSpaces) {
 
721
        length--;       // ensure space for \0
 
722
        if (!insertSpaces) {
 
723
                while ((indent >= tabSize) && (length > 0)) {
 
724
                        *linebuf++ = '\t';
 
725
                        indent -= tabSize;
 
726
                        length--;
 
727
                }
 
728
        }
 
729
        while ((indent > 0) && (length > 0)) {
 
730
                *linebuf++ = ' ';
 
731
                indent--;
 
732
                length--;
 
733
        }
 
734
        *linebuf = '\0';
 
735
}
 
736
 
 
737
int Document::GetLineIndentation(int line) {
 
738
        int indent = 0;
 
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);
 
744
                        if (ch == ' ')
 
745
                                indent++;
 
746
                        else if (ch == '\t')
 
747
                                indent = NextTab(indent, tabInChars);
 
748
                        else
 
749
                                return indent;
 
750
                }
 
751
        }
 
752
        return indent;
 
753
}
 
754
 
 
755
void Document::SetLineIndentation(int line, int indent) {
 
756
        int indentOfLine = GetLineIndentation(line);
 
757
        if (indent < 0)
 
758
                indent = 0;
 
759
        if (indent != indentOfLine) {
 
760
                char linebuf[1000];
 
761
                CreateIndentation(linebuf, sizeof(linebuf), indent, tabInChars, !useTabs);
 
762
                int thisLineStart = LineStart(line);
 
763
                int indentPos = GetLineIndentPosition(line);
 
764
                UndoGroup ug(this);
 
765
                DeleteChars(thisLineStart, indentPos - thisLineStart);
 
766
                InsertCString(thisLineStart, linebuf);
 
767
        }
 
768
}
 
769
 
 
770
int Document::GetLineIndentPosition(int line) const {
 
771
        if (line < 0)
 
772
                return 0;
 
773
        int pos = LineStart(line);
 
774
        int length = Length();
 
775
        while ((pos < length) && isindentchar(cb.CharAt(pos))) {
 
776
                pos++;
 
777
        }
 
778
        return pos;
 
779
}
 
780
 
 
781
int Document::GetColumn(int pos) {
 
782
        int column = 0;
 
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);
 
787
                        if (ch == '\t') {
 
788
                                column = NextTab(column, tabInChars);
 
789
                                i++;
 
790
                        } else if (ch == '\r') {
 
791
                                return column;
 
792
                        } else if (ch == '\n') {
 
793
                                return column;
 
794
                        } else if (i >= Length()) {
 
795
                                return column;
 
796
                        } else {
 
797
                                column++;
 
798
                                i = MovePositionOutsideChar(i + 1, 1, false);
 
799
                        }
 
800
                }
 
801
        }
 
802
        return column;
 
803
}
 
804
 
 
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);
 
811
                        if (ch == '\t') {
 
812
                                columnCurrent = NextTab(columnCurrent, tabInChars);
 
813
                                position++;
 
814
                        } else if (ch == '\r') {
 
815
                                return position;
 
816
                        } else if (ch == '\n') {
 
817
                                return position;
 
818
                        } else {
 
819
                                columnCurrent++;
 
820
                                position = MovePositionOutsideChar(position + 1, 1, false);
 
821
                        }
 
822
                }
 
823
        }
 
824
        return position;
 
825
}
 
826
 
 
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);
 
831
                if (forwards) {
 
832
                        if (LineStart(line) < LineEnd(line)) {
 
833
                                SetLineIndentation(line, indentOfLine + IndentSize());
 
834
                        }
 
835
                } else {
 
836
                        SetLineIndentation(line, indentOfLine - IndentSize());
 
837
                }
 
838
        }
 
839
}
 
840
 
 
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;
 
847
        char *dptr = dest;
 
848
        for (size_t i = 0; (i < len) && (*sptr != '\0'); i++) {
 
849
                if (*sptr == '\n' || *sptr == '\r') {
 
850
                        if (eolMode == SC_EOL_CR) {
 
851
                                *dptr++ = '\r';
 
852
                        } else if (eolMode == SC_EOL_LF) {
 
853
                                *dptr++ = '\n';
 
854
                        } else { // eolMode == SC_EOL_CRLF
 
855
                                *dptr++ = '\r';
 
856
                                *dptr++ = '\n';
 
857
                        }
 
858
                        if ((*sptr == '\r') && (i+1 < len) && (*(sptr+1) == '\n')) {
 
859
                                i++;
 
860
                                sptr++;
 
861
                        }
 
862
                        sptr++;
 
863
                } else {
 
864
                        *dptr++ = *sptr++;
 
865
                }
 
866
        }
 
867
        *dptr++ = '\0';
 
868
        *pLenOut = (dptr - dest) - 1;
 
869
        return dest;
 
870
}
 
871
 
 
872
void Document::ConvertLineEnds(int eolModeSet) {
 
873
        UndoGroup ug(this);
 
874
 
 
875
        for (int pos = 0; pos < Length(); pos++) {
 
876
                if (cb.CharAt(pos) == '\r') {
 
877
                        if (cb.CharAt(pos + 1) == '\n') {
 
878
                                // CRLF
 
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
 
883
                                } else {
 
884
                                        pos++;
 
885
                                }
 
886
                        } else {
 
887
                                // CR
 
888
                                if (eolModeSet == SC_EOL_CRLF) {
 
889
                                        InsertString(pos + 1, "\n", 1); // Insert LF
 
890
                                        pos++;
 
891
                                } else if (eolModeSet == SC_EOL_LF) {
 
892
                                        InsertString(pos, "\n", 1); // Insert LF
 
893
                                        DeleteChars(pos + 1, 1); // Delete CR
 
894
                                }
 
895
                        }
 
896
                } else if (cb.CharAt(pos) == '\n') {
 
897
                        // LF
 
898
                        if (eolModeSet == SC_EOL_CRLF) {
 
899
                                InsertString(pos, "\r", 1); // Insert CR
 
900
                                pos++;
 
901
                        } else if (eolModeSet == SC_EOL_CR) {
 
902
                                InsertString(pos, "\r", 1); // Insert CR
 
903
                                DeleteChars(pos + 1, 1); // Delete LF
 
904
                        }
 
905
                }
 
906
        }
 
907
 
 
908
}
 
909
 
 
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') {
 
915
                        return false;
 
916
                }
 
917
                ++currentChar;
 
918
        }
 
919
        return true;
 
920
}
 
921
 
 
922
int Document::ParaUp(int pos) {
 
923
        int line = LineFromPosition(pos);
 
924
        line--;
 
925
        while (line >= 0 && IsWhiteLine(line)) { // skip empty lines
 
926
                line--;
 
927
        }
 
928
        while (line >= 0 && !IsWhiteLine(line)) { // skip non-empty lines
 
929
                line--;
 
930
        }
 
931
        line++;
 
932
        return LineStart(line);
 
933
}
 
934
 
 
935
int Document::ParaDown(int pos) {
 
936
        int line = LineFromPosition(pos);
 
937
        while (line < LinesTotal() && !IsWhiteLine(line)) { // skip non-empty lines
 
938
                line++;
 
939
        }
 
940
        while (line < LinesTotal() && IsWhiteLine(line)) { // skip empty lines
 
941
                line++;
 
942
        }
 
943
        if (line < LinesTotal())
 
944
                return LineStart(line);
 
945
        else // end of a document
 
946
                return LineEnd(line-1);
 
947
}
 
948
 
 
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);
 
953
}
 
954
 
 
955
/**
 
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.
 
958
 */
 
959
int Document::ExtendWordSelect(int pos, int delta, bool onlyWordCharacters) {
 
960
        CharClassify::cc ccStart = CharClassify::ccWord;
 
961
        if (delta < 0) {
 
962
                if (!onlyWordCharacters)
 
963
                        ccStart = WordCharClass(cb.CharAt(pos-1));
 
964
                while (pos > 0 && (WordCharClass(cb.CharAt(pos - 1)) == ccStart))
 
965
                        pos--;
 
966
        } else {
 
967
                if (!onlyWordCharacters && pos < Length())
 
968
                        ccStart = WordCharClass(cb.CharAt(pos));
 
969
                while (pos < (Length()) && (WordCharClass(cb.CharAt(pos)) == ccStart))
 
970
                        pos++;
 
971
        }
 
972
        return MovePositionOutsideChar(pos, delta);
 
973
}
 
974
 
 
975
/**
 
976
 * Find the start of the next word in either a forward (delta >= 0) or backwards direction
 
977
 * (delta < 0).
 
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.
 
981
 */
 
982
int Document::NextWordStart(int pos, int delta) {
 
983
        if (delta < 0) {
 
984
                while (pos > 0 && (WordCharClass(cb.CharAt(pos - 1)) == CharClassify::ccSpace))
 
985
                        pos--;
 
986
                if (pos > 0) {
 
987
                        CharClassify::cc ccStart = WordCharClass(cb.CharAt(pos-1));
 
988
                        while (pos > 0 && (WordCharClass(cb.CharAt(pos - 1)) == ccStart)) {
 
989
                                pos--;
 
990
                        }
 
991
                }
 
992
        } else {
 
993
                CharClassify::cc ccStart = WordCharClass(cb.CharAt(pos));
 
994
                while (pos < (Length()) && (WordCharClass(cb.CharAt(pos)) == ccStart))
 
995
                        pos++;
 
996
                while (pos < (Length()) && (WordCharClass(cb.CharAt(pos)) == CharClassify::ccSpace))
 
997
                        pos++;
 
998
        }
 
999
        return pos;
 
1000
}
 
1001
 
 
1002
/**
 
1003
 * Find the end of the next word in either a forward (delta >= 0) or backwards direction
 
1004
 * (delta < 0).
 
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.
 
1008
 */
 
1009
int Document::NextWordEnd(int pos, int delta) {
 
1010
        if (delta < 0) {
 
1011
                if (pos > 0) {
 
1012
                        CharClassify::cc ccStart = WordCharClass(cb.CharAt(pos-1));
 
1013
                        if (ccStart != CharClassify::ccSpace) {
 
1014
                                while (pos > 0 && WordCharClass(cb.CharAt(pos - 1)) == ccStart) {
 
1015
                                        pos--;
 
1016
                                }
 
1017
                        }
 
1018
                        while (pos > 0 && WordCharClass(cb.CharAt(pos - 1)) == CharClassify::ccSpace) {
 
1019
                                pos--;
 
1020
                        }
 
1021
                }
 
1022
        } else {
 
1023
                while (pos < Length() && WordCharClass(cb.CharAt(pos)) == CharClassify::ccSpace) {
 
1024
                        pos++;
 
1025
                }
 
1026
                if (pos < Length()) {
 
1027
                        CharClassify::cc ccStart = WordCharClass(cb.CharAt(pos));
 
1028
                        while (pos < Length() && WordCharClass(cb.CharAt(pos)) == ccStart) {
 
1029
                                pos++;
 
1030
                        }
 
1031
                }
 
1032
        }
 
1033
        return pos;
 
1034
}
 
1035
 
 
1036
/**
 
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.
 
1039
 */
 
1040
bool Document::IsWordStartAt(int pos) {
 
1041
        if (pos > 0) {
 
1042
                CharClassify::cc ccPos = WordCharClass(CharAt(pos));
 
1043
                return (ccPos == CharClassify::ccWord || ccPos == CharClassify::ccPunctuation) &&
 
1044
                        (ccPos != WordCharClass(CharAt(pos - 1)));
 
1045
        }
 
1046
        return true;
 
1047
}
 
1048
 
 
1049
/**
 
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.
 
1052
 */
 
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)));
 
1058
        }
 
1059
        return true;
 
1060
}
 
1061
 
 
1062
/**
 
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.
 
1065
 */
 
1066
bool Document::IsWordAt(int start, int end) {
 
1067
        return IsWordStartAt(start) && IsWordEndAt(end);
 
1068
}
 
1069
 
 
1070
static inline char MakeLowerCase(char ch) {
 
1071
        if (ch < 'A' || ch > 'Z')
 
1072
                return ch;
 
1073
        else
 
1074
                return static_cast<char>(ch - 'A' + 'a');
 
1075
}
 
1076
 
 
1077
/**
 
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.
 
1081
 */
 
1082
long Document::FindText(int minPos, int maxPos, const char *s,
 
1083
                        bool caseSensitive, bool word, bool wordStart, bool regExp, int flags,
 
1084
                        int *length) {
 
1085
        if (regExp) {
 
1086
                if (!regex)
 
1087
                        regex = CreateRegexSearch(&charClass);
 
1088
                return regex->FindText(this, minPos, maxPos, s, caseSensitive, word, wordStart, flags, length);
 
1089
        } else {
 
1090
 
 
1091
                bool forward = minPos <= maxPos;
 
1092
                int increment = forward ? 1 : -1;
 
1093
 
 
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);
 
1097
 
 
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;
 
1105
                }
 
1106
                //Platform::DebugPrintf("Find %d %d %s %d\n", startPos, endPos, ft->lpstrText, lengthFind);
 
1107
                char firstChar = s[0];
 
1108
                if (!caseSensitive)
 
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) {
 
1115
                                        bool found = true;
 
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])
 
1120
                                                        found = false;
 
1121
                                        }
 
1122
                                        if (found) {
 
1123
                                                if ((!word && !wordStart) ||
 
1124
                                                        (word && IsWordAt(pos, pos + lengthFind)) ||
 
1125
                                                        (wordStart && IsWordStartAt(pos)))
 
1126
                                                        return pos;
 
1127
                                        }
 
1128
                                }
 
1129
                        } else {
 
1130
                                if (MakeUpperCase(ch) == firstChar) {
 
1131
                                        bool found = true;
 
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]))
 
1136
                                                        found = false;
 
1137
                                        }
 
1138
                                        if (found) {
 
1139
                                                if ((!word && !wordStart) ||
 
1140
                                                        (word && IsWordAt(pos, pos + lengthFind)) ||
 
1141
                                                        (wordStart && IsWordStartAt(pos)))
 
1142
                                                        return pos;
 
1143
                                        }
 
1144
                                }
 
1145
                        }
 
1146
                        pos += increment;
 
1147
                        if (dbcsCodePage && (pos >= 0)) {
 
1148
                                // Ensure trying to match from start of character
 
1149
                                pos = MovePositionOutsideChar(pos, increment, false);
 
1150
                        }
 
1151
                }
 
1152
        }
 
1153
        //Platform::DebugPrintf("Not found\n");
 
1154
        return -1;
 
1155
}
 
1156
 
 
1157
const char *Document::SubstituteByPosition(const char *text, int *length) {
 
1158
        return regex->SubstituteByPosition(this, text, length);
 
1159
}
 
1160
 
 
1161
int Document::LinesTotal() const {
 
1162
        return cb.Lines();
 
1163
}
 
1164
 
 
1165
void Document::ChangeCase(Range r, bool makeUpperCase) {
 
1166
        for (int pos = r.start; pos < r.end;) {
 
1167
                int len = LenChar(pos);
 
1168
                if (len == 1) {
 
1169
                        char ch = CharAt(pos);
 
1170
                        if (makeUpperCase) {
 
1171
                                if (IsLowerCase(ch)) {
 
1172
                                        ChangeChar(pos, static_cast<char>(MakeUpperCase(ch)));
 
1173
                                }
 
1174
                        } else {
 
1175
                                if (IsUpperCase(ch)) {
 
1176
                                        ChangeChar(pos, static_cast<char>(MakeLowerCase(ch)));
 
1177
                                }
 
1178
                        }
 
1179
                }
 
1180
                pos += len;
 
1181
        }
 
1182
}
 
1183
 
 
1184
void Document::SetDefaultCharClasses(bool includeWordClass) {
 
1185
    charClass.SetDefaultCharClasses(includeWordClass);
 
1186
}
 
1187
 
 
1188
void Document::SetCharClasses(const unsigned char *chars, CharClassify::cc newCharClass) {
 
1189
    charClass.SetCharClasses(chars, newCharClass);
 
1190
}
 
1191
 
 
1192
void Document::SetStylingBits(int bits) {
 
1193
        stylingBits = bits;
 
1194
        stylingBitsMask = (1 << stylingBits) - 1;
 
1195
}
 
1196
 
 
1197
void Document::StartStyling(int position, char mask) {
 
1198
        stylingMask = mask;
 
1199
        endStyled = position;
 
1200
}
 
1201
 
 
1202
bool Document::SetStyleFor(int length, char style) {
 
1203
        if (enteredStyling != 0) {
 
1204
                return false;
 
1205
        } else {
 
1206
                enteredStyling++;
 
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);
 
1212
                        NotifyModified(mh);
 
1213
                }
 
1214
                endStyled += length;
 
1215
                enteredStyling--;
 
1216
                return true;
 
1217
        }
 
1218
}
 
1219
 
 
1220
bool Document::SetStyles(int length, const char *styles) {
 
1221
        if (enteredStyling != 0) {
 
1222
                return false;
 
1223
        } else {
 
1224
                enteredStyling++;
 
1225
                bool didChange = false;
 
1226
                int startMod = 0;
 
1227
                int endMod = 0;
 
1228
                for (int iPos = 0; iPos < length; iPos++, endStyled++) {
 
1229
                        PLATFORM_ASSERT(endStyled < Length());
 
1230
                        if (cb.SetStyleAt(endStyled, styles[iPos], stylingMask)) {
 
1231
                                if (!didChange) {
 
1232
                                        startMod = endStyled;
 
1233
                                }
 
1234
                                didChange = true;
 
1235
                                endMod = endStyled;
 
1236
                        }
 
1237
                }
 
1238
                if (didChange) {
 
1239
                        DocModification mh(SC_MOD_CHANGESTYLE | SC_PERFORMED_USER,
 
1240
                                           startMod, endMod - startMod + 1);
 
1241
                        NotifyModified(mh);
 
1242
                }
 
1243
                enteredStyling--;
 
1244
                return true;
 
1245
        }
 
1246
}
 
1247
 
 
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);
 
1254
                }
 
1255
        }
 
1256
}
 
1257
 
 
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);
 
1262
                NotifyModified(mh);
 
1263
        }
 
1264
        return statePrevious;
 
1265
}
 
1266
 
 
1267
int Document::GetLineState(int line) { 
 
1268
        return static_cast<LineState*>(perLineData[ldState])->GetLineState(line); 
 
1269
}
 
1270
 
 
1271
int Document::GetMaxLineState() { 
 
1272
        return static_cast<LineState*>(perLineData[ldState])->GetMaxLineState(); 
 
1273
}
 
1274
 
 
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));
 
1279
}
 
1280
 
 
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);
 
1284
        NotifyModified(mh);
 
1285
}
 
1286
 
 
1287
void Document::MarginSetStyle(int line, int style) {
 
1288
        static_cast<LineAnnotation*>(perLineData[ldMargin])->SetStyle(line, style); 
 
1289
}
 
1290
 
 
1291
void Document::MarginSetStyles(int line, const unsigned char *styles) {
 
1292
        static_cast<LineAnnotation*>(perLineData[ldMargin])->SetStyles(line, styles); 
 
1293
}
 
1294
 
 
1295
int Document::MarginLength(int line) const {
 
1296
        return static_cast<LineAnnotation*>(perLineData[ldMargin])->Length(line);
 
1297
}
 
1298
 
 
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();
 
1305
}
 
1306
 
 
1307
bool Document::AnnotationAny() const {
 
1308
        return static_cast<LineAnnotation*>(perLineData[ldAnnotation])->AnySet(); 
 
1309
}
 
1310
 
 
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));
 
1315
}
 
1316
 
 
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;
 
1323
        NotifyModified(mh);
 
1324
}
 
1325
 
 
1326
void Document::AnnotationSetStyle(int line, int style) {
 
1327
        static_cast<LineAnnotation*>(perLineData[ldAnnotation])->SetStyle(line, style); 
 
1328
}
 
1329
 
 
1330
void Document::AnnotationSetStyles(int line, const unsigned char *styles) {
 
1331
        static_cast<LineAnnotation*>(perLineData[ldAnnotation])->SetStyles(line, styles); 
 
1332
}
 
1333
 
 
1334
int Document::AnnotationLength(int line) const {
 
1335
        return static_cast<LineAnnotation*>(perLineData[ldAnnotation])->Length(line);
 
1336
}
 
1337
 
 
1338
int Document::AnnotationLines(int line) const {
 
1339
        return static_cast<LineAnnotation*>(perLineData[ldAnnotation])->Lines(line);
 
1340
}
 
1341
 
 
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();
 
1348
}
 
1349
 
 
1350
void Document::IncrementStyleClock() {
 
1351
        styleClock = (styleClock + 1) % 0x100000;
 
1352
}
 
1353
 
 
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);
 
1358
                NotifyModified(mh);
 
1359
        }
 
1360
}
 
1361
 
 
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))
 
1366
                        return false;
 
1367
        }
 
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;
 
1373
        delete []watchers;
 
1374
        watchers = pwNew;
 
1375
        lenWatchers++;
 
1376
        return true;
 
1377
}
 
1378
 
 
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) {
 
1384
                                delete []watchers;
 
1385
                                watchers = 0;
 
1386
                                lenWatchers = 0;
 
1387
                        } else {
 
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];
 
1391
                                }
 
1392
                                delete []watchers;
 
1393
                                watchers = pwNew;
 
1394
                                lenWatchers--;
 
1395
                        }
 
1396
                        return true;
 
1397
                }
 
1398
        }
 
1399
        return false;
 
1400
}
 
1401
 
 
1402
void Document::NotifyModifyAttempt() {
 
1403
        for (int i = 0; i < lenWatchers; i++) {
 
1404
                watchers[i].watcher->NotifyModifyAttempt(this, watchers[i].userData);
 
1405
        }
 
1406
}
 
1407
 
 
1408
void Document::NotifySavePoint(bool atSavePoint) {
 
1409
        for (int i = 0; i < lenWatchers; i++) {
 
1410
                watchers[i].watcher->NotifySavePoint(this, watchers[i].userData, atSavePoint);
 
1411
        }
 
1412
}
 
1413
 
 
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);
 
1419
        }
 
1420
        for (int i = 0; i < lenWatchers; i++) {
 
1421
                watchers[i].watcher->NotifyModified(this, mh, watchers[i].userData);
 
1422
        }
 
1423
}
 
1424
 
 
1425
bool Document::IsWordPartSeparator(char ch) {
 
1426
        return (WordCharClass(ch) == CharClassify::ccWord) && IsPunctuation(ch);
 
1427
}
 
1428
 
 
1429
int Document::WordPartLeft(int pos) {
 
1430
        if (pos > 0) {
 
1431
                --pos;
 
1432
                char startChar = cb.CharAt(pos);
 
1433
                if (IsWordPartSeparator(startChar)) {
 
1434
                        while (pos > 0 && IsWordPartSeparator(cb.CharAt(pos))) {
 
1435
                                --pos;
 
1436
                        }
 
1437
                }
 
1438
                if (pos > 0) {
 
1439
                        startChar = cb.CharAt(pos);
 
1440
                        --pos;
 
1441
                        if (IsLowerCase(startChar)) {
 
1442
                                while (pos > 0 && IsLowerCase(cb.CharAt(pos)))
 
1443
                                        --pos;
 
1444
                                if (!IsUpperCase(cb.CharAt(pos)) && !IsLowerCase(cb.CharAt(pos)))
 
1445
                                        ++pos;
 
1446
                        } else if (IsUpperCase(startChar)) {
 
1447
                                while (pos > 0 && IsUpperCase(cb.CharAt(pos)))
 
1448
                                        --pos;
 
1449
                                if (!IsUpperCase(cb.CharAt(pos)))
 
1450
                                        ++pos;
 
1451
                        } else if (IsADigit(startChar)) {
 
1452
                                while (pos > 0 && IsADigit(cb.CharAt(pos)))
 
1453
                                        --pos;
 
1454
                                if (!IsADigit(cb.CharAt(pos)))
 
1455
                                        ++pos;
 
1456
                        } else if (IsPunctuation(startChar)) {
 
1457
                                while (pos > 0 && IsPunctuation(cb.CharAt(pos)))
 
1458
                                        --pos;
 
1459
                                if (!IsPunctuation(cb.CharAt(pos)))
 
1460
                                        ++pos;
 
1461
                        } else if (isspacechar(startChar)) {
 
1462
                                while (pos > 0 && isspacechar(cb.CharAt(pos)))
 
1463
                                        --pos;
 
1464
                                if (!isspacechar(cb.CharAt(pos)))
 
1465
                                        ++pos;
 
1466
                        } else if (!isascii(startChar)) {
 
1467
                                while (pos > 0 && !isascii(cb.CharAt(pos)))
 
1468
                                        --pos;
 
1469
                                if (isascii(cb.CharAt(pos)))
 
1470
                                        ++pos;
 
1471
                        } else {
 
1472
                                ++pos;
 
1473
                        }
 
1474
                }
 
1475
        }
 
1476
        return pos;
 
1477
}
 
1478
 
 
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)))
 
1484
                        ++pos;
 
1485
                startChar = cb.CharAt(pos);
 
1486
        }
 
1487
        if (!isascii(startChar)) {
 
1488
                while (pos < length && !isascii(cb.CharAt(pos)))
 
1489
                        ++pos;
 
1490
        } else if (IsLowerCase(startChar)) {
 
1491
                while (pos < length && IsLowerCase(cb.CharAt(pos)))
 
1492
                        ++pos;
 
1493
        } else if (IsUpperCase(startChar)) {
 
1494
                if (IsLowerCase(cb.CharAt(pos + 1))) {
 
1495
                        ++pos;
 
1496
                        while (pos < length && IsLowerCase(cb.CharAt(pos)))
 
1497
                                ++pos;
 
1498
                } else {
 
1499
                        while (pos < length && IsUpperCase(cb.CharAt(pos)))
 
1500
                                ++pos;
 
1501
                }
 
1502
                if (IsLowerCase(cb.CharAt(pos)) && IsUpperCase(cb.CharAt(pos - 1)))
 
1503
                        --pos;
 
1504
        } else if (IsADigit(startChar)) {
 
1505
                while (pos < length && IsADigit(cb.CharAt(pos)))
 
1506
                        ++pos;
 
1507
        } else if (IsPunctuation(startChar)) {
 
1508
                while (pos < length && IsPunctuation(cb.CharAt(pos)))
 
1509
                        ++pos;
 
1510
        } else if (isspacechar(startChar)) {
 
1511
                while (pos < length && isspacechar(cb.CharAt(pos)))
 
1512
                        ++pos;
 
1513
        } else {
 
1514
                ++pos;
 
1515
        }
 
1516
        return pos;
 
1517
}
 
1518
 
 
1519
bool IsLineEndChar(char c) {
 
1520
        return (c == '\n' || c == '\r');
 
1521
}
 
1522
 
 
1523
int Document::ExtendStyleRange(int pos, int delta, bool singleLine) {
 
1524
        int sStart = cb.StyleAt(pos);
 
1525
        if (delta < 0) {
 
1526
                while (pos > 0 && (cb.StyleAt(pos) == sStart) && (!singleLine || !IsLineEndChar(cb.CharAt(pos))) )
 
1527
                        pos--;
 
1528
                pos++;
 
1529
        } else {
 
1530
                while (pos < (Length()) && (cb.StyleAt(pos) == sStart) && (!singleLine || !IsLineEndChar(cb.CharAt(pos))) )
 
1531
                        pos++;
 
1532
        }
 
1533
        return pos;
 
1534
}
 
1535
 
 
1536
static char BraceOpposite(char ch) {
 
1537
        switch (ch) {
 
1538
        case '(':
 
1539
                return ')';
 
1540
        case ')':
 
1541
                return '(';
 
1542
        case '[':
 
1543
                return ']';
 
1544
        case ']':
 
1545
                return '[';
 
1546
        case '{':
 
1547
                return '}';
 
1548
        case '}':
 
1549
                return '{';
 
1550
        case '<':
 
1551
                return '>';
 
1552
        case '>':
 
1553
                return '<';
 
1554
        default:
 
1555
                return '\0';
 
1556
        }
 
1557
}
 
1558
 
 
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);
 
1563
        if (chSeek == '\0')
 
1564
                return - 1;
 
1565
        char styBrace = static_cast<char>(StyleAt(position) & stylingBitsMask);
 
1566
        int direction = -1;
 
1567
        if (chBrace == '(' || chBrace == '[' || chBrace == '{' || chBrace == '<')
 
1568
                direction = 1;
 
1569
        int depth = 1;
 
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)
 
1577
                                depth++;
 
1578
                        if (chAtPos == chSeek)
 
1579
                                depth--;
 
1580
                        if (depth == 0)
 
1581
                                return position;
 
1582
                }
 
1583
                position = position + direction;
 
1584
        }
 
1585
        return - 1;
 
1586
}
 
1587
 
 
1588
/**
 
1589
 * Implementation of RegexSearchBase for the default built-in regular expression engine
 
1590
 */
 
1591
class BuiltinRegex : public RegexSearchBase {
 
1592
public:
 
1593
        BuiltinRegex(CharClassify *charClassTable) : search(charClassTable), substituted(NULL) {}
 
1594
 
 
1595
        virtual ~BuiltinRegex() {
 
1596
                delete substituted;
 
1597
        }
 
1598
 
 
1599
        virtual long FindText(Document *doc, int minPos, int maxPos, const char *s,
 
1600
                        bool caseSensitive, bool word, bool wordStart, int flags,
 
1601
                        int *length);
 
1602
 
 
1603
        virtual const char *SubstituteByPosition(Document* doc, const char *text, int *length);
 
1604
 
 
1605
private:
 
1606
        RESearch search;
 
1607
        char *substituted;
 
1608
};
 
1609
 
 
1610
// Define a way for the Regular Expression code to access the document
 
1611
class DocumentIndexer : public CharacterIndexer {
 
1612
        Document *pdoc;
 
1613
        int end;
 
1614
public:
 
1615
        DocumentIndexer(Document *pdoc_, int end_) :
 
1616
                pdoc(pdoc_), end(end_) {
 
1617
        }
 
1618
 
 
1619
        virtual ~DocumentIndexer() {
 
1620
        }
 
1621
 
 
1622
        virtual char CharAt(int index) {
 
1623
                if (index < 0 || index >= end)
 
1624
                        return 0;
 
1625
                else
 
1626
                        return pdoc->CharAt(index);
 
1627
        }
 
1628
};
 
1629
 
 
1630
long BuiltinRegex::FindText(Document *doc, int minPos, int maxPos, const char *s,
 
1631
                        bool caseSensitive, bool, bool, int flags,
 
1632
                        int *length) {
 
1633
        bool posix = (flags & SCFIND_POSIX) != 0;
 
1634
        int increment = (minPos <= maxPos) ? 1 : -1;
 
1635
 
 
1636
        int startPos = minPos;
 
1637
        int endPos = maxPos;
 
1638
 
 
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);
 
1642
 
 
1643
        const char *errmsg = search.Compile(s, *length, caseSensitive, posix);
 
1644
        if (errmsg) {
 
1645
                return -1;
 
1646
        }
 
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.
 
1657
                lineRangeStart++;
 
1658
                startPos = doc->LineStart(lineRangeStart);
 
1659
        }
 
1660
        int pos = -1;
 
1661
        int lenRet = 0;
 
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;
 
1672
                        }
 
1673
                        if (line == lineRangeEnd) {
 
1674
                                if ((endPos != endOfLine) && (searchEnd == '$'))
 
1675
                                        continue;       // Can't match end of line if end position before end of line
 
1676
                                endOfLine = endPos;
 
1677
                        }
 
1678
                } else {
 
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;
 
1683
                        }
 
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;
 
1688
                        }
 
1689
                }
 
1690
 
 
1691
                DocumentIndexer di(doc, endOfLine);
 
1692
                int success = search.Execute(di, startOfLine, endOfLine);
 
1693
                if (success) {
 
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);
 
1701
                                        if (success) {
 
1702
                                                if (search.eopat[0] <= minPos) {
 
1703
                                                        pos = search.bopat[0];
 
1704
                                                        lenRet = search.eopat[0] - search.bopat[0];
 
1705
                                                } else {
 
1706
                                                        success = 0;
 
1707
                                                }
 
1708
                                        }
 
1709
                                }
 
1710
                        }
 
1711
                        break;
 
1712
                }
 
1713
        }
 
1714
        *length = lenRet;
 
1715
        return pos;
 
1716
}
 
1717
 
 
1718
const char *BuiltinRegex::SubstituteByPosition(Document* doc, const char *text, int *length) {
 
1719
        delete []substituted;
 
1720
        substituted = 0;
 
1721
        DocumentIndexer di(doc, doc->Length());
 
1722
        if (!search.GrabMatches(di))
 
1723
                return 0;
 
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];
 
1730
                                i++;
 
1731
                        } else {
 
1732
                                switch (text[i + 1]) {
 
1733
                                case 'a':
 
1734
                                case 'b':
 
1735
                                case 'f':
 
1736
                                case 'n':
 
1737
                                case 'r':
 
1738
                                case 't':
 
1739
                                case 'v':
 
1740
                                        i++;
 
1741
                                }
 
1742
                                lenResult++;
 
1743
                        }
 
1744
                } else {
 
1745
                        lenResult++;
 
1746
                }
 
1747
        }
 
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);
 
1757
                                o += len;
 
1758
                                j++;
 
1759
                        } else {
 
1760
                                j++;
 
1761
                                switch (text[j]) {
 
1762
                                case 'a':
 
1763
                                        *o++ = '\a';
 
1764
                                        break;
 
1765
                                case 'b':
 
1766
                                        *o++ = '\b';
 
1767
                                        break;
 
1768
                                case 'f':
 
1769
                                        *o++ = '\f';
 
1770
                                        break;
 
1771
                                case 'n':
 
1772
                                        *o++ = '\n';
 
1773
                                        break;
 
1774
                                case 'r':
 
1775
                                        *o++ = '\r';
 
1776
                                        break;
 
1777
                                case 't':
 
1778
                                        *o++ = '\t';
 
1779
                                        break;
 
1780
                                case 'v':
 
1781
                                        *o++ = '\v';
 
1782
                                        break;
 
1783
                                default:
 
1784
                                        *o++ = '\\';
 
1785
                                        j--;
 
1786
                                }
 
1787
                        }
 
1788
                } else {
 
1789
                        *o++ = text[j];
 
1790
                }
 
1791
        }
 
1792
        *o = '\0';
 
1793
        *length = lenResult;
 
1794
        return substituted;
 
1795
}
 
1796
 
 
1797
#ifndef SCI_OWNREGEX
 
1798
 
 
1799
#ifdef SCI_NAMESPACE
 
1800
 
 
1801
RegexSearchBase *Scintilla::CreateRegexSearch(CharClassify *charClassTable) {
 
1802
        return new BuiltinRegex(charClassTable);
 
1803
}
 
1804
 
 
1805
#else
 
1806
 
 
1807
RegexSearchBase *CreateRegexSearch(CharClassify *charClassTable) {
 
1808
        return new BuiltinRegex(charClassTable);
 
1809
}
 
1810
 
 
1811
#endif
 
1812
 
 
1813
#endif