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