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