1 // Scintilla source code edit control
2 /** @file PerLine.cxx
3 ** Manages data associated with each line of the document
4 **/
5 // Copyright 1998-2009 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
7
8 #include <cstddef>
9 #include <cassert>
10 #include <cstring>
11
12 #include <stdexcept>
13 #include <string_view>
14 #include <vector>
15 #include <forward_list>
16 #include <algorithm>
17 #include <memory>
18
19 #include "Platform.h"
20
21 #include "Scintilla.h"
22 #include "Position.h"
23 #include "SplitVector.h"
24 #include "Partitioning.h"
25 #include "CellBuffer.h"
26 #include "PerLine.h"
27
28 using namespace Scintilla;
29
MarkerHandleSet()30 MarkerHandleSet::MarkerHandleSet() {
31 }
32
~MarkerHandleSet()33 MarkerHandleSet::~MarkerHandleSet() {
34 mhList.clear();
35 }
36
Empty() const37 bool MarkerHandleSet::Empty() const noexcept {
38 return mhList.empty();
39 }
40
MarkValue() const41 int MarkerHandleSet::MarkValue() const noexcept {
42 unsigned int m = 0;
43 for (const MarkerHandleNumber &mhn : mhList) {
44 m |= (1 << mhn.number);
45 }
46 return m;
47 }
48
Contains(int handle) const49 bool MarkerHandleSet::Contains(int handle) const noexcept {
50 for (const MarkerHandleNumber &mhn : mhList) {
51 if (mhn.handle == handle) {
52 return true;
53 }
54 }
55 return false;
56 }
57
InsertHandle(int handle,int markerNum)58 bool MarkerHandleSet::InsertHandle(int handle, int markerNum) {
59 mhList.push_front(MarkerHandleNumber(handle, markerNum));
60 return true;
61 }
62
RemoveHandle(int handle)63 void MarkerHandleSet::RemoveHandle(int handle) {
64 mhList.remove_if([handle](const MarkerHandleNumber &mhn) { return mhn.handle == handle; });
65 }
66
RemoveNumber(int markerNum,bool all)67 bool MarkerHandleSet::RemoveNumber(int markerNum, bool all) {
68 bool performedDeletion = false;
69 mhList.remove_if([&](const MarkerHandleNumber &mhn) {
70 if ((all || !performedDeletion) && (mhn.number == markerNum)) {
71 performedDeletion = true;
72 return true;
73 }
74 return false;
75 });
76 return performedDeletion;
77 }
78
CombineWith(MarkerHandleSet * other)79 void MarkerHandleSet::CombineWith(MarkerHandleSet *other) {
80 mhList.splice_after(mhList.before_begin(), other->mhList);
81 }
82
~LineMarkers()83 LineMarkers::~LineMarkers() {
84 }
85
Init()86 void LineMarkers::Init() {
87 markers.DeleteAll();
88 }
89
InsertLine(Sci::Line line)90 void LineMarkers::InsertLine(Sci::Line line) {
91 if (markers.Length()) {
92 markers.Insert(line, 0);
93 }
94 }
95
RemoveLine(Sci::Line line)96 void LineMarkers::RemoveLine(Sci::Line line) {
97 // Retain the markers from the deleted line by oring them into the previous line
98 if (markers.Length()) {
99 if (line > 0) {
100 MergeMarkers(line - 1);
101 }
102 markers.Delete(line);
103 }
104 }
105
LineFromHandle(int markerHandle)106 Sci::Line LineMarkers::LineFromHandle(int markerHandle) {
107 if (markers.Length()) {
108 for (Sci::Line line = 0; line < markers.Length(); line++) {
109 if (markers[line]) {
110 if (markers[line]->Contains(markerHandle)) {
111 return line;
112 }
113 }
114 }
115 }
116 return -1;
117 }
118
MergeMarkers(Sci::Line line)119 void LineMarkers::MergeMarkers(Sci::Line line) {
120 if (markers[line + 1]) {
121 if (!markers[line])
122 markers[line] = std::make_unique<MarkerHandleSet>();
123 markers[line]->CombineWith(markers[line + 1].get());
124 markers[line + 1].reset();
125 }
126 }
127
MarkValue(Sci::Line line)128 int LineMarkers::MarkValue(Sci::Line line) noexcept {
129 if (markers.Length() && (line >= 0) && (line < markers.Length()) && markers[line])
130 return markers[line]->MarkValue();
131 else
132 return 0;
133 }
134
MarkerNext(Sci::Line lineStart,int mask) const135 Sci::Line LineMarkers::MarkerNext(Sci::Line lineStart, int mask) const {
136 if (lineStart < 0)
137 lineStart = 0;
138 const Sci::Line length = static_cast<Sci::Line>(markers.Length());
139 for (Sci::Line iLine = lineStart; iLine < length; iLine++) {
140 const MarkerHandleSet *onLine = markers[iLine].get();
141 if (onLine && ((onLine->MarkValue() & mask) != 0))
142 return iLine;
143 }
144 return -1;
145 }
146
AddMark(Sci::Line line,int markerNum,Sci::Line lines)147 int LineMarkers::AddMark(Sci::Line line, int markerNum, Sci::Line lines) {
148 handleCurrent++;
149 if (!markers.Length()) {
150 // No existing markers so allocate one element per line
151 markers.InsertEmpty(0, lines);
152 }
153 if (line >= markers.Length()) {
154 return -1;
155 }
156 if (!markers[line]) {
157 // Need new structure to hold marker handle
158 markers[line] = std::make_unique<MarkerHandleSet>();
159 }
160 markers[line]->InsertHandle(handleCurrent, markerNum);
161
162 return handleCurrent;
163 }
164
DeleteMark(Sci::Line line,int markerNum,bool all)165 bool LineMarkers::DeleteMark(Sci::Line line, int markerNum, bool all) {
166 bool someChanges = false;
167 if (markers.Length() && (line >= 0) && (line < markers.Length()) && markers[line]) {
168 if (markerNum == -1) {
169 someChanges = true;
170 markers[line].reset();
171 } else {
172 someChanges = markers[line]->RemoveNumber(markerNum, all);
173 if (markers[line]->Empty()) {
174 markers[line].reset();
175 }
176 }
177 }
178 return someChanges;
179 }
180
DeleteMarkFromHandle(int markerHandle)181 void LineMarkers::DeleteMarkFromHandle(int markerHandle) {
182 const Sci::Line line = LineFromHandle(markerHandle);
183 if (line >= 0) {
184 markers[line]->RemoveHandle(markerHandle);
185 if (markers[line]->Empty()) {
186 markers[line].reset();
187 }
188 }
189 }
190
~LineLevels()191 LineLevels::~LineLevels() {
192 }
193
Init()194 void LineLevels::Init() {
195 levels.DeleteAll();
196 }
197
InsertLine(Sci::Line line)198 void LineLevels::InsertLine(Sci::Line line) {
199 if (levels.Length()) {
200 const int level = (line < levels.Length()) ? levels[line] : SC_FOLDLEVELBASE;
201 levels.InsertValue(line, 1, level);
202 }
203 }
204
RemoveLine(Sci::Line line)205 void LineLevels::RemoveLine(Sci::Line line) {
206 if (levels.Length()) {
207 // Move up following lines but merge header flag from this line
208 // to line before to avoid a temporary disappearence causing expansion.
209 int firstHeader = levels[line] & SC_FOLDLEVELHEADERFLAG;
210 levels.Delete(line);
211 if (line == levels.Length()-1) // Last line loses the header flag
212 levels[line-1] &= ~SC_FOLDLEVELHEADERFLAG;
213 else if (line > 0)
214 levels[line-1] |= firstHeader;
215 }
216 }
217
ExpandLevels(Sci::Line sizeNew)218 void LineLevels::ExpandLevels(Sci::Line sizeNew) {
219 levels.InsertValue(levels.Length(), sizeNew - levels.Length(), SC_FOLDLEVELBASE);
220 }
221
ClearLevels()222 void LineLevels::ClearLevels() {
223 levels.DeleteAll();
224 }
225
SetLevel(Sci::Line line,int level,Sci::Line lines)226 int LineLevels::SetLevel(Sci::Line line, int level, Sci::Line lines) {
227 int prev = 0;
228 if ((line >= 0) && (line < lines)) {
229 if (!levels.Length()) {
230 ExpandLevels(lines + 1);
231 }
232 prev = levels[line];
233 if (prev != level) {
234 levels[line] = level;
235 }
236 }
237 return prev;
238 }
239
GetLevel(Sci::Line line) const240 int LineLevels::GetLevel(Sci::Line line) const {
241 if (levels.Length() && (line >= 0) && (line < levels.Length())) {
242 return levels[line];
243 } else {
244 return SC_FOLDLEVELBASE;
245 }
246 }
247
~LineState()248 LineState::~LineState() {
249 }
250
Init()251 void LineState::Init() {
252 lineStates.DeleteAll();
253 }
254
InsertLine(Sci::Line line)255 void LineState::InsertLine(Sci::Line line) {
256 if (lineStates.Length()) {
257 lineStates.EnsureLength(line);
258 const int val = (line < lineStates.Length()) ? lineStates[line] : 0;
259 lineStates.Insert(line, val);
260 }
261 }
262
RemoveLine(Sci::Line line)263 void LineState::RemoveLine(Sci::Line line) {
264 if (lineStates.Length() > line) {
265 lineStates.Delete(line);
266 }
267 }
268
SetLineState(Sci::Line line,int state)269 int LineState::SetLineState(Sci::Line line, int state) {
270 lineStates.EnsureLength(line + 1);
271 const int stateOld = lineStates[line];
272 lineStates[line] = state;
273 return stateOld;
274 }
275
GetLineState(Sci::Line line)276 int LineState::GetLineState(Sci::Line line) {
277 if (line < 0)
278 return 0;
279 lineStates.EnsureLength(line + 1);
280 return lineStates[line];
281 }
282
GetMaxLineState() const283 Sci::Line LineState::GetMaxLineState() const {
284 return static_cast<Sci::Line>(lineStates.Length());
285 }
286
NumberLines(const char * text)287 static int NumberLines(const char *text) noexcept {
288 if (text) {
289 int newLines = 0;
290 while (*text) {
291 if (*text == '\n')
292 newLines++;
293 text++;
294 }
295 return newLines+1;
296 } else {
297 return 0;
298 }
299 }
300
301 // Each allocated LineAnnotation is a char array which starts with an AnnotationHeader
302 // and then has text and optional styles.
303
304 static const int IndividualStyles = 0x100;
305
306 struct AnnotationHeader {
307 short style; // Style IndividualStyles implies array of styles
308 short lines;
309 int length;
310 };
311
~LineAnnotation()312 LineAnnotation::~LineAnnotation() {
313 }
314
Init()315 void LineAnnotation::Init() {
316 ClearAll();
317 }
318
InsertLine(Sci::Line line)319 void LineAnnotation::InsertLine(Sci::Line line) {
320 if (annotations.Length()) {
321 annotations.EnsureLength(line);
322 annotations.Insert(line, std::unique_ptr<char []>());
323 }
324 }
325
RemoveLine(Sci::Line line)326 void LineAnnotation::RemoveLine(Sci::Line line) {
327 if (annotations.Length() && (line > 0) && (line <= annotations.Length())) {
328 annotations[line-1].reset();
329 annotations.Delete(line-1);
330 }
331 }
332
MultipleStyles(Sci::Line line) const333 bool LineAnnotation::MultipleStyles(Sci::Line line) const {
334 if (annotations.Length() && (line >= 0) && (line < annotations.Length()) && annotations[line])
335 return reinterpret_cast<AnnotationHeader *>(annotations[line].get())->style == IndividualStyles;
336 else
337 return false;
338 }
339
Style(Sci::Line line) const340 int LineAnnotation::Style(Sci::Line line) const {
341 if (annotations.Length() && (line >= 0) && (line < annotations.Length()) && annotations[line])
342 return reinterpret_cast<AnnotationHeader *>(annotations[line].get())->style;
343 else
344 return 0;
345 }
346
Text(Sci::Line line) const347 const char *LineAnnotation::Text(Sci::Line line) const {
348 if (annotations.Length() && (line >= 0) && (line < annotations.Length()) && annotations[line])
349 return annotations[line].get()+sizeof(AnnotationHeader);
350 else
351 return nullptr;
352 }
353
Styles(Sci::Line line) const354 const unsigned char *LineAnnotation::Styles(Sci::Line line) const {
355 if (annotations.Length() && (line >= 0) && (line < annotations.Length()) && annotations[line] && MultipleStyles(line))
356 return reinterpret_cast<unsigned char *>(annotations[line].get() + sizeof(AnnotationHeader) + Length(line));
357 else
358 return nullptr;
359 }
360
AllocateAnnotation(int length,int style)361 static std::unique_ptr<char[]>AllocateAnnotation(int length, int style) {
362 const size_t len = sizeof(AnnotationHeader) + length + ((style == IndividualStyles) ? length : 0);
363 return std::make_unique<char[]>(len);
364 }
365
SetText(Sci::Line line,const char * text)366 void LineAnnotation::SetText(Sci::Line line, const char *text) {
367 if (text && (line >= 0)) {
368 annotations.EnsureLength(line+1);
369 const int style = Style(line);
370 annotations[line] = AllocateAnnotation(static_cast<int>(strlen(text)), style);
371 char *pa = annotations[line].get();
372 assert(pa);
373 AnnotationHeader *pah = reinterpret_cast<AnnotationHeader *>(pa);
374 pah->style = static_cast<short>(style);
375 pah->length = static_cast<int>(strlen(text));
376 pah->lines = static_cast<short>(NumberLines(text));
377 memcpy(pa+sizeof(AnnotationHeader), text, pah->length);
378 } else {
379 if (annotations.Length() && (line >= 0) && (line < annotations.Length()) && annotations[line]) {
380 annotations[line].reset();
381 }
382 }
383 }
384
ClearAll()385 void LineAnnotation::ClearAll() {
386 annotations.DeleteAll();
387 }
388
SetStyle(Sci::Line line,int style)389 void LineAnnotation::SetStyle(Sci::Line line, int style) {
390 annotations.EnsureLength(line+1);
391 if (!annotations[line]) {
392 annotations[line] = AllocateAnnotation(0, style);
393 }
394 reinterpret_cast<AnnotationHeader *>(annotations[line].get())->style = static_cast<short>(style);
395 }
396
SetStyles(Sci::Line line,const unsigned char * styles)397 void LineAnnotation::SetStyles(Sci::Line line, const unsigned char *styles) {
398 if (line >= 0) {
399 annotations.EnsureLength(line+1);
400 if (!annotations[line]) {
401 annotations[line] = AllocateAnnotation(0, IndividualStyles);
402 } else {
403 const AnnotationHeader *pahSource = reinterpret_cast<AnnotationHeader *>(annotations[line].get());
404 if (pahSource->style != IndividualStyles) {
405 std::unique_ptr<char[]>allocation = AllocateAnnotation(pahSource->length, IndividualStyles);
406 AnnotationHeader *pahAlloc = reinterpret_cast<AnnotationHeader *>(allocation.get());
407 pahAlloc->length = pahSource->length;
408 pahAlloc->lines = pahSource->lines;
409 memcpy(allocation.get() + sizeof(AnnotationHeader), annotations[line].get() + sizeof(AnnotationHeader), pahSource->length);
410 annotations[line] = std::move(allocation);
411 }
412 }
413 AnnotationHeader *pah = reinterpret_cast<AnnotationHeader *>(annotations[line].get());
414 pah->style = IndividualStyles;
415 memcpy(annotations[line].get() + sizeof(AnnotationHeader) + pah->length, styles, pah->length);
416 }
417 }
418
Length(Sci::Line line) const419 int LineAnnotation::Length(Sci::Line line) const {
420 if (annotations.Length() && (line >= 0) && (line < annotations.Length()) && annotations[line])
421 return reinterpret_cast<AnnotationHeader *>(annotations[line].get())->length;
422 else
423 return 0;
424 }
425
Lines(Sci::Line line) const426 int LineAnnotation::Lines(Sci::Line line) const {
427 if (annotations.Length() && (line >= 0) && (line < annotations.Length()) && annotations[line])
428 return reinterpret_cast<AnnotationHeader *>(annotations[line].get())->lines;
429 else
430 return 0;
431 }
432
~LineTabstops()433 LineTabstops::~LineTabstops() {
434 }
435
Init()436 void LineTabstops::Init() {
437 tabstops.DeleteAll();
438 }
439
InsertLine(Sci::Line line)440 void LineTabstops::InsertLine(Sci::Line line) {
441 if (tabstops.Length()) {
442 tabstops.EnsureLength(line);
443 tabstops.Insert(line, nullptr);
444 }
445 }
446
RemoveLine(Sci::Line line)447 void LineTabstops::RemoveLine(Sci::Line line) {
448 if (tabstops.Length() > line) {
449 tabstops[line].reset();
450 tabstops.Delete(line);
451 }
452 }
453
ClearTabstops(Sci::Line line)454 bool LineTabstops::ClearTabstops(Sci::Line line) {
455 if (line < tabstops.Length()) {
456 TabstopList *tl = tabstops[line].get();
457 if (tl) {
458 tl->clear();
459 return true;
460 }
461 }
462 return false;
463 }
464
AddTabstop(Sci::Line line,int x)465 bool LineTabstops::AddTabstop(Sci::Line line, int x) {
466 tabstops.EnsureLength(line + 1);
467 if (!tabstops[line]) {
468 tabstops[line] = std::make_unique<TabstopList>();
469 }
470
471 TabstopList *tl = tabstops[line].get();
472 if (tl) {
473 // tabstop positions are kept in order - insert in the right place
474 std::vector<int>::iterator it = std::lower_bound(tl->begin(), tl->end(), x);
475 // don't insert duplicates
476 if (it == tl->end() || *it != x) {
477 tl->insert(it, x);
478 return true;
479 }
480 }
481 return false;
482 }
483
GetNextTabstop(Sci::Line line,int x) const484 int LineTabstops::GetNextTabstop(Sci::Line line, int x) const {
485 if (line < tabstops.Length()) {
486 TabstopList *tl = tabstops[line].get();
487 if (tl) {
488 for (const int i : *tl) {
489 if (i > x) {
490 return i;
491 }
492 }
493 }
494 }
495 return 0;
496 }
497