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
GetMarkerHandleNumber(int which) const58 MarkerHandleNumber const *MarkerHandleSet::GetMarkerHandleNumber(int which) const noexcept {
59 for (const MarkerHandleNumber &mhn : mhList) {
60 if (which == 0)
61 return &mhn;
62 which--;
63 }
64 return nullptr;
65 }
66
InsertHandle(int handle,int markerNum)67 bool MarkerHandleSet::InsertHandle(int handle, int markerNum) {
68 mhList.push_front(MarkerHandleNumber(handle, markerNum));
69 return true;
70 }
71
RemoveHandle(int handle)72 void MarkerHandleSet::RemoveHandle(int handle) {
73 mhList.remove_if([handle](const MarkerHandleNumber &mhn) noexcept { return mhn.handle == handle; });
74 }
75
RemoveNumber(int markerNum,bool all)76 bool MarkerHandleSet::RemoveNumber(int markerNum, bool all) {
77 bool performedDeletion = false;
78 mhList.remove_if([&](const MarkerHandleNumber &mhn) noexcept {
79 if ((all || !performedDeletion) && (mhn.number == markerNum)) {
80 performedDeletion = true;
81 return true;
82 }
83 return false;
84 });
85 return performedDeletion;
86 }
87
CombineWith(MarkerHandleSet * other)88 void MarkerHandleSet::CombineWith(MarkerHandleSet *other) noexcept {
89 mhList.splice_after(mhList.before_begin(), other->mhList);
90 }
91
~LineMarkers()92 LineMarkers::~LineMarkers() {
93 }
94
Init()95 void LineMarkers::Init() {
96 markers.DeleteAll();
97 }
98
InsertLine(Sci::Line line)99 void LineMarkers::InsertLine(Sci::Line line) {
100 if (markers.Length()) {
101 markers.Insert(line, 0);
102 }
103 }
104
InsertLines(Sci::Line line,Sci::Line lines)105 void LineMarkers::InsertLines(Sci::Line line, Sci::Line lines) {
106 if (markers.Length()) {
107 markers.InsertEmpty(line, lines);
108 }
109 }
110
RemoveLine(Sci::Line line)111 void LineMarkers::RemoveLine(Sci::Line line) {
112 // Retain the markers from the deleted line by oring them into the previous line
113 if (markers.Length()) {
114 if (line > 0) {
115 MergeMarkers(line - 1);
116 }
117 markers.Delete(line);
118 }
119 }
120
LineFromHandle(int markerHandle) const121 Sci::Line LineMarkers::LineFromHandle(int markerHandle) const noexcept {
122 for (Sci::Line line = 0; line < markers.Length(); line++) {
123 if (markers[line] && markers[line]->Contains(markerHandle)) {
124 return line;
125 }
126 }
127 return -1;
128 }
129
HandleFromLine(Sci::Line line,int which) const130 int LineMarkers::HandleFromLine(Sci::Line line, int which) const noexcept {
131 if (markers.Length() && (line >= 0) && (line < markers.Length()) && markers[line]) {
132 MarkerHandleNumber const *pnmh = markers[line]->GetMarkerHandleNumber(which);
133 return pnmh ? pnmh->handle : -1;
134 }
135 return -1;
136 }
137
NumberFromLine(Sci::Line line,int which) const138 int LineMarkers::NumberFromLine(Sci::Line line, int which) const noexcept {
139 if (markers.Length() && (line >= 0) && (line < markers.Length()) && markers[line]) {
140 MarkerHandleNumber const *pnmh = markers[line]->GetMarkerHandleNumber(which);
141 return pnmh ? pnmh->number : -1;
142 }
143 return -1;
144 }
145
MergeMarkers(Sci::Line line)146 void LineMarkers::MergeMarkers(Sci::Line line) {
147 if (markers[line + 1]) {
148 if (!markers[line])
149 markers[line] = std::make_unique<MarkerHandleSet>();
150 markers[line]->CombineWith(markers[line + 1].get());
151 markers[line + 1].reset();
152 }
153 }
154
MarkValue(Sci::Line line) const155 int LineMarkers::MarkValue(Sci::Line line) const noexcept {
156 if (markers.Length() && (line >= 0) && (line < markers.Length()) && markers[line])
157 return markers[line]->MarkValue();
158 else
159 return 0;
160 }
161
MarkerNext(Sci::Line lineStart,int mask) const162 Sci::Line LineMarkers::MarkerNext(Sci::Line lineStart, int mask) const noexcept {
163 if (lineStart < 0)
164 lineStart = 0;
165 const Sci::Line length = markers.Length();
166 for (Sci::Line iLine = lineStart; iLine < length; iLine++) {
167 const MarkerHandleSet *onLine = markers[iLine].get();
168 if (onLine && ((onLine->MarkValue() & mask) != 0))
169 return iLine;
170 }
171 return -1;
172 }
173
AddMark(Sci::Line line,int markerNum,Sci::Line lines)174 int LineMarkers::AddMark(Sci::Line line, int markerNum, Sci::Line lines) {
175 handleCurrent++;
176 if (!markers.Length()) {
177 // No existing markers so allocate one element per line
178 markers.InsertEmpty(0, lines);
179 }
180 if (line >= markers.Length()) {
181 return -1;
182 }
183 if (!markers[line]) {
184 // Need new structure to hold marker handle
185 markers[line] = std::make_unique<MarkerHandleSet>();
186 }
187 markers[line]->InsertHandle(handleCurrent, markerNum);
188
189 return handleCurrent;
190 }
191
DeleteMark(Sci::Line line,int markerNum,bool all)192 bool LineMarkers::DeleteMark(Sci::Line line, int markerNum, bool all) {
193 bool someChanges = false;
194 if (markers.Length() && (line >= 0) && (line < markers.Length()) && markers[line]) {
195 if (markerNum == -1) {
196 someChanges = true;
197 markers[line].reset();
198 } else {
199 someChanges = markers[line]->RemoveNumber(markerNum, all);
200 if (markers[line]->Empty()) {
201 markers[line].reset();
202 }
203 }
204 }
205 return someChanges;
206 }
207
DeleteMarkFromHandle(int markerHandle)208 void LineMarkers::DeleteMarkFromHandle(int markerHandle) {
209 const Sci::Line line = LineFromHandle(markerHandle);
210 if (line >= 0) {
211 markers[line]->RemoveHandle(markerHandle);
212 if (markers[line]->Empty()) {
213 markers[line].reset();
214 }
215 }
216 }
217
~LineLevels()218 LineLevels::~LineLevels() {
219 }
220
Init()221 void LineLevels::Init() {
222 levels.DeleteAll();
223 }
224
InsertLine(Sci::Line line)225 void LineLevels::InsertLine(Sci::Line line) {
226 if (levels.Length()) {
227 const int level = (line < levels.Length()) ? levels[line] : SC_FOLDLEVELBASE;
228 levels.Insert(line, level);
229 }
230 }
231
InsertLines(Sci::Line line,Sci::Line lines)232 void LineLevels::InsertLines(Sci::Line line, Sci::Line lines) {
233 if (levels.Length()) {
234 const int level = (line < levels.Length()) ? levels[line] : SC_FOLDLEVELBASE;
235 levels.InsertValue(line, lines, level);
236 }
237 }
238
RemoveLine(Sci::Line line)239 void LineLevels::RemoveLine(Sci::Line line) {
240 if (levels.Length()) {
241 // Move up following lines but merge header flag from this line
242 // to line before to avoid a temporary disappearance causing expansion.
243 int firstHeader = levels[line] & SC_FOLDLEVELHEADERFLAG;
244 levels.Delete(line);
245 if (line == levels.Length()-1) // Last line loses the header flag
246 levels[line-1] &= ~SC_FOLDLEVELHEADERFLAG;
247 else if (line > 0)
248 levels[line-1] |= firstHeader;
249 }
250 }
251
ExpandLevels(Sci::Line sizeNew)252 void LineLevels::ExpandLevels(Sci::Line sizeNew) {
253 levels.InsertValue(levels.Length(), sizeNew - levels.Length(), SC_FOLDLEVELBASE);
254 }
255
ClearLevels()256 void LineLevels::ClearLevels() {
257 levels.DeleteAll();
258 }
259
SetLevel(Sci::Line line,int level,Sci::Line lines)260 int LineLevels::SetLevel(Sci::Line line, int level, Sci::Line lines) {
261 int prev = 0;
262 if ((line >= 0) && (line < lines)) {
263 if (!levels.Length()) {
264 ExpandLevels(lines + 1);
265 }
266 prev = levels[line];
267 if (prev != level) {
268 levels[line] = level;
269 }
270 }
271 return prev;
272 }
273
GetLevel(Sci::Line line) const274 int LineLevels::GetLevel(Sci::Line line) const noexcept {
275 if (levels.Length() && (line >= 0) && (line < levels.Length())) {
276 return levels[line];
277 } else {
278 return SC_FOLDLEVELBASE;
279 }
280 }
281
~LineState()282 LineState::~LineState() {
283 }
284
Init()285 void LineState::Init() {
286 lineStates.DeleteAll();
287 }
288
InsertLine(Sci::Line line)289 void LineState::InsertLine(Sci::Line line) {
290 if (lineStates.Length()) {
291 lineStates.EnsureLength(line);
292 const int val = (line < lineStates.Length()) ? lineStates[line] : 0;
293 lineStates.Insert(line, val);
294 }
295 }
296
InsertLines(Sci::Line line,Sci::Line lines)297 void LineState::InsertLines(Sci::Line line, Sci::Line lines) {
298 if (lineStates.Length()) {
299 lineStates.EnsureLength(line);
300 const int val = (line < lineStates.Length()) ? lineStates[line] : 0;
301 lineStates.InsertValue(line, lines, val);
302 }
303 }
304
RemoveLine(Sci::Line line)305 void LineState::RemoveLine(Sci::Line line) {
306 if (lineStates.Length() > line) {
307 lineStates.Delete(line);
308 }
309 }
310
SetLineState(Sci::Line line,int state)311 int LineState::SetLineState(Sci::Line line, int state) {
312 lineStates.EnsureLength(line + 1);
313 const int stateOld = lineStates[line];
314 lineStates[line] = state;
315 return stateOld;
316 }
317
GetLineState(Sci::Line line)318 int LineState::GetLineState(Sci::Line line) {
319 if (line < 0)
320 return 0;
321 lineStates.EnsureLength(line + 1);
322 return lineStates[line];
323 }
324
GetMaxLineState() const325 Sci::Line LineState::GetMaxLineState() const noexcept {
326 return lineStates.Length();
327 }
328
329 // Each allocated LineAnnotation is a char array which starts with an AnnotationHeader
330 // and then has text and optional styles.
331
332 struct AnnotationHeader {
333 short style; // Style IndividualStyles implies array of styles
334 short lines;
335 int length;
336 };
337
338 namespace {
339
340 constexpr int IndividualStyles = 0x100;
341
NumberLines(std::string_view sv)342 size_t NumberLines(std::string_view sv) {
343 return std::count(sv.begin(), sv.end(), '\n') + 1;
344 }
345
AllocateAnnotation(size_t length,int style)346 std::unique_ptr<char[]>AllocateAnnotation(size_t length, int style) {
347 const size_t len = sizeof(AnnotationHeader) + length + ((style == IndividualStyles) ? length : 0);
348 return std::make_unique<char[]>(len);
349 }
350
351 }
352
~LineAnnotation()353 LineAnnotation::~LineAnnotation() {
354 }
355
Init()356 void LineAnnotation::Init() {
357 ClearAll();
358 }
359
InsertLine(Sci::Line line)360 void LineAnnotation::InsertLine(Sci::Line line) {
361 if (annotations.Length()) {
362 annotations.EnsureLength(line);
363 annotations.Insert(line, std::unique_ptr<char []>());
364 }
365 }
366
InsertLines(Sci::Line line,Sci::Line lines)367 void LineAnnotation::InsertLines(Sci::Line line, Sci::Line lines) {
368 if (annotations.Length()) {
369 annotations.EnsureLength(line);
370 annotations.InsertEmpty(line, lines);
371 }
372 }
373
RemoveLine(Sci::Line line)374 void LineAnnotation::RemoveLine(Sci::Line line) {
375 if (annotations.Length() && (line > 0) && (line <= annotations.Length())) {
376 annotations[line-1].reset();
377 annotations.Delete(line-1);
378 }
379 }
380
MultipleStyles(Sci::Line line) const381 bool LineAnnotation::MultipleStyles(Sci::Line line) const noexcept {
382 if (annotations.Length() && (line >= 0) && (line < annotations.Length()) && annotations[line])
383 return reinterpret_cast<AnnotationHeader *>(annotations[line].get())->style == IndividualStyles;
384 else
385 return false;
386 }
387
Style(Sci::Line line) const388 int LineAnnotation::Style(Sci::Line line) const noexcept {
389 if (annotations.Length() && (line >= 0) && (line < annotations.Length()) && annotations[line])
390 return reinterpret_cast<AnnotationHeader *>(annotations[line].get())->style;
391 else
392 return 0;
393 }
394
Text(Sci::Line line) const395 const char *LineAnnotation::Text(Sci::Line line) const noexcept {
396 if (annotations.Length() && (line >= 0) && (line < annotations.Length()) && annotations[line])
397 return annotations[line].get()+sizeof(AnnotationHeader);
398 else
399 return nullptr;
400 }
401
Styles(Sci::Line line) const402 const unsigned char *LineAnnotation::Styles(Sci::Line line) const noexcept {
403 if (annotations.Length() && (line >= 0) && (line < annotations.Length()) && annotations[line] && MultipleStyles(line))
404 return reinterpret_cast<unsigned char *>(annotations[line].get() + sizeof(AnnotationHeader) + Length(line));
405 else
406 return nullptr;
407 }
408
SetText(Sci::Line line,const char * text)409 void LineAnnotation::SetText(Sci::Line line, const char *text) {
410 if (text && (line >= 0)) {
411 annotations.EnsureLength(line+1);
412 const int style = Style(line);
413 annotations[line] = AllocateAnnotation(strlen(text), style);
414 char *pa = annotations[line].get();
415 assert(pa);
416 AnnotationHeader *pah = reinterpret_cast<AnnotationHeader *>(pa);
417 pah->style = static_cast<short>(style);
418 pah->length = static_cast<int>(strlen(text));
419 pah->lines = static_cast<short>(NumberLines(text));
420 memcpy(pa+sizeof(AnnotationHeader), text, pah->length);
421 } else {
422 if (annotations.Length() && (line >= 0) && (line < annotations.Length()) && annotations[line]) {
423 annotations[line].reset();
424 }
425 }
426 }
427
ClearAll()428 void LineAnnotation::ClearAll() {
429 annotations.DeleteAll();
430 }
431
SetStyle(Sci::Line line,int style)432 void LineAnnotation::SetStyle(Sci::Line line, int style) {
433 annotations.EnsureLength(line+1);
434 if (!annotations[line]) {
435 annotations[line] = AllocateAnnotation(0, style);
436 }
437 reinterpret_cast<AnnotationHeader *>(annotations[line].get())->style = static_cast<short>(style);
438 }
439
SetStyles(Sci::Line line,const unsigned char * styles)440 void LineAnnotation::SetStyles(Sci::Line line, const unsigned char *styles) {
441 if (line >= 0) {
442 annotations.EnsureLength(line+1);
443 if (!annotations[line]) {
444 annotations[line] = AllocateAnnotation(0, IndividualStyles);
445 } else {
446 const AnnotationHeader *pahSource = reinterpret_cast<AnnotationHeader *>(annotations[line].get());
447 if (pahSource->style != IndividualStyles) {
448 std::unique_ptr<char[]>allocation = AllocateAnnotation(pahSource->length, IndividualStyles);
449 AnnotationHeader *pahAlloc = reinterpret_cast<AnnotationHeader *>(allocation.get());
450 pahAlloc->length = pahSource->length;
451 pahAlloc->lines = pahSource->lines;
452 memcpy(allocation.get() + sizeof(AnnotationHeader), annotations[line].get() + sizeof(AnnotationHeader), pahSource->length);
453 annotations[line] = std::move(allocation);
454 }
455 }
456 AnnotationHeader *pah = reinterpret_cast<AnnotationHeader *>(annotations[line].get());
457 pah->style = IndividualStyles;
458 memcpy(annotations[line].get() + sizeof(AnnotationHeader) + pah->length, styles, pah->length);
459 }
460 }
461
Length(Sci::Line line) const462 int LineAnnotation::Length(Sci::Line line) const noexcept {
463 if (annotations.Length() && (line >= 0) && (line < annotations.Length()) && annotations[line])
464 return reinterpret_cast<AnnotationHeader *>(annotations[line].get())->length;
465 else
466 return 0;
467 }
468
Lines(Sci::Line line) const469 int LineAnnotation::Lines(Sci::Line line) const noexcept {
470 if (annotations.Length() && (line >= 0) && (line < annotations.Length()) && annotations[line])
471 return reinterpret_cast<AnnotationHeader *>(annotations[line].get())->lines;
472 else
473 return 0;
474 }
475
~LineTabstops()476 LineTabstops::~LineTabstops() {
477 }
478
Init()479 void LineTabstops::Init() {
480 tabstops.DeleteAll();
481 }
482
InsertLine(Sci::Line line)483 void LineTabstops::InsertLine(Sci::Line line) {
484 if (tabstops.Length()) {
485 tabstops.EnsureLength(line);
486 tabstops.Insert(line, nullptr);
487 }
488 }
489
InsertLines(Sci::Line line,Sci::Line lines)490 void LineTabstops::InsertLines(Sci::Line line, Sci::Line lines) {
491 if (tabstops.Length()) {
492 tabstops.EnsureLength(line);
493 tabstops.InsertEmpty(line, lines);
494 }
495 }
496
RemoveLine(Sci::Line line)497 void LineTabstops::RemoveLine(Sci::Line line) {
498 if (tabstops.Length() > line) {
499 tabstops[line].reset();
500 tabstops.Delete(line);
501 }
502 }
503
ClearTabstops(Sci::Line line)504 bool LineTabstops::ClearTabstops(Sci::Line line) noexcept {
505 if (line < tabstops.Length()) {
506 TabstopList *tl = tabstops[line].get();
507 if (tl) {
508 tl->clear();
509 return true;
510 }
511 }
512 return false;
513 }
514
AddTabstop(Sci::Line line,int x)515 bool LineTabstops::AddTabstop(Sci::Line line, int x) {
516 tabstops.EnsureLength(line + 1);
517 if (!tabstops[line]) {
518 tabstops[line] = std::make_unique<TabstopList>();
519 }
520
521 TabstopList *tl = tabstops[line].get();
522 if (tl) {
523 // tabstop positions are kept in order - insert in the right place
524 std::vector<int>::iterator it = std::lower_bound(tl->begin(), tl->end(), x);
525 // don't insert duplicates
526 if (it == tl->end() || *it != x) {
527 tl->insert(it, x);
528 return true;
529 }
530 }
531 return false;
532 }
533
GetNextTabstop(Sci::Line line,int x) const534 int LineTabstops::GetNextTabstop(Sci::Line line, int x) const noexcept {
535 if (line < tabstops.Length()) {
536 TabstopList *tl = tabstops[line].get();
537 if (tl) {
538 for (const int i : *tl) {
539 if (i > x) {
540 return i;
541 }
542 }
543 }
544 }
545 return 0;
546 }
547