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 | |
32 | using namespace Scintilla::Internal; |
33 | |
34 | MarkerHandleSet::MarkerHandleSet() { |
35 | } |
36 | |
37 | MarkerHandleSet::~MarkerHandleSet() { |
38 | mhList.clear(); |
39 | } |
40 | |
41 | bool MarkerHandleSet::Empty() const noexcept { |
42 | return mhList.empty(); |
43 | } |
44 | |
45 | int 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 | |
53 | bool 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 | |
62 | MarkerHandleNumber 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 | |
71 | bool MarkerHandleSet::InsertHandle(int handle, int markerNum) { |
72 | mhList.push_front(MarkerHandleNumber(handle, markerNum)); |
73 | return true; |
74 | } |
75 | |
76 | void MarkerHandleSet::RemoveHandle(int handle) { |
77 | mhList.remove_if([handle](const MarkerHandleNumber &mhn) noexcept { return mhn.handle == handle; }); |
78 | } |
79 | |
80 | bool 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 | |
92 | void MarkerHandleSet::CombineWith(MarkerHandleSet *other) noexcept { |
93 | mhList.splice_after(mhList.before_begin(), other->mhList); |
94 | } |
95 | |
96 | LineMarkers::~LineMarkers() { |
97 | } |
98 | |
99 | void LineMarkers::Init() { |
100 | markers.DeleteAll(); |
101 | } |
102 | |
103 | void LineMarkers::InsertLine(Sci::Line line) { |
104 | if (markers.Length()) { |
105 | markers.Insert(line, nullptr); |
106 | } |
107 | } |
108 | |
109 | void LineMarkers::InsertLines(Sci::Line line, Sci::Line lines) { |
110 | if (markers.Length()) { |
111 | markers.InsertEmpty(line, lines); |
112 | } |
113 | } |
114 | |
115 | void 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 | |
125 | Sci::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 | |
134 | int 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 | |
142 | int 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 | |
150 | void 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 | |
159 | int 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 | |
166 | Sci::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 | |
178 | int 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 | |
196 | bool 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 | |
212 | void 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 | |
222 | LineLevels::~LineLevels() { |
223 | } |
224 | |
225 | void LineLevels::Init() { |
226 | levels.DeleteAll(); |
227 | } |
228 | |
229 | void 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 | |
236 | void 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 | |
243 | void 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 = 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 | |
256 | void LineLevels::ExpandLevels(Sci::Line sizeNew) { |
257 | levels.InsertValue(levels.Length(), sizeNew - levels.Length(), static_cast<int>(Scintilla::FoldLevel::Base)); |
258 | } |
259 | |
260 | void LineLevels::ClearLevels() { |
261 | levels.DeleteAll(); |
262 | } |
263 | |
264 | int 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 | |
278 | int 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 | |
286 | LineState::~LineState() { |
287 | } |
288 | |
289 | void LineState::Init() { |
290 | lineStates.DeleteAll(); |
291 | } |
292 | |
293 | void 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 | |
301 | void 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 | |
309 | void LineState::RemoveLine(Sci::Line line) { |
310 | if (lineStates.Length() > line) { |
311 | lineStates.Delete(line); |
312 | } |
313 | } |
314 | |
315 | int 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 | |
322 | int LineState::GetLineState(Sci::Line line) { |
323 | if (line < 0) |
324 | return 0; |
325 | lineStates.EnsureLength(line + 1); |
326 | return lineStates[line]; |
327 | } |
328 | |
329 | Sci::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 | |
336 | struct { |
337 | short ; // Style IndividualStyles implies array of styles |
338 | short ; |
339 | int ; |
340 | }; |
341 | |
342 | namespace { |
343 | |
344 | constexpr int IndividualStyles = 0x100; |
345 | |
346 | size_t NumberLines(std::string_view sv) { |
347 | return std::count(sv.begin(), sv.end(), '\n') + 1; |
348 | } |
349 | |
350 | std::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 | |
357 | LineAnnotation::~LineAnnotation() { |
358 | } |
359 | |
360 | void LineAnnotation::Init() { |
361 | ClearAll(); |
362 | } |
363 | |
364 | void LineAnnotation::InsertLine(Sci::Line line) { |
365 | if (annotations.Length()) { |
366 | annotations.EnsureLength(line); |
367 | annotations.Insert(line, std::unique_ptr<char []>()); |
368 | } |
369 | } |
370 | |
371 | void LineAnnotation::InsertLines(Sci::Line line, Sci::Line lines) { |
372 | if (annotations.Length()) { |
373 | annotations.EnsureLength(line); |
374 | annotations.InsertEmpty(line, lines); |
375 | } |
376 | } |
377 | |
378 | void 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 | |
385 | bool 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 | |
392 | int 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 | |
399 | const 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 | |
406 | const 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 | |
413 | void 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 | |
432 | void LineAnnotation::ClearAll() { |
433 | annotations.DeleteAll(); |
434 | } |
435 | |
436 | void 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 | |
444 | void 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 | |
466 | int 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 | |
473 | int 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 | |
480 | LineTabstops::~LineTabstops() { |
481 | } |
482 | |
483 | void LineTabstops::Init() { |
484 | tabstops.DeleteAll(); |
485 | } |
486 | |
487 | void LineTabstops::InsertLine(Sci::Line line) { |
488 | if (tabstops.Length()) { |
489 | tabstops.EnsureLength(line); |
490 | tabstops.Insert(line, nullptr); |
491 | } |
492 | } |
493 | |
494 | void LineTabstops::InsertLines(Sci::Line line, Sci::Line lines) { |
495 | if (tabstops.Length()) { |
496 | tabstops.EnsureLength(line); |
497 | tabstops.InsertEmpty(line, lines); |
498 | } |
499 | } |
500 | |
501 | void LineTabstops::RemoveLine(Sci::Line line) { |
502 | if (tabstops.Length() > line) { |
503 | tabstops[line].reset(); |
504 | tabstops.Delete(line); |
505 | } |
506 | } |
507 | |
508 | bool 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 | |
519 | bool 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 | |
538 | int 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 | |