~ubuntu-branches/ubuntu/utopic/geany/utopic

« back to all changes in this revision

Viewing changes to scintilla/src/Document.cxx

  • Committer: Package Import Robot
  • Author(s): Chow Loong Jin
  • Date: 2011-12-10 07:43:26 UTC
  • mfrom: (3.3.7 sid)
  • Revision ID: package-import@ubuntu.com-20111210074326-s8yqbew5i20h33tf
Tags: 0.21-1ubuntu1
* Merge from Debian Unstable, remaining changes:
  - debian/patches/20_use_evince_viewer.patch:
     + use evince as viewer for pdf and dvi files
  - debian/patches/20_use_x_terminal_emulator.patch:
     + use x-terminal-emulator as terminal
  - debian/control
     + Add breaks on geany-plugins-common << 0.20
* Also fixes bugs:
  - Filter for MATLAB/Octave files filters everythign (LP: 885505)

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
#include <assert.h>
 
13
 
 
14
#include <string>
 
15
#include <vector>
 
16
 
 
17
#include "Platform.h"
 
18
 
 
19
#include "ILexer.h"
 
20
#include "Scintilla.h"
 
21
 
 
22
#include "SplitVector.h"
 
23
#include "Partitioning.h"
 
24
#include "RunStyles.h"
 
25
#include "CellBuffer.h"
 
26
#include "PerLine.h"
 
27
#include "CharClassify.h"
 
28
#include "CharacterSet.h"
 
29
#include "Decoration.h"
 
30
#include "Document.h"
 
31
#include "RESearch.h"
 
32
#include "UniConversion.h"
 
33
 
 
34
#ifdef SCI_NAMESPACE
 
35
using namespace Scintilla;
 
36
#endif
 
37
 
 
38
// This is ASCII specific but is safe with chars >= 0x80
 
39
static inline bool isspacechar(unsigned char ch) {
 
40
        return (ch == ' ') || ((ch >= 0x09) && (ch <= 0x0d));
 
41
}
 
42
 
 
43
static inline bool IsPunctuation(char ch) {
 
44
        return isascii(ch) && ispunct(ch);
 
45
}
 
46
 
 
47
static inline bool IsADigit(char ch) {
 
48
        return isascii(ch) && isdigit(ch);
 
49
}
 
50
 
 
51
static inline bool IsLowerCase(char ch) {
 
52
        return isascii(ch) && islower(ch);
 
53
}
 
54
 
 
55
static inline bool IsUpperCase(char ch) {
 
56
        return isascii(ch) && isupper(ch);
 
57
}
 
58
 
 
59
void LexInterface::Colourise(int start, int end) {
 
60
        ElapsedTime et;
 
61
        if (pdoc && instance && !performingStyle) {
 
62
                // Protect against reentrance, which may occur, for example, when
 
63
                // fold points are discovered while performing styling and the folding
 
64
                // code looks for child lines which may trigger styling.
 
65
                performingStyle = true;
 
66
 
 
67
                int lengthDoc = pdoc->Length();
 
68
                if (end == -1)
 
69
                        end = lengthDoc;
 
70
                int len = end - start;
 
71
 
 
72
                PLATFORM_ASSERT(len >= 0);
 
73
                PLATFORM_ASSERT(start + len <= lengthDoc);
 
74
 
 
75
                int styleStart = 0;
 
76
                if (start > 0)
 
77
                        styleStart = pdoc->StyleAt(start - 1) & pdoc->stylingBitsMask;
 
78
 
 
79
                if (len > 0) {
 
80
                        instance->Lex(start, len, styleStart, pdoc);
 
81
                        instance->Fold(start, len, styleStart, pdoc);
 
82
                }
 
83
 
 
84
                performingStyle = false;
 
85
        }
 
86
}
 
87
 
 
88
Document::Document() {
 
89
        refCount = 0;
 
90
#ifdef __unix__
 
91
        eolMode = SC_EOL_LF;
 
92
#else
 
93
        eolMode = SC_EOL_CRLF;
 
94
#endif
 
95
        dbcsCodePage = 0;
 
96
        stylingBits = 5;
 
97
        stylingBitsMask = 0x1F;
 
98
        stylingMask = 0;
 
99
        endStyled = 0;
 
100
        styleClock = 0;
 
101
        enteredModification = 0;
 
102
        enteredStyling = 0;
 
103
        enteredReadOnlyCount = 0;
 
104
        tabInChars = 8;
 
105
        indentInChars = 0;
 
106
        actualIndentInChars = 8;
 
107
        useTabs = true;
 
108
        tabIndents = true;
 
109
        backspaceUnindents = false;
 
110
        watchers = 0;
 
111
        lenWatchers = 0;
 
112
 
 
113
        matchesValid = false;
 
114
        regex = 0;
 
115
 
 
116
        perLineData[ldMarkers] = new LineMarkers();
 
117
        perLineData[ldLevels] = new LineLevels();
 
118
        perLineData[ldState] = new LineState();
 
119
        perLineData[ldMargin] = new LineAnnotation();
 
120
        perLineData[ldAnnotation] = new LineAnnotation();
 
121
 
 
122
        cb.SetPerLine(this);
 
123
 
 
124
        pli = 0;
 
125
}
 
126
 
 
127
Document::~Document() {
 
128
        for (int i = 0; i < lenWatchers; i++) {
 
129
                watchers[i].watcher->NotifyDeleted(this, watchers[i].userData);
 
130
        }
 
131
        delete []watchers;
 
132
        for (int j=0; j<ldSize; j++) {
 
133
                delete perLineData[j];
 
134
                perLineData[j] = 0;
 
135
        }
 
136
        watchers = 0;
 
137
        lenWatchers = 0;
 
138
        delete regex;
 
139
        regex = 0;
 
140
        delete pli;
 
141
        pli = 0;
 
142
}
 
143
 
 
144
void Document::Init() {
 
145
        for (int j=0; j<ldSize; j++) {
 
146
                if (perLineData[j])
 
147
                        perLineData[j]->Init();
 
148
        }
 
149
}
 
150
 
 
151
void Document::InsertLine(int line) {
 
152
        for (int j=0; j<ldSize; j++) {
 
153
                if (perLineData[j])
 
154
                        perLineData[j]->InsertLine(line);
 
155
        }
 
156
}
 
157
 
 
158
void Document::RemoveLine(int line) {
 
159
        for (int j=0; j<ldSize; j++) {
 
160
                if (perLineData[j])
 
161
                        perLineData[j]->RemoveLine(line);
 
162
        }
 
163
}
 
164
 
 
165
// Increase reference count and return its previous value.
 
166
int Document::AddRef() {
 
167
        return refCount++;
 
168
}
 
169
 
 
170
// Decrease reference count and return its previous value.
 
171
// Delete the document if reference count reaches zero.
 
172
int Document::Release() {
 
173
        int curRefCount = --refCount;
 
174
        if (curRefCount == 0)
 
175
                delete this;
 
176
        return curRefCount;
 
177
}
 
178
 
 
179
void Document::SetSavePoint() {
 
180
        cb.SetSavePoint();
 
181
        NotifySavePoint(true);
 
182
}
 
183
 
 
184
int Document::GetMark(int line) {
 
185
        return static_cast<LineMarkers *>(perLineData[ldMarkers])->MarkValue(line);
 
186
}
 
187
 
 
188
int Document::AddMark(int line, int markerNum) {
 
189
        if (line >= 0 && line <= LinesTotal()) {
 
190
                int prev = static_cast<LineMarkers *>(perLineData[ldMarkers])->
 
191
                        AddMark(line, markerNum, LinesTotal());
 
192
                DocModification mh(SC_MOD_CHANGEMARKER, LineStart(line), 0, 0, 0, line);
 
193
                NotifyModified(mh);
 
194
                return prev;
 
195
        } else {
 
196
                return 0;
 
197
        }
 
198
}
 
199
 
 
200
void Document::AddMarkSet(int line, int valueSet) {
 
201
        if (line < 0 || line > LinesTotal()) {
 
202
                return;
 
203
        }
 
204
        unsigned int m = valueSet;
 
205
        for (int i = 0; m; i++, m >>= 1)
 
206
                if (m & 1)
 
207
                        static_cast<LineMarkers *>(perLineData[ldMarkers])->
 
208
                                AddMark(line, i, LinesTotal());
 
209
        DocModification mh(SC_MOD_CHANGEMARKER, LineStart(line), 0, 0, 0, line);
 
210
        NotifyModified(mh);
 
211
}
 
212
 
 
213
void Document::DeleteMark(int line, int markerNum) {
 
214
        static_cast<LineMarkers *>(perLineData[ldMarkers])->DeleteMark(line, markerNum, false);
 
215
        DocModification mh(SC_MOD_CHANGEMARKER, LineStart(line), 0, 0, 0, line);
 
216
        NotifyModified(mh);
 
217
}
 
218
 
 
219
void Document::DeleteMarkFromHandle(int markerHandle) {
 
220
        static_cast<LineMarkers *>(perLineData[ldMarkers])->DeleteMarkFromHandle(markerHandle);
 
221
        DocModification mh(SC_MOD_CHANGEMARKER, 0, 0, 0, 0);
 
222
        mh.line = -1;
 
223
        NotifyModified(mh);
 
224
}
 
225
 
 
226
void Document::DeleteAllMarks(int markerNum) {
 
227
        bool someChanges = false;
 
228
        for (int line = 0; line < LinesTotal(); line++) {
 
229
                if (static_cast<LineMarkers *>(perLineData[ldMarkers])->DeleteMark(line, markerNum, true))
 
230
                        someChanges = true;
 
231
        }
 
232
        if (someChanges) {
 
233
                DocModification mh(SC_MOD_CHANGEMARKER, 0, 0, 0, 0);
 
234
                mh.line = -1;
 
235
                NotifyModified(mh);
 
236
        }
 
237
}
 
238
 
 
239
int Document::LineFromHandle(int markerHandle) {
 
240
        return static_cast<LineMarkers *>(perLineData[ldMarkers])->LineFromHandle(markerHandle);
 
241
}
 
242
 
 
243
int SCI_METHOD Document::LineStart(int line) const {
 
244
        return cb.LineStart(line);
 
245
}
 
246
 
 
247
int Document::LineEnd(int line) const {
 
248
        if (line == LinesTotal() - 1) {
 
249
                return LineStart(line + 1);
 
250
        } else {
 
251
                int position = LineStart(line + 1) - 1;
 
252
                // When line terminator is CR+LF, may need to go back one more
 
253
                if ((position > LineStart(line)) && (cb.CharAt(position - 1) == '\r')) {
 
254
                        position--;
 
255
                }
 
256
                return position;
 
257
        }
 
258
}
 
259
 
 
260
void SCI_METHOD Document::SetErrorStatus(int status) {
 
261
        // Tell the watchers the lexer has changed.
 
262
        for (int i = 0; i < lenWatchers; i++) {
 
263
                watchers[i].watcher->NotifyErrorOccurred(this, watchers[i].userData, status);
 
264
        }
 
265
}
 
266
 
 
267
int SCI_METHOD Document::LineFromPosition(int pos) const {
 
268
        return cb.LineFromPosition(pos);
 
269
}
 
270
 
 
271
int Document::LineEndPosition(int position) const {
 
272
        return LineEnd(LineFromPosition(position));
 
273
}
 
274
 
 
275
bool Document::IsLineEndPosition(int position) const {
 
276
        return LineEnd(LineFromPosition(position)) == position;
 
277
}
 
278
 
 
279
int Document::VCHomePosition(int position) const {
 
280
        int line = LineFromPosition(position);
 
281
        int startPosition = LineStart(line);
 
282
        int endLine = LineEnd(line);
 
283
        int startText = startPosition;
 
284
        while (startText < endLine && (cb.CharAt(startText) == ' ' || cb.CharAt(startText) == '\t'))
 
285
                startText++;
 
286
        if (position == startText)
 
287
                return startPosition;
 
288
        else
 
289
                return startText;
 
290
}
 
291
 
 
292
int SCI_METHOD Document::SetLevel(int line, int level) {
 
293
        int prev = static_cast<LineLevels *>(perLineData[ldLevels])->SetLevel(line, level, LinesTotal());
 
294
        if (prev != level) {
 
295
                DocModification mh(SC_MOD_CHANGEFOLD | SC_MOD_CHANGEMARKER,
 
296
                                   LineStart(line), 0, 0, 0, line);
 
297
                mh.foldLevelNow = level;
 
298
                mh.foldLevelPrev = prev;
 
299
                NotifyModified(mh);
 
300
        }
 
301
        return prev;
 
302
}
 
303
 
 
304
int SCI_METHOD Document::GetLevel(int line) const {
 
305
        return static_cast<LineLevels *>(perLineData[ldLevels])->GetLevel(line);
 
306
}
 
307
 
 
308
void Document::ClearLevels() {
 
309
        static_cast<LineLevels *>(perLineData[ldLevels])->ClearLevels();
 
310
}
 
311
 
 
312
static bool IsSubordinate(int levelStart, int levelTry) {
 
313
        if (levelTry & SC_FOLDLEVELWHITEFLAG)
 
314
                return true;
 
315
        else
 
316
                return (levelStart & SC_FOLDLEVELNUMBERMASK) < (levelTry & SC_FOLDLEVELNUMBERMASK);
 
317
}
 
318
 
 
319
int Document::GetLastChild(int lineParent, int level) {
 
320
        if (level == -1)
 
321
                level = GetLevel(lineParent) & SC_FOLDLEVELNUMBERMASK;
 
322
        int maxLine = LinesTotal();
 
323
        int lineMaxSubord = lineParent;
 
324
        while (lineMaxSubord < maxLine - 1) {
 
325
                EnsureStyledTo(LineStart(lineMaxSubord + 2));
 
326
                if (!IsSubordinate(level, GetLevel(lineMaxSubord + 1)))
 
327
                        break;
 
328
                lineMaxSubord++;
 
329
        }
 
330
        if (lineMaxSubord > lineParent) {
 
331
                if (level > (GetLevel(lineMaxSubord + 1) & SC_FOLDLEVELNUMBERMASK)) {
 
332
                        // Have chewed up some whitespace that belongs to a parent so seek back
 
333
                        if (GetLevel(lineMaxSubord) & SC_FOLDLEVELWHITEFLAG) {
 
334
                                lineMaxSubord--;
 
335
                        }
 
336
                }
 
337
        }
 
338
        return lineMaxSubord;
 
339
}
 
340
 
 
341
int Document::GetFoldParent(int line) {
 
342
        int level = GetLevel(line) & SC_FOLDLEVELNUMBERMASK;
 
343
        int lineLook = line - 1;
 
344
        while ((lineLook > 0) && (
 
345
                    (!(GetLevel(lineLook) & SC_FOLDLEVELHEADERFLAG)) ||
 
346
                    ((GetLevel(lineLook) & SC_FOLDLEVELNUMBERMASK) >= level))
 
347
              ) {
 
348
                lineLook--;
 
349
        }
 
350
        if ((GetLevel(lineLook) & SC_FOLDLEVELHEADERFLAG) &&
 
351
                ((GetLevel(lineLook) & SC_FOLDLEVELNUMBERMASK) < level)) {
 
352
                return lineLook;
 
353
        } else {
 
354
                return -1;
 
355
        }
 
356
}
 
357
 
 
358
int Document::ClampPositionIntoDocument(int pos) {
 
359
        return Platform::Clamp(pos, 0, Length());
 
360
}
 
361
 
 
362
bool Document::IsCrLf(int pos) {
 
363
        if (pos < 0)
 
364
                return false;
 
365
        if (pos >= (Length() - 1))
 
366
                return false;
 
367
        return (cb.CharAt(pos) == '\r') && (cb.CharAt(pos + 1) == '\n');
 
368
}
 
369
 
 
370
int Document::LenChar(int pos) {
 
371
        if (pos < 0) {
 
372
                return 1;
 
373
        } else if (IsCrLf(pos)) {
 
374
                return 2;
 
375
        } else if (SC_CP_UTF8 == dbcsCodePage) {
 
376
                unsigned char ch = static_cast<unsigned char>(cb.CharAt(pos));
 
377
                if (ch < 0x80)
 
378
                        return 1;
 
379
                int len = 2;
 
380
                if (ch >= (0x80 + 0x40 + 0x20 + 0x10))
 
381
                        len = 4;
 
382
                else if (ch >= (0x80 + 0x40 + 0x20))
 
383
                        len = 3;
 
384
                int lengthDoc = Length();
 
385
                if ((pos + len) > lengthDoc)
 
386
                        return lengthDoc -pos;
 
387
                else
 
388
                        return len;
 
389
        } else if (dbcsCodePage) {
 
390
                return IsDBCSLeadByte(cb.CharAt(pos)) ? 2 : 1;
 
391
        } else {
 
392
                return 1;
 
393
        }
 
394
}
 
395
 
 
396
static bool IsTrailByte(int ch) {
 
397
        return (ch >= 0x80) && (ch < (0x80 + 0x40));
 
398
}
 
399
 
 
400
static int BytesFromLead(int leadByte) {
 
401
        if (leadByte > 0xF4) {
 
402
                // Characters longer than 4 bytes not possible in current UTF-8
 
403
                return 0;
 
404
        } else if (leadByte >= 0xF0) {
 
405
                return 4;
 
406
        } else if (leadByte >= 0xE0) {
 
407
                return 3;
 
408
        } else if (leadByte >= 0xC2) {
 
409
                return 2;
 
410
        }
 
411
        return 0;
 
412
}
 
413
 
 
414
bool Document::InGoodUTF8(int pos, int &start, int &end) const {
 
415
        int lead = pos;
 
416
        while ((lead>0) && (pos-lead < 4) && IsTrailByte(static_cast<unsigned char>(cb.CharAt(lead-1))))
 
417
                lead--;
 
418
        start = 0;
 
419
        if (lead > 0) {
 
420
                start = lead-1;
 
421
        }
 
422
        int leadByte = static_cast<unsigned char>(cb.CharAt(start));
 
423
        int bytes = BytesFromLead(leadByte);
 
424
        if (bytes == 0) {
 
425
                return false;
 
426
        } else {
 
427
                int trailBytes = bytes - 1;
 
428
                int len = pos - lead + 1;
 
429
                if (len > trailBytes)
 
430
                        // pos too far from lead
 
431
                        return false;
 
432
                // Check that there are enough trails for this lead
 
433
                int trail = pos + 1;
 
434
                while ((trail-lead<trailBytes) && (trail < Length())) {
 
435
                        if (!IsTrailByte(static_cast<unsigned char>(cb.CharAt(trail)))) {
 
436
                                return false;
 
437
                        }
 
438
                        trail++;
 
439
                }
 
440
                end = start + bytes;
 
441
                return true;
 
442
        }
 
443
}
 
444
 
 
445
// Normalise a position so that it is not halfway through a two byte character.
 
446
// This can occur in two situations -
 
447
// When lines are terminated with \r\n pairs which should be treated as one character.
 
448
// When displaying DBCS text such as Japanese.
 
449
// If moving, move the position in the indicated direction.
 
450
int Document::MovePositionOutsideChar(int pos, int moveDir, bool checkLineEnd) {
 
451
        //Platform::DebugPrintf("NoCRLF %d %d\n", pos, moveDir);
 
452
        // If out of range, just return minimum/maximum value.
 
453
        if (pos <= 0)
 
454
                return 0;
 
455
        if (pos >= Length())
 
456
                return Length();
 
457
 
 
458
        // PLATFORM_ASSERT(pos > 0 && pos < Length());
 
459
        if (checkLineEnd && IsCrLf(pos - 1)) {
 
460
                if (moveDir > 0)
 
461
                        return pos + 1;
 
462
                else
 
463
                        return pos - 1;
 
464
        }
 
465
 
 
466
        if (dbcsCodePage) {
 
467
                if (SC_CP_UTF8 == dbcsCodePage) {
 
468
                        unsigned char ch = static_cast<unsigned char>(cb.CharAt(pos));
 
469
                        int startUTF = pos;
 
470
                        int endUTF = pos;
 
471
                        if (IsTrailByte(ch) && InGoodUTF8(pos, startUTF, endUTF)) {
 
472
                                // ch is a trail byte within a UTF-8 character
 
473
                                if (moveDir > 0)
 
474
                                        pos = endUTF;
 
475
                                else
 
476
                                        pos = startUTF;
 
477
                        }
 
478
                } else {
 
479
                        // Anchor DBCS calculations at start of line because start of line can
 
480
                        // not be a DBCS trail byte.
 
481
                        int posStartLine = LineStart(LineFromPosition(pos));
 
482
                        if (pos == posStartLine)
 
483
                                return pos;
 
484
 
 
485
                        // Step back until a non-lead-byte is found.
 
486
                        int posCheck = pos;
 
487
                        while ((posCheck > posStartLine) && IsDBCSLeadByte(cb.CharAt(posCheck-1)))
 
488
                                posCheck--;
 
489
 
 
490
                        // Check from known start of character.
 
491
                        while (posCheck < pos) {
 
492
                                int mbsize = IsDBCSLeadByte(cb.CharAt(posCheck)) ? 2 : 1;
 
493
                                if (posCheck + mbsize == pos) {
 
494
                                        return pos;
 
495
                                } else if (posCheck + mbsize > pos) {
 
496
                                        if (moveDir > 0) {
 
497
                                                return posCheck + mbsize;
 
498
                                        } else {
 
499
                                                return posCheck;
 
500
                                        }
 
501
                                }
 
502
                                posCheck += mbsize;
 
503
                        }
 
504
                }
 
505
        }
 
506
 
 
507
        return pos;
 
508
}
 
509
 
 
510
// NextPosition moves between valid positions - it can not handle a position in the middle of a
 
511
// multi-byte character. It is used to iterate through text more efficiently than MovePositionOutsideChar.
 
512
// A \r\n pair is treated as two characters.
 
513
int Document::NextPosition(int pos, int moveDir) const {
 
514
        // If out of range, just return minimum/maximum value.
 
515
        int increment = (moveDir > 0) ? 1 : -1;
 
516
        if (pos + increment <= 0)
 
517
                return 0;
 
518
        if (pos + increment >= Length())
 
519
                return Length();
 
520
 
 
521
        if (dbcsCodePage) {
 
522
                if (SC_CP_UTF8 == dbcsCodePage) {
 
523
                        pos += increment;
 
524
                        unsigned char ch = static_cast<unsigned char>(cb.CharAt(pos));
 
525
                        int startUTF = pos;
 
526
                        int endUTF = pos;
 
527
                        if (IsTrailByte(ch) && InGoodUTF8(pos, startUTF, endUTF)) {
 
528
                                // ch is a trail byte within a UTF-8 character
 
529
                                if (moveDir > 0)
 
530
                                        pos = endUTF;
 
531
                                else
 
532
                                        pos = startUTF;
 
533
                        }
 
534
                } else {
 
535
                        if (moveDir > 0) {
 
536
                                int mbsize = IsDBCSLeadByte(cb.CharAt(pos)) ? 2 : 1;
 
537
                                pos += mbsize;
 
538
                                if (pos > Length())
 
539
                                        pos = Length();
 
540
                        } else {
 
541
                                // Anchor DBCS calculations at start of line because start of line can
 
542
                                // not be a DBCS trail byte.
 
543
                                int posStartLine = LineStart(LineFromPosition(pos));
 
544
                                // See http://msdn.microsoft.com/en-us/library/cc194792%28v=MSDN.10%29.aspx
 
545
                                // http://msdn.microsoft.com/en-us/library/cc194790.aspx
 
546
                                if ((pos - 1) <= posStartLine) {
 
547
                                        return pos - 1;
 
548
                                } else if (IsDBCSLeadByte(cb.CharAt(pos - 1))) {
 
549
                                        // Must actually be trail byte
 
550
                                        return pos - 2;
 
551
                                } else {
 
552
                                        // Otherwise, step back until a non-lead-byte is found.
 
553
                                        int posTemp = pos - 1;
 
554
                                        while (posStartLine <= --posTemp && IsDBCSLeadByte(cb.CharAt(posTemp)))
 
555
                                                ;
 
556
                                        // Now posTemp+1 must point to the beginning of a character,
 
557
                                        // so figure out whether we went back an even or an odd
 
558
                                        // number of bytes and go back 1 or 2 bytes, respectively.
 
559
                                        return (pos - 1 - ((pos - posTemp) & 1));
 
560
                                }
 
561
                        }
 
562
                }
 
563
        } else {
 
564
                pos += increment;
 
565
        }
 
566
 
 
567
        return pos;
 
568
}
 
569
 
 
570
bool Document::NextCharacter(int &pos, int moveDir) {
 
571
        // Returns true if pos changed
 
572
        int posNext = NextPosition(pos, moveDir);
 
573
        if (posNext == pos) {
 
574
                return false;
 
575
        } else {
 
576
                pos = posNext;
 
577
                return true;
 
578
        }
 
579
}
 
580
 
 
581
int SCI_METHOD Document::CodePage() const {
 
582
        return dbcsCodePage;
 
583
}
 
584
 
 
585
bool SCI_METHOD Document::IsDBCSLeadByte(char ch) const {
 
586
        // Byte ranges found in Wikipedia articles with relevant search strings in each case
 
587
        unsigned char uch = static_cast<unsigned char>(ch);
 
588
        switch (dbcsCodePage) {
 
589
                case 932:
 
590
                        // Shift_jis
 
591
                        return ((uch >= 0x81) && (uch <= 0x9F)) ||
 
592
                                ((uch >= 0xE0) && (uch <= 0xEF));
 
593
                case 936:
 
594
                        // GBK
 
595
                        return (uch >= 0x81) && (uch <= 0xFE);
 
596
                case 949:
 
597
                        // Korean Wansung KS C-5601-1987
 
598
                        return (uch >= 0x81) && (uch <= 0xFE);
 
599
                case 950:
 
600
                        // Big5
 
601
                        return (uch >= 0x81) && (uch <= 0xFE);
 
602
                case 1361:
 
603
                        // Korean Johab KS C-5601-1992
 
604
                        return
 
605
                                ((uch >= 0x84) && (uch <= 0xD3)) ||
 
606
                                ((uch >= 0xD8) && (uch <= 0xDE)) ||
 
607
                                ((uch >= 0xE0) && (uch <= 0xF9));
 
608
        }
 
609
        return false;
 
610
}
 
611
 
 
612
void Document::ModifiedAt(int pos) {
 
613
        if (endStyled > pos)
 
614
                endStyled = pos;
 
615
}
 
616
 
 
617
void Document::CheckReadOnly() {
 
618
        if (cb.IsReadOnly() && enteredReadOnlyCount == 0) {
 
619
                enteredReadOnlyCount++;
 
620
                NotifyModifyAttempt();
 
621
                enteredReadOnlyCount--;
 
622
        }
 
623
}
 
624
 
 
625
// Document only modified by gateways DeleteChars, InsertString, Undo, Redo, and SetStyleAt.
 
626
// SetStyleAt does not change the persistent state of a document
 
627
 
 
628
bool Document::DeleteChars(int pos, int len) {
 
629
        if (len == 0)
 
630
                return false;
 
631
        if ((pos + len) > Length())
 
632
                return false;
 
633
        CheckReadOnly();
 
634
        if (enteredModification != 0) {
 
635
                return false;
 
636
        } else {
 
637
                enteredModification++;
 
638
                if (!cb.IsReadOnly()) {
 
639
                        NotifyModified(
 
640
                            DocModification(
 
641
                                SC_MOD_BEFOREDELETE | SC_PERFORMED_USER,
 
642
                                pos, len,
 
643
                                0, 0));
 
644
                        int prevLinesTotal = LinesTotal();
 
645
                        bool startSavePoint = cb.IsSavePoint();
 
646
                        bool startSequence = false;
 
647
                        const char *text = cb.DeleteChars(pos, len, startSequence);
 
648
                        if (startSavePoint && cb.IsCollectingUndo())
 
649
                                NotifySavePoint(!startSavePoint);
 
650
                        if ((pos < Length()) || (pos == 0))
 
651
                                ModifiedAt(pos);
 
652
                        else
 
653
                                ModifiedAt(pos-1);
 
654
                        NotifyModified(
 
655
                            DocModification(
 
656
                                SC_MOD_DELETETEXT | SC_PERFORMED_USER | (startSequence?SC_STARTACTION:0),
 
657
                                pos, len,
 
658
                                LinesTotal() - prevLinesTotal, text));
 
659
                }
 
660
                enteredModification--;
 
661
        }
 
662
        return !cb.IsReadOnly();
 
663
}
 
664
 
 
665
/**
 
666
 * Insert a string with a length.
 
667
 */
 
668
bool Document::InsertString(int position, const char *s, int insertLength) {
 
669
        if (insertLength <= 0) {
 
670
                return false;
 
671
        }
 
672
        CheckReadOnly();
 
673
        if (enteredModification != 0) {
 
674
                return false;
 
675
        } else {
 
676
                enteredModification++;
 
677
                if (!cb.IsReadOnly()) {
 
678
                        NotifyModified(
 
679
                            DocModification(
 
680
                                SC_MOD_BEFOREINSERT | SC_PERFORMED_USER,
 
681
                                position, insertLength,
 
682
                                0, s));
 
683
                        int prevLinesTotal = LinesTotal();
 
684
                        bool startSavePoint = cb.IsSavePoint();
 
685
                        bool startSequence = false;
 
686
                        const char *text = cb.InsertString(position, s, insertLength, startSequence);
 
687
                        if (startSavePoint && cb.IsCollectingUndo())
 
688
                                NotifySavePoint(!startSavePoint);
 
689
                        ModifiedAt(position);
 
690
                        NotifyModified(
 
691
                            DocModification(
 
692
                                SC_MOD_INSERTTEXT | SC_PERFORMED_USER | (startSequence?SC_STARTACTION:0),
 
693
                                position, insertLength,
 
694
                                LinesTotal() - prevLinesTotal, text));
 
695
                }
 
696
                enteredModification--;
 
697
        }
 
698
        return !cb.IsReadOnly();
 
699
}
 
700
 
 
701
int Document::Undo() {
 
702
        int newPos = -1;
 
703
        CheckReadOnly();
 
704
        if (enteredModification == 0) {
 
705
                enteredModification++;
 
706
                if (!cb.IsReadOnly()) {
 
707
                        bool startSavePoint = cb.IsSavePoint();
 
708
                        bool multiLine = false;
 
709
                        int steps = cb.StartUndo();
 
710
                        //Platform::DebugPrintf("Steps=%d\n", steps);
 
711
                        for (int step = 0; step < steps; step++) {
 
712
                                const int prevLinesTotal = LinesTotal();
 
713
                                const Action &action = cb.GetUndoStep();
 
714
                                if (action.at == removeAction) {
 
715
                                        NotifyModified(DocModification(
 
716
                                                                        SC_MOD_BEFOREINSERT | SC_PERFORMED_UNDO, action));
 
717
                                } else if (action.at == containerAction) {
 
718
                                        DocModification dm(SC_MOD_CONTAINER | SC_PERFORMED_UNDO);
 
719
                                        dm.token = action.position;
 
720
                                        NotifyModified(dm);
 
721
                                } else {
 
722
                                        NotifyModified(DocModification(
 
723
                                                                        SC_MOD_BEFOREDELETE | SC_PERFORMED_UNDO, action));
 
724
                                }
 
725
                                cb.PerformUndoStep();
 
726
                                int cellPosition = action.position;
 
727
                                if (action.at != containerAction) {
 
728
                                        ModifiedAt(cellPosition);
 
729
                                        newPos = cellPosition;
 
730
                                }
 
731
 
 
732
                                int modFlags = SC_PERFORMED_UNDO;
 
733
                                // With undo, an insertion action becomes a deletion notification
 
734
                                if (action.at == removeAction) {
 
735
                                        newPos += action.lenData;
 
736
                                        modFlags |= SC_MOD_INSERTTEXT;
 
737
                                } else if (action.at == insertAction) {
 
738
                                        modFlags |= SC_MOD_DELETETEXT;
 
739
                                }
 
740
                                if (steps > 1)
 
741
                                        modFlags |= SC_MULTISTEPUNDOREDO;
 
742
                                const int linesAdded = LinesTotal() - prevLinesTotal;
 
743
                                if (linesAdded != 0)
 
744
                                        multiLine = true;
 
745
                                if (step == steps - 1) {
 
746
                                        modFlags |= SC_LASTSTEPINUNDOREDO;
 
747
                                        if (multiLine)
 
748
                                                modFlags |= SC_MULTILINEUNDOREDO;
 
749
                                }
 
750
                                NotifyModified(DocModification(modFlags, cellPosition, action.lenData,
 
751
                                                                                           linesAdded, action.data));
 
752
                        }
 
753
 
 
754
                        bool endSavePoint = cb.IsSavePoint();
 
755
                        if (startSavePoint != endSavePoint)
 
756
                                NotifySavePoint(endSavePoint);
 
757
                }
 
758
                enteredModification--;
 
759
        }
 
760
        return newPos;
 
761
}
 
762
 
 
763
int Document::Redo() {
 
764
        int newPos = -1;
 
765
        CheckReadOnly();
 
766
        if (enteredModification == 0) {
 
767
                enteredModification++;
 
768
                if (!cb.IsReadOnly()) {
 
769
                        bool startSavePoint = cb.IsSavePoint();
 
770
                        bool multiLine = false;
 
771
                        int steps = cb.StartRedo();
 
772
                        for (int step = 0; step < steps; step++) {
 
773
                                const int prevLinesTotal = LinesTotal();
 
774
                                const Action &action = cb.GetRedoStep();
 
775
                                if (action.at == insertAction) {
 
776
                                        NotifyModified(DocModification(
 
777
                                                                        SC_MOD_BEFOREINSERT | SC_PERFORMED_REDO, action));
 
778
                                } else if (action.at == containerAction) {
 
779
                                        DocModification dm(SC_MOD_CONTAINER | SC_PERFORMED_REDO);
 
780
                                        dm.token = action.position;
 
781
                                        NotifyModified(dm);
 
782
                                } else {
 
783
                                        NotifyModified(DocModification(
 
784
                                                                        SC_MOD_BEFOREDELETE | SC_PERFORMED_REDO, action));
 
785
                                }
 
786
                                cb.PerformRedoStep();
 
787
                                if (action.at != containerAction) {
 
788
                                        ModifiedAt(action.position);
 
789
                                        newPos = action.position;
 
790
                                }
 
791
 
 
792
                                int modFlags = SC_PERFORMED_REDO;
 
793
                                if (action.at == insertAction) {
 
794
                                        newPos += action.lenData;
 
795
                                        modFlags |= SC_MOD_INSERTTEXT;
 
796
                                } else if (action.at == removeAction) {
 
797
                                        modFlags |= SC_MOD_DELETETEXT;
 
798
                                }
 
799
                                if (steps > 1)
 
800
                                        modFlags |= SC_MULTISTEPUNDOREDO;
 
801
                                const int linesAdded = LinesTotal() - prevLinesTotal;
 
802
                                if (linesAdded != 0)
 
803
                                        multiLine = true;
 
804
                                if (step == steps - 1) {
 
805
                                        modFlags |= SC_LASTSTEPINUNDOREDO;
 
806
                                        if (multiLine)
 
807
                                                modFlags |= SC_MULTILINEUNDOREDO;
 
808
                                }
 
809
                                NotifyModified(
 
810
                                        DocModification(modFlags, action.position, action.lenData,
 
811
                                                                        linesAdded, action.data));
 
812
                        }
 
813
 
 
814
                        bool endSavePoint = cb.IsSavePoint();
 
815
                        if (startSavePoint != endSavePoint)
 
816
                                NotifySavePoint(endSavePoint);
 
817
                }
 
818
                enteredModification--;
 
819
        }
 
820
        return newPos;
 
821
}
 
822
 
 
823
/**
 
824
 * Insert a single character.
 
825
 */
 
826
bool Document::InsertChar(int pos, char ch) {
 
827
        char chs[1];
 
828
        chs[0] = ch;
 
829
        return InsertString(pos, chs, 1);
 
830
}
 
831
 
 
832
/**
 
833
 * Insert a null terminated string.
 
834
 */
 
835
bool Document::InsertCString(int position, const char *s) {
 
836
        return InsertString(position, s, strlen(s));
 
837
}
 
838
 
 
839
void Document::ChangeChar(int pos, char ch) {
 
840
        DeleteChars(pos, 1);
 
841
        InsertChar(pos, ch);
 
842
}
 
843
 
 
844
void Document::DelChar(int pos) {
 
845
        DeleteChars(pos, LenChar(pos));
 
846
}
 
847
 
 
848
void Document::DelCharBack(int pos) {
 
849
        if (pos <= 0) {
 
850
                return;
 
851
        } else if (IsCrLf(pos - 2)) {
 
852
                DeleteChars(pos - 2, 2);
 
853
        } else if (dbcsCodePage) {
 
854
                int startChar = NextPosition(pos, -1);
 
855
                DeleteChars(startChar, pos - startChar);
 
856
        } else {
 
857
                DeleteChars(pos - 1, 1);
 
858
        }
 
859
}
 
860
 
 
861
static bool isindentchar(char ch) {
 
862
        return (ch == ' ') || (ch == '\t');
 
863
}
 
864
 
 
865
static int NextTab(int pos, int tabSize) {
 
866
        return ((pos / tabSize) + 1) * tabSize;
 
867
}
 
868
 
 
869
static void CreateIndentation(char *linebuf, int length, int indent, int tabSize, bool insertSpaces) {
 
870
        length--;       // ensure space for \0
 
871
        if (!insertSpaces) {
 
872
                while ((indent >= tabSize) && (length > 0)) {
 
873
                        *linebuf++ = '\t';
 
874
                        indent -= tabSize;
 
875
                        length--;
 
876
                }
 
877
        }
 
878
        while ((indent > 0) && (length > 0)) {
 
879
                *linebuf++ = ' ';
 
880
                indent--;
 
881
                length--;
 
882
        }
 
883
        *linebuf = '\0';
 
884
}
 
885
 
 
886
int SCI_METHOD Document::GetLineIndentation(int line) {
 
887
        int indent = 0;
 
888
        if ((line >= 0) && (line < LinesTotal())) {
 
889
                int lineStart = LineStart(line);
 
890
                int length = Length();
 
891
                for (int i = lineStart; i < length; i++) {
 
892
                        char ch = cb.CharAt(i);
 
893
                        if (ch == ' ')
 
894
                                indent++;
 
895
                        else if (ch == '\t')
 
896
                                indent = NextTab(indent, tabInChars);
 
897
                        else
 
898
                                return indent;
 
899
                }
 
900
        }
 
901
        return indent;
 
902
}
 
903
 
 
904
void Document::SetLineIndentation(int line, int indent) {
 
905
        int indentOfLine = GetLineIndentation(line);
 
906
        if (indent < 0)
 
907
                indent = 0;
 
908
        if (indent != indentOfLine) {
 
909
                char linebuf[1000];
 
910
                CreateIndentation(linebuf, sizeof(linebuf), indent, tabInChars, !useTabs);
 
911
                int thisLineStart = LineStart(line);
 
912
                int indentPos = GetLineIndentPosition(line);
 
913
                UndoGroup ug(this);
 
914
                DeleteChars(thisLineStart, indentPos - thisLineStart);
 
915
                InsertCString(thisLineStart, linebuf);
 
916
        }
 
917
}
 
918
 
 
919
int Document::GetLineIndentPosition(int line) const {
 
920
        if (line < 0)
 
921
                return 0;
 
922
        int pos = LineStart(line);
 
923
        int length = Length();
 
924
        while ((pos < length) && isindentchar(cb.CharAt(pos))) {
 
925
                pos++;
 
926
        }
 
927
        return pos;
 
928
}
 
929
 
 
930
int Document::GetColumn(int pos) {
 
931
        int column = 0;
 
932
        int line = LineFromPosition(pos);
 
933
        if ((line >= 0) && (line < LinesTotal())) {
 
934
                for (int i = LineStart(line); i < pos;) {
 
935
                        char ch = cb.CharAt(i);
 
936
                        if (ch == '\t') {
 
937
                                column = NextTab(column, tabInChars);
 
938
                                i++;
 
939
                        } else if (ch == '\r') {
 
940
                                return column;
 
941
                        } else if (ch == '\n') {
 
942
                                return column;
 
943
                        } else if (i >= Length()) {
 
944
                                return column;
 
945
                        } else {
 
946
                                column++;
 
947
                                i = NextPosition(i, 1);
 
948
                        }
 
949
                }
 
950
        }
 
951
        return column;
 
952
}
 
953
 
 
954
int Document::FindColumn(int line, int column) {
 
955
        int position = LineStart(line);
 
956
        if ((line >= 0) && (line < LinesTotal())) {
 
957
                int columnCurrent = 0;
 
958
                while ((columnCurrent < column) && (position < Length())) {
 
959
                        char ch = cb.CharAt(position);
 
960
                        if (ch == '\t') {
 
961
                                columnCurrent = NextTab(columnCurrent, tabInChars);
 
962
                                position++;
 
963
                        } else if (ch == '\r') {
 
964
                                return position;
 
965
                        } else if (ch == '\n') {
 
966
                                return position;
 
967
                        } else {
 
968
                                columnCurrent++;
 
969
                                position = NextPosition(position, 1);
 
970
                        }
 
971
                }
 
972
        }
 
973
        return position;
 
974
}
 
975
 
 
976
void Document::Indent(bool forwards, int lineBottom, int lineTop) {
 
977
        // Dedent - suck white space off the front of the line to dedent by equivalent of a tab
 
978
        for (int line = lineBottom; line >= lineTop; line--) {
 
979
                int indentOfLine = GetLineIndentation(line);
 
980
                if (forwards) {
 
981
                        if (LineStart(line) < LineEnd(line)) {
 
982
                                SetLineIndentation(line, indentOfLine + IndentSize());
 
983
                        }
 
984
                } else {
 
985
                        SetLineIndentation(line, indentOfLine - IndentSize());
 
986
                }
 
987
        }
 
988
}
 
989
 
 
990
// Convert line endings for a piece of text to a particular mode.
 
991
// Stop at len or when a NUL is found.
 
992
// Caller must delete the returned pointer.
 
993
char *Document::TransformLineEnds(int *pLenOut, const char *s, size_t len, int eolMode) {
 
994
        char *dest = new char[2 * len + 1];
 
995
        const char *sptr = s;
 
996
        char *dptr = dest;
 
997
        for (size_t i = 0; (i < len) && (*sptr != '\0'); i++) {
 
998
                if (*sptr == '\n' || *sptr == '\r') {
 
999
                        if (eolMode == SC_EOL_CR) {
 
1000
                                *dptr++ = '\r';
 
1001
                        } else if (eolMode == SC_EOL_LF) {
 
1002
                                *dptr++ = '\n';
 
1003
                        } else { // eolMode == SC_EOL_CRLF
 
1004
                                *dptr++ = '\r';
 
1005
                                *dptr++ = '\n';
 
1006
                        }
 
1007
                        if ((*sptr == '\r') && (i+1 < len) && (*(sptr+1) == '\n')) {
 
1008
                                i++;
 
1009
                                sptr++;
 
1010
                        }
 
1011
                        sptr++;
 
1012
                } else {
 
1013
                        *dptr++ = *sptr++;
 
1014
                }
 
1015
        }
 
1016
        *dptr++ = '\0';
 
1017
        *pLenOut = (dptr - dest) - 1;
 
1018
        return dest;
 
1019
}
 
1020
 
 
1021
void Document::ConvertLineEnds(int eolModeSet) {
 
1022
        UndoGroup ug(this);
 
1023
 
 
1024
        for (int pos = 0; pos < Length(); pos++) {
 
1025
                if (cb.CharAt(pos) == '\r') {
 
1026
                        if (cb.CharAt(pos + 1) == '\n') {
 
1027
                                // CRLF
 
1028
                                if (eolModeSet == SC_EOL_CR) {
 
1029
                                        DeleteChars(pos + 1, 1); // Delete the LF
 
1030
                                } else if (eolModeSet == SC_EOL_LF) {
 
1031
                                        DeleteChars(pos, 1); // Delete the CR
 
1032
                                } else {
 
1033
                                        pos++;
 
1034
                                }
 
1035
                        } else {
 
1036
                                // CR
 
1037
                                if (eolModeSet == SC_EOL_CRLF) {
 
1038
                                        InsertString(pos + 1, "\n", 1); // Insert LF
 
1039
                                        pos++;
 
1040
                                } else if (eolModeSet == SC_EOL_LF) {
 
1041
                                        InsertString(pos, "\n", 1); // Insert LF
 
1042
                                        DeleteChars(pos + 1, 1); // Delete CR
 
1043
                                }
 
1044
                        }
 
1045
                } else if (cb.CharAt(pos) == '\n') {
 
1046
                        // LF
 
1047
                        if (eolModeSet == SC_EOL_CRLF) {
 
1048
                                InsertString(pos, "\r", 1); // Insert CR
 
1049
                                pos++;
 
1050
                        } else if (eolModeSet == SC_EOL_CR) {
 
1051
                                InsertString(pos, "\r", 1); // Insert CR
 
1052
                                DeleteChars(pos + 1, 1); // Delete LF
 
1053
                        }
 
1054
                }
 
1055
        }
 
1056
 
 
1057
}
 
1058
 
 
1059
bool Document::IsWhiteLine(int line) const {
 
1060
        int currentChar = LineStart(line);
 
1061
        int endLine = LineEnd(line);
 
1062
        while (currentChar < endLine) {
 
1063
                if (cb.CharAt(currentChar) != ' ' && cb.CharAt(currentChar) != '\t') {
 
1064
                        return false;
 
1065
                }
 
1066
                ++currentChar;
 
1067
        }
 
1068
        return true;
 
1069
}
 
1070
 
 
1071
int Document::ParaUp(int pos) {
 
1072
        int line = LineFromPosition(pos);
 
1073
        line--;
 
1074
        while (line >= 0 && IsWhiteLine(line)) { // skip empty lines
 
1075
                line--;
 
1076
        }
 
1077
        while (line >= 0 && !IsWhiteLine(line)) { // skip non-empty lines
 
1078
                line--;
 
1079
        }
 
1080
        line++;
 
1081
        return LineStart(line);
 
1082
}
 
1083
 
 
1084
int Document::ParaDown(int pos) {
 
1085
        int line = LineFromPosition(pos);
 
1086
        while (line < LinesTotal() && !IsWhiteLine(line)) { // skip non-empty lines
 
1087
                line++;
 
1088
        }
 
1089
        while (line < LinesTotal() && IsWhiteLine(line)) { // skip empty lines
 
1090
                line++;
 
1091
        }
 
1092
        if (line < LinesTotal())
 
1093
                return LineStart(line);
 
1094
        else // end of a document
 
1095
                return LineEnd(line-1);
 
1096
}
 
1097
 
 
1098
CharClassify::cc Document::WordCharClass(unsigned char ch) {
 
1099
        if ((SC_CP_UTF8 == dbcsCodePage) && (ch >= 0x80))
 
1100
                return CharClassify::ccWord;
 
1101
        return charClass.GetClass(ch);
 
1102
}
 
1103
 
 
1104
/**
 
1105
 * Used by commmands that want to select whole words.
 
1106
 * Finds the start of word at pos when delta < 0 or the end of the word when delta >= 0.
 
1107
 */
 
1108
int Document::ExtendWordSelect(int pos, int delta, bool onlyWordCharacters) {
 
1109
        CharClassify::cc ccStart = CharClassify::ccWord;
 
1110
        if (delta < 0) {
 
1111
                if (!onlyWordCharacters)
 
1112
                        ccStart = WordCharClass(cb.CharAt(pos-1));
 
1113
                while (pos > 0 && (WordCharClass(cb.CharAt(pos - 1)) == ccStart))
 
1114
                        pos--;
 
1115
        } else {
 
1116
                if (!onlyWordCharacters && pos < Length())
 
1117
                        ccStart = WordCharClass(cb.CharAt(pos));
 
1118
                while (pos < (Length()) && (WordCharClass(cb.CharAt(pos)) == ccStart))
 
1119
                        pos++;
 
1120
        }
 
1121
        return MovePositionOutsideChar(pos, delta, true);
 
1122
}
 
1123
 
 
1124
/**
 
1125
 * Find the start of the next word in either a forward (delta >= 0) or backwards direction
 
1126
 * (delta < 0).
 
1127
 * This is looking for a transition between character classes although there is also some
 
1128
 * additional movement to transit white space.
 
1129
 * Used by cursor movement by word commands.
 
1130
 */
 
1131
int Document::NextWordStart(int pos, int delta) {
 
1132
        if (delta < 0) {
 
1133
                while (pos > 0 && (WordCharClass(cb.CharAt(pos - 1)) == CharClassify::ccSpace))
 
1134
                        pos--;
 
1135
                if (pos > 0) {
 
1136
                        CharClassify::cc ccStart = WordCharClass(cb.CharAt(pos-1));
 
1137
                        while (pos > 0 && (WordCharClass(cb.CharAt(pos - 1)) == ccStart)) {
 
1138
                                pos--;
 
1139
                        }
 
1140
                }
 
1141
        } else {
 
1142
                CharClassify::cc ccStart = WordCharClass(cb.CharAt(pos));
 
1143
                while (pos < (Length()) && (WordCharClass(cb.CharAt(pos)) == ccStart))
 
1144
                        pos++;
 
1145
                while (pos < (Length()) && (WordCharClass(cb.CharAt(pos)) == CharClassify::ccSpace))
 
1146
                        pos++;
 
1147
        }
 
1148
        return pos;
 
1149
}
 
1150
 
 
1151
/**
 
1152
 * Find the end of the next word in either a forward (delta >= 0) or backwards direction
 
1153
 * (delta < 0).
 
1154
 * This is looking for a transition between character classes although there is also some
 
1155
 * additional movement to transit white space.
 
1156
 * Used by cursor movement by word commands.
 
1157
 */
 
1158
int Document::NextWordEnd(int pos, int delta) {
 
1159
        if (delta < 0) {
 
1160
                if (pos > 0) {
 
1161
                        CharClassify::cc ccStart = WordCharClass(cb.CharAt(pos-1));
 
1162
                        if (ccStart != CharClassify::ccSpace) {
 
1163
                                while (pos > 0 && WordCharClass(cb.CharAt(pos - 1)) == ccStart) {
 
1164
                                        pos--;
 
1165
                                }
 
1166
                        }
 
1167
                        while (pos > 0 && WordCharClass(cb.CharAt(pos - 1)) == CharClassify::ccSpace) {
 
1168
                                pos--;
 
1169
                        }
 
1170
                }
 
1171
        } else {
 
1172
                while (pos < Length() && WordCharClass(cb.CharAt(pos)) == CharClassify::ccSpace) {
 
1173
                        pos++;
 
1174
                }
 
1175
                if (pos < Length()) {
 
1176
                        CharClassify::cc ccStart = WordCharClass(cb.CharAt(pos));
 
1177
                        while (pos < Length() && WordCharClass(cb.CharAt(pos)) == ccStart) {
 
1178
                                pos++;
 
1179
                        }
 
1180
                }
 
1181
        }
 
1182
        return pos;
 
1183
}
 
1184
 
 
1185
/**
 
1186
 * Check that the character at the given position is a word or punctuation character and that
 
1187
 * the previous character is of a different character class.
 
1188
 */
 
1189
bool Document::IsWordStartAt(int pos) {
 
1190
        if (pos > 0) {
 
1191
                CharClassify::cc ccPos = WordCharClass(CharAt(pos));
 
1192
                return (ccPos == CharClassify::ccWord || ccPos == CharClassify::ccPunctuation) &&
 
1193
                        (ccPos != WordCharClass(CharAt(pos - 1)));
 
1194
        }
 
1195
        return true;
 
1196
}
 
1197
 
 
1198
/**
 
1199
 * Check that the character at the given position is a word or punctuation character and that
 
1200
 * the next character is of a different character class.
 
1201
 */
 
1202
bool Document::IsWordEndAt(int pos) {
 
1203
        if (pos < Length()) {
 
1204
                CharClassify::cc ccPrev = WordCharClass(CharAt(pos-1));
 
1205
                return (ccPrev == CharClassify::ccWord || ccPrev == CharClassify::ccPunctuation) &&
 
1206
                        (ccPrev != WordCharClass(CharAt(pos)));
 
1207
        }
 
1208
        return true;
 
1209
}
 
1210
 
 
1211
/**
 
1212
 * Check that the given range is has transitions between character classes at both
 
1213
 * ends and where the characters on the inside are word or punctuation characters.
 
1214
 */
 
1215
bool Document::IsWordAt(int start, int end) {
 
1216
        return IsWordStartAt(start) && IsWordEndAt(end);
 
1217
}
 
1218
 
 
1219
static inline char MakeLowerCase(char ch) {
 
1220
        if (ch < 'A' || ch > 'Z')
 
1221
                return ch;
 
1222
        else
 
1223
                return static_cast<char>(ch - 'A' + 'a');
 
1224
}
 
1225
 
 
1226
static bool GoodTrailByte(int v) {
 
1227
        return (v >= 0x80) && (v < 0xc0);
 
1228
}
 
1229
 
 
1230
size_t Document::ExtractChar(int pos, char *bytes) {
 
1231
        unsigned char ch = static_cast<unsigned char>(cb.CharAt(pos));
 
1232
        size_t widthChar = UTF8CharLength(ch);
 
1233
        bytes[0] = ch;
 
1234
        for (size_t i=1; i<widthChar; i++) {
 
1235
                bytes[i] = cb.CharAt(pos+i);
 
1236
                if (!GoodTrailByte(static_cast<unsigned char>(bytes[i]))) { // Bad byte
 
1237
                        widthChar = 1;
 
1238
                }
 
1239
        }
 
1240
        return widthChar;
 
1241
}
 
1242
 
 
1243
CaseFolderTable::CaseFolderTable() {
 
1244
        for (size_t iChar=0; iChar<sizeof(mapping); iChar++) {
 
1245
                mapping[iChar] = static_cast<char>(iChar);
 
1246
        }
 
1247
}
 
1248
 
 
1249
CaseFolderTable::~CaseFolderTable() {
 
1250
}
 
1251
 
 
1252
size_t CaseFolderTable::Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) {
 
1253
        if (lenMixed > sizeFolded) {
 
1254
                return 0;
 
1255
        } else {
 
1256
                for (size_t i=0; i<lenMixed; i++) {
 
1257
                        folded[i] = mapping[static_cast<unsigned char>(mixed[i])];
 
1258
                }
 
1259
                return lenMixed;
 
1260
        }
 
1261
}
 
1262
 
 
1263
void CaseFolderTable::SetTranslation(char ch, char chTranslation) {
 
1264
        mapping[static_cast<unsigned char>(ch)] = chTranslation;
 
1265
}
 
1266
 
 
1267
void CaseFolderTable::StandardASCII() {
 
1268
        for (size_t iChar=0; iChar<sizeof(mapping); iChar++) {
 
1269
                if (iChar >= 'A' && iChar <= 'Z') {
 
1270
                        mapping[iChar] = static_cast<char>(iChar - 'A' + 'a');
 
1271
                } else {
 
1272
                        mapping[iChar] = static_cast<char>(iChar);
 
1273
                }
 
1274
        }
 
1275
}
 
1276
 
 
1277
bool Document::MatchesWordOptions(bool word, bool wordStart, int pos, int length) {
 
1278
        return (!word && !wordStart) ||
 
1279
                        (word && IsWordAt(pos, pos + length)) ||
 
1280
                        (wordStart && IsWordStartAt(pos));
 
1281
}
 
1282
 
 
1283
/**
 
1284
 * Find text in document, supporting both forward and backward
 
1285
 * searches (just pass minPos > maxPos to do a backward search)
 
1286
 * Has not been tested with backwards DBCS searches yet.
 
1287
 */
 
1288
long Document::FindText(int minPos, int maxPos, const char *search,
 
1289
                        bool caseSensitive, bool word, bool wordStart, bool regExp, int flags,
 
1290
                        int *length, CaseFolder *pcf) {
 
1291
        if (*length <= 0)
 
1292
                return minPos;
 
1293
        if (regExp) {
 
1294
                if (!regex)
 
1295
                        regex = CreateRegexSearch(&charClass);
 
1296
                return regex->FindText(this, minPos, maxPos, search, caseSensitive, word, wordStart, flags, length);
 
1297
        } else {
 
1298
 
 
1299
                const bool forward = minPos <= maxPos;
 
1300
                const int increment = forward ? 1 : -1;
 
1301
 
 
1302
                // Range endpoints should not be inside DBCS characters, but just in case, move them.
 
1303
                const int startPos = MovePositionOutsideChar(minPos, increment, false);
 
1304
                const int endPos = MovePositionOutsideChar(maxPos, increment, false);
 
1305
 
 
1306
                // Compute actual search ranges needed
 
1307
                const int lengthFind = (*length == -1) ? static_cast<int>(strlen(search)) : *length;
 
1308
                const int endSearch = (startPos <= endPos) ? endPos - lengthFind + 1 : endPos;
 
1309
 
 
1310
                //Platform::DebugPrintf("Find %d %d %s %d\n", startPos, endPos, ft->lpstrText, lengthFind);
 
1311
                const int limitPos = Platform::Maximum(startPos, endPos);
 
1312
                int pos = startPos;
 
1313
                if (!forward) {
 
1314
                        // Back all of a character
 
1315
                        pos = NextPosition(pos, increment);
 
1316
                }
 
1317
                if (caseSensitive) {
 
1318
                        while (forward ? (pos < endSearch) : (pos >= endSearch)) {
 
1319
                                bool found = (pos + lengthFind) <= limitPos;
 
1320
                                for (int indexSearch = 0; (indexSearch < lengthFind) && found; indexSearch++) {
 
1321
                                        found = CharAt(pos + indexSearch) == search[indexSearch];
 
1322
                                }
 
1323
                                if (found && MatchesWordOptions(word, wordStart, pos, lengthFind)) {
 
1324
                                        return pos;
 
1325
                                }
 
1326
                                if (!NextCharacter(pos, increment))
 
1327
                                        break;
 
1328
                        }
 
1329
                } else if (SC_CP_UTF8 == dbcsCodePage) {
 
1330
                        const size_t maxBytesCharacter = 4;
 
1331
                        const size_t maxFoldingExpansion = 4;
 
1332
                        std::vector<char> searchThing(lengthFind * maxBytesCharacter * maxFoldingExpansion + 1);
 
1333
                        const int lenSearch = pcf->Fold(&searchThing[0], searchThing.size(), search, lengthFind);
 
1334
                        while (forward ? (pos < endSearch) : (pos >= endSearch)) {
 
1335
                                int widthFirstCharacter = 0;
 
1336
                                int indexDocument = 0;
 
1337
                                int indexSearch = 0;
 
1338
                                bool characterMatches = true;
 
1339
                                while (characterMatches &&
 
1340
                                        ((pos + indexDocument) < limitPos) &&
 
1341
                                        (indexSearch < lenSearch)) {
 
1342
                                        char bytes[maxBytesCharacter + 1];
 
1343
                                        bytes[maxBytesCharacter] = 0;
 
1344
                                        const int widthChar = ExtractChar(pos + indexDocument, bytes);
 
1345
                                        if (!widthFirstCharacter)
 
1346
                                                widthFirstCharacter = widthChar;
 
1347
                                        char folded[maxBytesCharacter * maxFoldingExpansion + 1];
 
1348
                                        const int lenFlat = pcf->Fold(folded, sizeof(folded), bytes, widthChar);
 
1349
                                        folded[lenFlat] = 0;
 
1350
                                        // Does folded match the buffer
 
1351
                                        characterMatches = 0 == memcmp(folded, &searchThing[0] + indexSearch, lenFlat);
 
1352
                                        indexDocument += widthChar;
 
1353
                                        indexSearch += lenFlat;
 
1354
                                }
 
1355
                                if (characterMatches && (indexSearch == static_cast<int>(lenSearch))) {
 
1356
                                        if (MatchesWordOptions(word, wordStart, pos, indexDocument)) {
 
1357
                                                *length = indexDocument;
 
1358
                                                return pos;
 
1359
                                        }
 
1360
                                }
 
1361
                                if (forward) {
 
1362
                                        pos += widthFirstCharacter;
 
1363
                                } else {
 
1364
                                        if (!NextCharacter(pos, increment))
 
1365
                                                break;
 
1366
                                }
 
1367
                        }
 
1368
                } else if (dbcsCodePage) {
 
1369
                        const size_t maxBytesCharacter = 2;
 
1370
                        const size_t maxFoldingExpansion = 4;
 
1371
                        std::vector<char> searchThing(lengthFind * maxBytesCharacter * maxFoldingExpansion + 1);
 
1372
                        const int lenSearch = pcf->Fold(&searchThing[0], searchThing.size(), search, lengthFind);
 
1373
                        while (forward ? (pos < endSearch) : (pos >= endSearch)) {
 
1374
                                int indexDocument = 0;
 
1375
                                int indexSearch = 0;
 
1376
                                bool characterMatches = true;
 
1377
                                while (characterMatches &&
 
1378
                                        ((pos + indexDocument) < limitPos) &&
 
1379
                                        (indexSearch < lenSearch)) {
 
1380
                                        char bytes[maxBytesCharacter + 1];
 
1381
                                        bytes[0] = cb.CharAt(pos + indexDocument);
 
1382
                                        const int widthChar = IsDBCSLeadByte(bytes[0]) ? 2 : 1;
 
1383
                                        if (widthChar == 2)
 
1384
                                                bytes[1] = cb.CharAt(pos + indexDocument + 1);
 
1385
                                        char folded[maxBytesCharacter * maxFoldingExpansion + 1];
 
1386
                                        const int lenFlat = pcf->Fold(folded, sizeof(folded), bytes, widthChar);
 
1387
                                        folded[lenFlat] = 0;
 
1388
                                        // Does folded match the buffer
 
1389
                                        characterMatches = 0 == memcmp(folded, &searchThing[0] + indexSearch, lenFlat);
 
1390
                                        indexDocument += widthChar;
 
1391
                                        indexSearch += lenFlat;
 
1392
                                }
 
1393
                                if (characterMatches && (indexSearch == static_cast<int>(lenSearch))) {
 
1394
                                        if (MatchesWordOptions(word, wordStart, pos, indexDocument)) {
 
1395
                                                *length = indexDocument;
 
1396
                                                return pos;
 
1397
                                        }
 
1398
                                }
 
1399
                                if (!NextCharacter(pos, increment))
 
1400
                                        break;
 
1401
                        }
 
1402
                } else {
 
1403
                        CaseFolderTable caseFolder;
 
1404
                        std::vector<char> searchThing(lengthFind + 1);
 
1405
                        pcf->Fold(&searchThing[0], searchThing.size(), search, lengthFind);
 
1406
                        while (forward ? (pos < endSearch) : (pos >= endSearch)) {
 
1407
                                bool found = (pos + lengthFind) <= limitPos;
 
1408
                                for (int indexSearch = 0; (indexSearch < lengthFind) && found; indexSearch++) {
 
1409
                                        char ch = CharAt(pos + indexSearch);
 
1410
                                        char folded[2];
 
1411
                                        pcf->Fold(folded, sizeof(folded), &ch, 1);
 
1412
                                        found = folded[0] == searchThing[indexSearch];
 
1413
                                }
 
1414
                                if (found && MatchesWordOptions(word, wordStart, pos, lengthFind)) {
 
1415
                                        return pos;
 
1416
                                }
 
1417
                                if (!NextCharacter(pos, increment))
 
1418
                                        break;
 
1419
                        }
 
1420
                }
 
1421
        }
 
1422
        //Platform::DebugPrintf("Not found\n");
 
1423
        return -1;
 
1424
}
 
1425
 
 
1426
const char *Document::SubstituteByPosition(const char *text, int *length) {
 
1427
        if (regex)
 
1428
                return regex->SubstituteByPosition(this, text, length);
 
1429
        else
 
1430
                return 0;
 
1431
}
 
1432
 
 
1433
int Document::LinesTotal() const {
 
1434
        return cb.Lines();
 
1435
}
 
1436
 
 
1437
void Document::ChangeCase(Range r, bool makeUpperCase) {
 
1438
        for (int pos = r.start; pos < r.end;) {
 
1439
                int len = LenChar(pos);
 
1440
                if (len == 1) {
 
1441
                        char ch = CharAt(pos);
 
1442
                        if (makeUpperCase) {
 
1443
                                if (IsLowerCase(ch)) {
 
1444
                                        ChangeChar(pos, static_cast<char>(MakeUpperCase(ch)));
 
1445
                                }
 
1446
                        } else {
 
1447
                                if (IsUpperCase(ch)) {
 
1448
                                        ChangeChar(pos, static_cast<char>(MakeLowerCase(ch)));
 
1449
                                }
 
1450
                        }
 
1451
                }
 
1452
                pos += len;
 
1453
        }
 
1454
}
 
1455
 
 
1456
void Document::SetDefaultCharClasses(bool includeWordClass) {
 
1457
    charClass.SetDefaultCharClasses(includeWordClass);
 
1458
}
 
1459
 
 
1460
void Document::SetCharClasses(const unsigned char *chars, CharClassify::cc newCharClass) {
 
1461
    charClass.SetCharClasses(chars, newCharClass);
 
1462
}
 
1463
 
 
1464
void Document::SetStylingBits(int bits) {
 
1465
        stylingBits = bits;
 
1466
        stylingBitsMask = (1 << stylingBits) - 1;
 
1467
}
 
1468
 
 
1469
void SCI_METHOD Document::StartStyling(int position, char mask) {
 
1470
        stylingMask = mask;
 
1471
        endStyled = position;
 
1472
}
 
1473
 
 
1474
bool SCI_METHOD Document::SetStyleFor(int length, char style) {
 
1475
        if (enteredStyling != 0) {
 
1476
                return false;
 
1477
        } else {
 
1478
                enteredStyling++;
 
1479
                style &= stylingMask;
 
1480
                int prevEndStyled = endStyled;
 
1481
                if (cb.SetStyleFor(endStyled, length, style, stylingMask)) {
 
1482
                        DocModification mh(SC_MOD_CHANGESTYLE | SC_PERFORMED_USER,
 
1483
                                           prevEndStyled, length);
 
1484
                        NotifyModified(mh);
 
1485
                }
 
1486
                endStyled += length;
 
1487
                enteredStyling--;
 
1488
                return true;
 
1489
        }
 
1490
}
 
1491
 
 
1492
bool SCI_METHOD Document::SetStyles(int length, const char *styles) {
 
1493
        if (enteredStyling != 0) {
 
1494
                return false;
 
1495
        } else {
 
1496
                enteredStyling++;
 
1497
                bool didChange = false;
 
1498
                int startMod = 0;
 
1499
                int endMod = 0;
 
1500
                for (int iPos = 0; iPos < length; iPos++, endStyled++) {
 
1501
                        PLATFORM_ASSERT(endStyled < Length());
 
1502
                        if (cb.SetStyleAt(endStyled, styles[iPos], stylingMask)) {
 
1503
                                if (!didChange) {
 
1504
                                        startMod = endStyled;
 
1505
                                }
 
1506
                                didChange = true;
 
1507
                                endMod = endStyled;
 
1508
                        }
 
1509
                }
 
1510
                if (didChange) {
 
1511
                        DocModification mh(SC_MOD_CHANGESTYLE | SC_PERFORMED_USER,
 
1512
                                           startMod, endMod - startMod + 1);
 
1513
                        NotifyModified(mh);
 
1514
                }
 
1515
                enteredStyling--;
 
1516
                return true;
 
1517
        }
 
1518
}
 
1519
 
 
1520
void Document::EnsureStyledTo(int pos) {
 
1521
        if ((enteredStyling == 0) && (pos > GetEndStyled())) {
 
1522
                IncrementStyleClock();
 
1523
                if (pli && !pli->UseContainerLexing()) {
 
1524
                        int lineEndStyled = LineFromPosition(GetEndStyled());
 
1525
                        int endStyledTo = LineStart(lineEndStyled);
 
1526
                        pli->Colourise(endStyledTo, pos);
 
1527
                } else {
 
1528
                        // Ask the watchers to style, and stop as soon as one responds.
 
1529
                        for (int i = 0; pos > GetEndStyled() && i < lenWatchers; i++) {
 
1530
                                watchers[i].watcher->NotifyStyleNeeded(this, watchers[i].userData, pos);
 
1531
                        }
 
1532
                }
 
1533
        }
 
1534
}
 
1535
 
 
1536
void Document::LexerChanged() {
 
1537
        // Tell the watchers the lexer has changed.
 
1538
        for (int i = 0; i < lenWatchers; i++) {
 
1539
                watchers[i].watcher->NotifyLexerChanged(this, watchers[i].userData);
 
1540
        }
 
1541
}
 
1542
 
 
1543
int SCI_METHOD Document::SetLineState(int line, int state) {
 
1544
        int statePrevious = static_cast<LineState *>(perLineData[ldState])->SetLineState(line, state);
 
1545
        if (state != statePrevious) {
 
1546
                DocModification mh(SC_MOD_CHANGELINESTATE, LineStart(line), 0, 0, 0, line);
 
1547
                NotifyModified(mh);
 
1548
        }
 
1549
        return statePrevious;
 
1550
}
 
1551
 
 
1552
int SCI_METHOD Document::GetLineState(int line) const {
 
1553
        return static_cast<LineState *>(perLineData[ldState])->GetLineState(line);
 
1554
}
 
1555
 
 
1556
int Document::GetMaxLineState() {
 
1557
        return static_cast<LineState *>(perLineData[ldState])->GetMaxLineState();
 
1558
}
 
1559
 
 
1560
void SCI_METHOD Document::ChangeLexerState(int start, int end) {
 
1561
        DocModification mh(SC_MOD_LEXERSTATE, start, end-start, 0, 0, 0);
 
1562
        NotifyModified(mh);
 
1563
}
 
1564
 
 
1565
StyledText Document::MarginStyledText(int line) {
 
1566
        LineAnnotation *pla = static_cast<LineAnnotation *>(perLineData[ldMargin]);
 
1567
        return StyledText(pla->Length(line), pla->Text(line),
 
1568
                pla->MultipleStyles(line), pla->Style(line), pla->Styles(line));
 
1569
}
 
1570
 
 
1571
void Document::MarginSetText(int line, const char *text) {
 
1572
        static_cast<LineAnnotation *>(perLineData[ldMargin])->SetText(line, text);
 
1573
        DocModification mh(SC_MOD_CHANGEMARGIN, LineStart(line), 0, 0, 0, line);
 
1574
        NotifyModified(mh);
 
1575
}
 
1576
 
 
1577
void Document::MarginSetStyle(int line, int style) {
 
1578
        static_cast<LineAnnotation *>(perLineData[ldMargin])->SetStyle(line, style);
 
1579
}
 
1580
 
 
1581
void Document::MarginSetStyles(int line, const unsigned char *styles) {
 
1582
        static_cast<LineAnnotation *>(perLineData[ldMargin])->SetStyles(line, styles);
 
1583
}
 
1584
 
 
1585
int Document::MarginLength(int line) const {
 
1586
        return static_cast<LineAnnotation *>(perLineData[ldMargin])->Length(line);
 
1587
}
 
1588
 
 
1589
void Document::MarginClearAll() {
 
1590
        int maxEditorLine = LinesTotal();
 
1591
        for (int l=0; l<maxEditorLine; l++)
 
1592
                MarginSetText(l, 0);
 
1593
        // Free remaining data
 
1594
        static_cast<LineAnnotation *>(perLineData[ldMargin])->ClearAll();
 
1595
}
 
1596
 
 
1597
bool Document::AnnotationAny() const {
 
1598
        return static_cast<LineAnnotation *>(perLineData[ldAnnotation])->AnySet();
 
1599
}
 
1600
 
 
1601
StyledText Document::AnnotationStyledText(int line) {
 
1602
        LineAnnotation *pla = static_cast<LineAnnotation *>(perLineData[ldAnnotation]);
 
1603
        return StyledText(pla->Length(line), pla->Text(line),
 
1604
                pla->MultipleStyles(line), pla->Style(line), pla->Styles(line));
 
1605
}
 
1606
 
 
1607
void Document::AnnotationSetText(int line, const char *text) {
 
1608
        const int linesBefore = AnnotationLines(line);
 
1609
        static_cast<LineAnnotation *>(perLineData[ldAnnotation])->SetText(line, text);
 
1610
        const int linesAfter = AnnotationLines(line);
 
1611
        DocModification mh(SC_MOD_CHANGEANNOTATION, LineStart(line), 0, 0, 0, line);
 
1612
        mh.annotationLinesAdded = linesAfter - linesBefore;
 
1613
        NotifyModified(mh);
 
1614
}
 
1615
 
 
1616
void Document::AnnotationSetStyle(int line, int style) {
 
1617
        static_cast<LineAnnotation *>(perLineData[ldAnnotation])->SetStyle(line, style);
 
1618
        DocModification mh(SC_MOD_CHANGEANNOTATION, LineStart(line), 0, 0, 0, line);
 
1619
        NotifyModified(mh);
 
1620
}
 
1621
 
 
1622
void Document::AnnotationSetStyles(int line, const unsigned char *styles) {
 
1623
        static_cast<LineAnnotation *>(perLineData[ldAnnotation])->SetStyles(line, styles);
 
1624
}
 
1625
 
 
1626
int Document::AnnotationLength(int line) const {
 
1627
        return static_cast<LineAnnotation *>(perLineData[ldAnnotation])->Length(line);
 
1628
}
 
1629
 
 
1630
int Document::AnnotationLines(int line) const {
 
1631
        return static_cast<LineAnnotation *>(perLineData[ldAnnotation])->Lines(line);
 
1632
}
 
1633
 
 
1634
void Document::AnnotationClearAll() {
 
1635
        int maxEditorLine = LinesTotal();
 
1636
        for (int l=0; l<maxEditorLine; l++)
 
1637
                AnnotationSetText(l, 0);
 
1638
        // Free remaining data
 
1639
        static_cast<LineAnnotation *>(perLineData[ldAnnotation])->ClearAll();
 
1640
}
 
1641
 
 
1642
void Document::IncrementStyleClock() {
 
1643
        styleClock = (styleClock + 1) % 0x100000;
 
1644
}
 
1645
 
 
1646
void SCI_METHOD Document::DecorationFillRange(int position, int value, int fillLength) {
 
1647
        if (decorations.FillRange(position, value, fillLength)) {
 
1648
                DocModification mh(SC_MOD_CHANGEINDICATOR | SC_PERFORMED_USER,
 
1649
                                                        position, fillLength);
 
1650
                NotifyModified(mh);
 
1651
        }
 
1652
}
 
1653
 
 
1654
bool Document::AddWatcher(DocWatcher *watcher, void *userData) {
 
1655
        for (int i = 0; i < lenWatchers; i++) {
 
1656
                if ((watchers[i].watcher == watcher) &&
 
1657
                        (watchers[i].userData == userData))
 
1658
                        return false;
 
1659
        }
 
1660
        WatcherWithUserData *pwNew = new WatcherWithUserData[lenWatchers + 1];
 
1661
        for (int j = 0; j < lenWatchers; j++)
 
1662
                pwNew[j] = watchers[j];
 
1663
        pwNew[lenWatchers].watcher = watcher;
 
1664
        pwNew[lenWatchers].userData = userData;
 
1665
        delete []watchers;
 
1666
        watchers = pwNew;
 
1667
        lenWatchers++;
 
1668
        return true;
 
1669
}
 
1670
 
 
1671
bool Document::RemoveWatcher(DocWatcher *watcher, void *userData) {
 
1672
        for (int i = 0; i < lenWatchers; i++) {
 
1673
                if ((watchers[i].watcher == watcher) &&
 
1674
                        (watchers[i].userData == userData)) {
 
1675
                        if (lenWatchers == 1) {
 
1676
                                delete []watchers;
 
1677
                                watchers = 0;
 
1678
                                lenWatchers = 0;
 
1679
                        } else {
 
1680
                                WatcherWithUserData *pwNew = new WatcherWithUserData[lenWatchers];
 
1681
                                for (int j = 0; j < lenWatchers - 1; j++) {
 
1682
                                        pwNew[j] = (j < i) ? watchers[j] : watchers[j + 1];
 
1683
                                }
 
1684
                                delete []watchers;
 
1685
                                watchers = pwNew;
 
1686
                                lenWatchers--;
 
1687
                        }
 
1688
                        return true;
 
1689
                }
 
1690
        }
 
1691
        return false;
 
1692
}
 
1693
 
 
1694
void Document::NotifyModifyAttempt() {
 
1695
        for (int i = 0; i < lenWatchers; i++) {
 
1696
                watchers[i].watcher->NotifyModifyAttempt(this, watchers[i].userData);
 
1697
        }
 
1698
}
 
1699
 
 
1700
void Document::NotifySavePoint(bool atSavePoint) {
 
1701
        for (int i = 0; i < lenWatchers; i++) {
 
1702
                watchers[i].watcher->NotifySavePoint(this, watchers[i].userData, atSavePoint);
 
1703
        }
 
1704
}
 
1705
 
 
1706
void Document::NotifyModified(DocModification mh) {
 
1707
        if (mh.modificationType & SC_MOD_INSERTTEXT) {
 
1708
                decorations.InsertSpace(mh.position, mh.length);
 
1709
        } else if (mh.modificationType & SC_MOD_DELETETEXT) {
 
1710
                decorations.DeleteRange(mh.position, mh.length);
 
1711
        }
 
1712
        for (int i = 0; i < lenWatchers; i++) {
 
1713
                watchers[i].watcher->NotifyModified(this, mh, watchers[i].userData);
 
1714
        }
 
1715
}
 
1716
 
 
1717
bool Document::IsWordPartSeparator(char ch) {
 
1718
        return (WordCharClass(ch) == CharClassify::ccWord) && IsPunctuation(ch);
 
1719
}
 
1720
 
 
1721
int Document::WordPartLeft(int pos) {
 
1722
        if (pos > 0) {
 
1723
                --pos;
 
1724
                char startChar = cb.CharAt(pos);
 
1725
                if (IsWordPartSeparator(startChar)) {
 
1726
                        while (pos > 0 && IsWordPartSeparator(cb.CharAt(pos))) {
 
1727
                                --pos;
 
1728
                        }
 
1729
                }
 
1730
                if (pos > 0) {
 
1731
                        startChar = cb.CharAt(pos);
 
1732
                        --pos;
 
1733
                        if (IsLowerCase(startChar)) {
 
1734
                                while (pos > 0 && IsLowerCase(cb.CharAt(pos)))
 
1735
                                        --pos;
 
1736
                                if (!IsUpperCase(cb.CharAt(pos)) && !IsLowerCase(cb.CharAt(pos)))
 
1737
                                        ++pos;
 
1738
                        } else if (IsUpperCase(startChar)) {
 
1739
                                while (pos > 0 && IsUpperCase(cb.CharAt(pos)))
 
1740
                                        --pos;
 
1741
                                if (!IsUpperCase(cb.CharAt(pos)))
 
1742
                                        ++pos;
 
1743
                        } else if (IsADigit(startChar)) {
 
1744
                                while (pos > 0 && IsADigit(cb.CharAt(pos)))
 
1745
                                        --pos;
 
1746
                                if (!IsADigit(cb.CharAt(pos)))
 
1747
                                        ++pos;
 
1748
                        } else if (IsPunctuation(startChar)) {
 
1749
                                while (pos > 0 && IsPunctuation(cb.CharAt(pos)))
 
1750
                                        --pos;
 
1751
                                if (!IsPunctuation(cb.CharAt(pos)))
 
1752
                                        ++pos;
 
1753
                        } else if (isspacechar(startChar)) {
 
1754
                                while (pos > 0 && isspacechar(cb.CharAt(pos)))
 
1755
                                        --pos;
 
1756
                                if (!isspacechar(cb.CharAt(pos)))
 
1757
                                        ++pos;
 
1758
                        } else if (!isascii(startChar)) {
 
1759
                                while (pos > 0 && !isascii(cb.CharAt(pos)))
 
1760
                                        --pos;
 
1761
                                if (isascii(cb.CharAt(pos)))
 
1762
                                        ++pos;
 
1763
                        } else {
 
1764
                                ++pos;
 
1765
                        }
 
1766
                }
 
1767
        }
 
1768
        return pos;
 
1769
}
 
1770
 
 
1771
int Document::WordPartRight(int pos) {
 
1772
        char startChar = cb.CharAt(pos);
 
1773
        int length = Length();
 
1774
        if (IsWordPartSeparator(startChar)) {
 
1775
                while (pos < length && IsWordPartSeparator(cb.CharAt(pos)))
 
1776
                        ++pos;
 
1777
                startChar = cb.CharAt(pos);
 
1778
        }
 
1779
        if (!isascii(startChar)) {
 
1780
                while (pos < length && !isascii(cb.CharAt(pos)))
 
1781
                        ++pos;
 
1782
        } else if (IsLowerCase(startChar)) {
 
1783
                while (pos < length && IsLowerCase(cb.CharAt(pos)))
 
1784
                        ++pos;
 
1785
        } else if (IsUpperCase(startChar)) {
 
1786
                if (IsLowerCase(cb.CharAt(pos + 1))) {
 
1787
                        ++pos;
 
1788
                        while (pos < length && IsLowerCase(cb.CharAt(pos)))
 
1789
                                ++pos;
 
1790
                } else {
 
1791
                        while (pos < length && IsUpperCase(cb.CharAt(pos)))
 
1792
                                ++pos;
 
1793
                }
 
1794
                if (IsLowerCase(cb.CharAt(pos)) && IsUpperCase(cb.CharAt(pos - 1)))
 
1795
                        --pos;
 
1796
        } else if (IsADigit(startChar)) {
 
1797
                while (pos < length && IsADigit(cb.CharAt(pos)))
 
1798
                        ++pos;
 
1799
        } else if (IsPunctuation(startChar)) {
 
1800
                while (pos < length && IsPunctuation(cb.CharAt(pos)))
 
1801
                        ++pos;
 
1802
        } else if (isspacechar(startChar)) {
 
1803
                while (pos < length && isspacechar(cb.CharAt(pos)))
 
1804
                        ++pos;
 
1805
        } else {
 
1806
                ++pos;
 
1807
        }
 
1808
        return pos;
 
1809
}
 
1810
 
 
1811
bool IsLineEndChar(char c) {
 
1812
        return (c == '\n' || c == '\r');
 
1813
}
 
1814
 
 
1815
int Document::ExtendStyleRange(int pos, int delta, bool singleLine) {
 
1816
        int sStart = cb.StyleAt(pos);
 
1817
        if (delta < 0) {
 
1818
                while (pos > 0 && (cb.StyleAt(pos) == sStart) && (!singleLine || !IsLineEndChar(cb.CharAt(pos))))
 
1819
                        pos--;
 
1820
                pos++;
 
1821
        } else {
 
1822
                while (pos < (Length()) && (cb.StyleAt(pos) == sStart) && (!singleLine || !IsLineEndChar(cb.CharAt(pos))))
 
1823
                        pos++;
 
1824
        }
 
1825
        return pos;
 
1826
}
 
1827
 
 
1828
static char BraceOpposite(char ch) {
 
1829
        switch (ch) {
 
1830
        case '(':
 
1831
                return ')';
 
1832
        case ')':
 
1833
                return '(';
 
1834
        case '[':
 
1835
                return ']';
 
1836
        case ']':
 
1837
                return '[';
 
1838
        case '{':
 
1839
                return '}';
 
1840
        case '}':
 
1841
                return '{';
 
1842
        case '<':
 
1843
                return '>';
 
1844
        case '>':
 
1845
                return '<';
 
1846
        default:
 
1847
                return '\0';
 
1848
        }
 
1849
}
 
1850
 
 
1851
// TODO: should be able to extend styled region to find matching brace
 
1852
int Document::BraceMatch(int position, int /*maxReStyle*/) {
 
1853
        char chBrace = CharAt(position);
 
1854
        char chSeek = BraceOpposite(chBrace);
 
1855
        if (chSeek == '\0')
 
1856
                return - 1;
 
1857
        char styBrace = static_cast<char>(StyleAt(position) & stylingBitsMask);
 
1858
        int direction = -1;
 
1859
        if (chBrace == '(' || chBrace == '[' || chBrace == '{' || chBrace == '<')
 
1860
                direction = 1;
 
1861
        int depth = 1;
 
1862
        position = NextPosition(position, direction);
 
1863
        while ((position >= 0) && (position < Length())) {
 
1864
                char chAtPos = CharAt(position);
 
1865
                char styAtPos = static_cast<char>(StyleAt(position) & stylingBitsMask);
 
1866
                if ((position > GetEndStyled()) || (styAtPos == styBrace)) {
 
1867
                        if (chAtPos == chBrace)
 
1868
                                depth++;
 
1869
                        if (chAtPos == chSeek)
 
1870
                                depth--;
 
1871
                        if (depth == 0)
 
1872
                                return position;
 
1873
                }
 
1874
                int positionBeforeMove = position;
 
1875
                position = NextPosition(position, direction);
 
1876
                if (position == positionBeforeMove)
 
1877
                        break;
 
1878
        }
 
1879
        return - 1;
 
1880
}
 
1881
 
 
1882
/**
 
1883
 * Implementation of RegexSearchBase for the default built-in regular expression engine
 
1884
 */
 
1885
class BuiltinRegex : public RegexSearchBase {
 
1886
public:
 
1887
        BuiltinRegex(CharClassify *charClassTable) : search(charClassTable), substituted(NULL) {}
 
1888
 
 
1889
        virtual ~BuiltinRegex() {
 
1890
                delete substituted;
 
1891
        }
 
1892
 
 
1893
        virtual long FindText(Document *doc, int minPos, int maxPos, const char *s,
 
1894
                        bool caseSensitive, bool word, bool wordStart, int flags,
 
1895
                        int *length);
 
1896
 
 
1897
        virtual const char *SubstituteByPosition(Document *doc, const char *text, int *length);
 
1898
 
 
1899
private:
 
1900
        RESearch search;
 
1901
        char *substituted;
 
1902
};
 
1903
 
 
1904
// Define a way for the Regular Expression code to access the document
 
1905
class DocumentIndexer : public CharacterIndexer {
 
1906
        Document *pdoc;
 
1907
        int end;
 
1908
public:
 
1909
        DocumentIndexer(Document *pdoc_, int end_) :
 
1910
                pdoc(pdoc_), end(end_) {
 
1911
        }
 
1912
 
 
1913
        virtual ~DocumentIndexer() {
 
1914
        }
 
1915
 
 
1916
        virtual char CharAt(int index) {
 
1917
                if (index < 0 || index >= end)
 
1918
                        return 0;
 
1919
                else
 
1920
                        return pdoc->CharAt(index);
 
1921
        }
 
1922
};
 
1923
 
 
1924
long BuiltinRegex::FindText(Document *doc, int minPos, int maxPos, const char *s,
 
1925
                        bool caseSensitive, bool, bool, int flags,
 
1926
                        int *length) {
 
1927
        bool posix = (flags & SCFIND_POSIX) != 0;
 
1928
        int increment = (minPos <= maxPos) ? 1 : -1;
 
1929
 
 
1930
        int startPos = minPos;
 
1931
        int endPos = maxPos;
 
1932
 
 
1933
        // Range endpoints should not be inside DBCS characters, but just in case, move them.
 
1934
        startPos = doc->MovePositionOutsideChar(startPos, 1, false);
 
1935
        endPos = doc->MovePositionOutsideChar(endPos, 1, false);
 
1936
 
 
1937
        const char *errmsg = search.Compile(s, *length, caseSensitive, posix);
 
1938
        if (errmsg) {
 
1939
                return -1;
 
1940
        }
 
1941
        // Find a variable in a property file: \$(\([A-Za-z0-9_.]+\))
 
1942
        // Replace first '.' with '-' in each property file variable reference:
 
1943
        //     Search: \$(\([A-Za-z0-9_-]+\)\.\([A-Za-z0-9_.]+\))
 
1944
        //     Replace: $(\1-\2)
 
1945
        int lineRangeStart = doc->LineFromPosition(startPos);
 
1946
        int lineRangeEnd = doc->LineFromPosition(endPos);
 
1947
        if ((increment == 1) &&
 
1948
                (startPos >= doc->LineEnd(lineRangeStart)) &&
 
1949
                (lineRangeStart < lineRangeEnd)) {
 
1950
                // the start position is at end of line or between line end characters.
 
1951
                lineRangeStart++;
 
1952
                startPos = doc->LineStart(lineRangeStart);
 
1953
        }
 
1954
        int pos = -1;
 
1955
        int lenRet = 0;
 
1956
        char searchEnd = s[*length - 1];
 
1957
        int lineRangeBreak = lineRangeEnd + increment;
 
1958
        for (int line = lineRangeStart; line != lineRangeBreak; line += increment) {
 
1959
                int startOfLine = doc->LineStart(line);
 
1960
                int endOfLine = doc->LineEnd(line);
 
1961
                if (increment == 1) {
 
1962
                        if (line == lineRangeStart) {
 
1963
                                if ((startPos != startOfLine) && (s[0] == '^'))
 
1964
                                        continue;       // Can't match start of line if start position after start of line
 
1965
                                startOfLine = startPos;
 
1966
                        }
 
1967
                        if (line == lineRangeEnd) {
 
1968
                                if ((endPos != endOfLine) && (searchEnd == '$'))
 
1969
                                        continue;       // Can't match end of line if end position before end of line
 
1970
                                endOfLine = endPos;
 
1971
                        }
 
1972
                } else {
 
1973
                        if (line == lineRangeEnd) {
 
1974
                                if ((endPos != startOfLine) && (s[0] == '^'))
 
1975
                                        continue;       // Can't match start of line if end position after start of line
 
1976
                                startOfLine = endPos;
 
1977
                        }
 
1978
                        if (line == lineRangeStart) {
 
1979
                                if ((startPos != endOfLine) && (searchEnd == '$'))
 
1980
                                        continue;       // Can't match end of line if start position before end of line
 
1981
                                endOfLine = startPos;
 
1982
                        }
 
1983
                }
 
1984
 
 
1985
                DocumentIndexer di(doc, endOfLine);
 
1986
                int success = search.Execute(di, startOfLine, endOfLine);
 
1987
                if (success) {
 
1988
                        pos = search.bopat[0];
 
1989
                        lenRet = search.eopat[0] - search.bopat[0];
 
1990
                        if (increment == -1) {
 
1991
                                // Check for the last match on this line.
 
1992
                                int repetitions = 1000; // Break out of infinite loop
 
1993
                                while (success && (search.eopat[0] <= endOfLine) && (repetitions--)) {
 
1994
                                        success = search.Execute(di, pos+1, endOfLine);
 
1995
                                        if (success) {
 
1996
                                                if (search.eopat[0] <= minPos) {
 
1997
                                                        pos = search.bopat[0];
 
1998
                                                        lenRet = search.eopat[0] - search.bopat[0];
 
1999
                                                } else {
 
2000
                                                        success = 0;
 
2001
                                                }
 
2002
                                        }
 
2003
                                }
 
2004
                        }
 
2005
                        break;
 
2006
                }
 
2007
        }
 
2008
        *length = lenRet;
 
2009
        return pos;
 
2010
}
 
2011
 
 
2012
const char *BuiltinRegex::SubstituteByPosition(Document *doc, const char *text, int *length) {
 
2013
        delete []substituted;
 
2014
        substituted = 0;
 
2015
        DocumentIndexer di(doc, doc->Length());
 
2016
        if (!search.GrabMatches(di))
 
2017
                return 0;
 
2018
        unsigned int lenResult = 0;
 
2019
        for (int i = 0; i < *length; i++) {
 
2020
                if (text[i] == '\\') {
 
2021
                        if (text[i + 1] >= '1' && text[i + 1] <= '9') {
 
2022
                                unsigned int patNum = text[i + 1] - '0';
 
2023
                                lenResult += search.eopat[patNum] - search.bopat[patNum];
 
2024
                                i++;
 
2025
                        } else {
 
2026
                                switch (text[i + 1]) {
 
2027
                                case 'a':
 
2028
                                case 'b':
 
2029
                                case 'f':
 
2030
                                case 'n':
 
2031
                                case 'r':
 
2032
                                case 't':
 
2033
                                case 'v':
 
2034
                                case '\\':
 
2035
                                        i++;
 
2036
                                }
 
2037
                                lenResult++;
 
2038
                        }
 
2039
                } else {
 
2040
                        lenResult++;
 
2041
                }
 
2042
        }
 
2043
        substituted = new char[lenResult + 1];
 
2044
        char *o = substituted;
 
2045
        for (int j = 0; j < *length; j++) {
 
2046
                if (text[j] == '\\') {
 
2047
                        if (text[j + 1] >= '1' && text[j + 1] <= '9') {
 
2048
                                unsigned int patNum = text[j + 1] - '0';
 
2049
                                unsigned int len = search.eopat[patNum] - search.bopat[patNum];
 
2050
                                if (search.pat[patNum]) // Will be null if try for a match that did not occur
 
2051
                                        memcpy(o, search.pat[patNum], len);
 
2052
                                o += len;
 
2053
                                j++;
 
2054
                        } else {
 
2055
                                j++;
 
2056
                                switch (text[j]) {
 
2057
                                case 'a':
 
2058
                                        *o++ = '\a';
 
2059
                                        break;
 
2060
                                case 'b':
 
2061
                                        *o++ = '\b';
 
2062
                                        break;
 
2063
                                case 'f':
 
2064
                                        *o++ = '\f';
 
2065
                                        break;
 
2066
                                case 'n':
 
2067
                                        *o++ = '\n';
 
2068
                                        break;
 
2069
                                case 'r':
 
2070
                                        *o++ = '\r';
 
2071
                                        break;
 
2072
                                case 't':
 
2073
                                        *o++ = '\t';
 
2074
                                        break;
 
2075
                                case 'v':
 
2076
                                        *o++ = '\v';
 
2077
                                        break;
 
2078
                                case '\\':
 
2079
                                        *o++ = '\\';
 
2080
                                        break;
 
2081
                                default:
 
2082
                                        *o++ = '\\';
 
2083
                                        j--;
 
2084
                                }
 
2085
                        }
 
2086
                } else {
 
2087
                        *o++ = text[j];
 
2088
                }
 
2089
        }
 
2090
        *o = '\0';
 
2091
        *length = lenResult;
 
2092
        return substituted;
 
2093
}
 
2094
 
 
2095
#ifndef SCI_OWNREGEX
 
2096
 
 
2097
#ifdef SCI_NAMESPACE
 
2098
 
 
2099
RegexSearchBase *Scintilla::CreateRegexSearch(CharClassify *charClassTable) {
 
2100
        return new BuiltinRegex(charClassTable);
 
2101
}
 
2102
 
 
2103
#else
 
2104
 
 
2105
RegexSearchBase *CreateRegexSearch(CharClassify *charClassTable) {
 
2106
        return new BuiltinRegex(charClassTable);
 
2107
}
 
2108
 
 
2109
#endif
 
2110
 
 
2111
#endif