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 <vector>
14 #include <forward_list>
15 #include <algorithm>
16 #include <memory>
17 
18 #include "Platform.h"
19 
20 #include "Scintilla.h"
21 #include "Position.h"
22 #include "SplitVector.h"
23 #include "Partitioning.h"
24 #include "CellBuffer.h"
25 #include "PerLine.h"
26 
27 using namespace Scintilla;
28 
MarkerHandleSet()29 MarkerHandleSet::MarkerHandleSet() {
30 }
31 
~MarkerHandleSet()32 MarkerHandleSet::~MarkerHandleSet() {
33 	mhList.clear();
34 }
35 
Empty() const36 bool MarkerHandleSet::Empty() const noexcept {
37 	return mhList.empty();
38 }
39 
MarkValue() const40 int MarkerHandleSet::MarkValue() const noexcept {
41 	unsigned int m = 0;
42 	for (const MarkerHandleNumber &mhn : mhList) {
43 		m |= (1 << mhn.number);
44 	}
45 	return m;
46 }
47 
Contains(int handle) const48 bool MarkerHandleSet::Contains(int handle) const noexcept {
49 	for (const MarkerHandleNumber &mhn : mhList) {
50 		if (mhn.handle == handle) {
51 			return true;
52 		}
53 	}
54 	return false;
55 }
56 
InsertHandle(int handle,int markerNum)57 bool MarkerHandleSet::InsertHandle(int handle, int markerNum) {
58 	mhList.push_front(MarkerHandleNumber(handle, markerNum));
59 	return true;
60 }
61 
RemoveHandle(int handle)62 void MarkerHandleSet::RemoveHandle(int handle) {
63 	mhList.remove_if([handle](const MarkerHandleNumber &mhn) { return mhn.handle == handle; });
64 }
65 
RemoveNumber(int markerNum,bool all)66 bool MarkerHandleSet::RemoveNumber(int markerNum, bool all) {
67 	bool performedDeletion = false;
68 	mhList.remove_if([&](const MarkerHandleNumber &mhn) {
69 		if ((all || !performedDeletion) && (mhn.number == markerNum)) {
70 			performedDeletion = true;
71 			return true;
72 		}
73 		return false;
74 	});
75 	return performedDeletion;
76 }
77 
CombineWith(MarkerHandleSet * other)78 void MarkerHandleSet::CombineWith(MarkerHandleSet *other) {
79 	mhList.splice_after(mhList.before_begin(), other->mhList);
80 }
81 
~LineMarkers()82 LineMarkers::~LineMarkers() {
83 	markers.DeleteAll();
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].reset(new 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].reset(new 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 	ClearAll();
314 }
315 
Init()316 void LineAnnotation::Init() {
317 	ClearAll();
318 }
319 
InsertLine(Sci::Line line)320 void LineAnnotation::InsertLine(Sci::Line line) {
321 	if (annotations.Length()) {
322 		annotations.EnsureLength(line);
323 		annotations.Insert(line, std::unique_ptr<char []>());
324 	}
325 }
326 
RemoveLine(Sci::Line line)327 void LineAnnotation::RemoveLine(Sci::Line line) {
328 	if (annotations.Length() && (line > 0) && (line <= annotations.Length())) {
329 		annotations[line-1].reset();
330 		annotations.Delete(line-1);
331 	}
332 }
333 
MultipleStyles(Sci::Line line) const334 bool LineAnnotation::MultipleStyles(Sci::Line line) const {
335 	if (annotations.Length() && (line >= 0) && (line < annotations.Length()) && annotations[line])
336 		return reinterpret_cast<AnnotationHeader *>(annotations[line].get())->style == IndividualStyles;
337 	else
338 		return false;
339 }
340 
Style(Sci::Line line) const341 int LineAnnotation::Style(Sci::Line line) const {
342 	if (annotations.Length() && (line >= 0) && (line < annotations.Length()) && annotations[line])
343 		return reinterpret_cast<AnnotationHeader *>(annotations[line].get())->style;
344 	else
345 		return 0;
346 }
347 
Text(Sci::Line line) const348 const char *LineAnnotation::Text(Sci::Line line) const {
349 	if (annotations.Length() && (line >= 0) && (line < annotations.Length()) && annotations[line])
350 		return annotations[line].get()+sizeof(AnnotationHeader);
351 	else
352 		return nullptr;
353 }
354 
Styles(Sci::Line line) const355 const unsigned char *LineAnnotation::Styles(Sci::Line line) const {
356 	if (annotations.Length() && (line >= 0) && (line < annotations.Length()) && annotations[line] && MultipleStyles(line))
357 		return reinterpret_cast<unsigned char *>(annotations[line].get() + sizeof(AnnotationHeader) + Length(line));
358 	else
359 		return nullptr;
360 }
361 
AllocateAnnotation(int length,int style)362 static char *AllocateAnnotation(int length, int style) {
363 	const size_t len = sizeof(AnnotationHeader) + length + ((style == IndividualStyles) ? length : 0);
364 	char *ret = new char[len]();
365 	return ret;
366 }
367 
SetText(Sci::Line line,const char * text)368 void LineAnnotation::SetText(Sci::Line line, const char *text) {
369 	if (text && (line >= 0)) {
370 		annotations.EnsureLength(line+1);
371 		const int style = Style(line);
372 		annotations[line].reset(AllocateAnnotation(static_cast<int>(strlen(text)), style));
373 		char *pa = annotations[line].get();
374 		assert(pa);
375 		AnnotationHeader *pah = reinterpret_cast<AnnotationHeader *>(pa);
376 		pah->style = static_cast<short>(style);
377 		pah->length = static_cast<int>(strlen(text));
378 		pah->lines = static_cast<short>(NumberLines(text));
379 		memcpy(pa+sizeof(AnnotationHeader), text, pah->length);
380 	} else {
381 		if (annotations.Length() && (line >= 0) && (line < annotations.Length()) && annotations[line]) {
382 			annotations[line].reset();
383 		}
384 	}
385 }
386 
ClearAll()387 void LineAnnotation::ClearAll() {
388 	annotations.DeleteAll();
389 }
390 
SetStyle(Sci::Line line,int style)391 void LineAnnotation::SetStyle(Sci::Line line, int style) {
392 	annotations.EnsureLength(line+1);
393 	if (!annotations[line]) {
394 		annotations[line].reset(AllocateAnnotation(0, style));
395 	}
396 	reinterpret_cast<AnnotationHeader *>(annotations[line].get())->style = static_cast<short>(style);
397 }
398 
SetStyles(Sci::Line line,const unsigned char * styles)399 void LineAnnotation::SetStyles(Sci::Line line, const unsigned char *styles) {
400 	if (line >= 0) {
401 		annotations.EnsureLength(line+1);
402 		if (!annotations[line]) {
403 			annotations[line].reset(AllocateAnnotation(0, IndividualStyles));
404 		} else {
405 			const AnnotationHeader *pahSource = reinterpret_cast<AnnotationHeader *>(annotations[line].get());
406 			if (pahSource->style != IndividualStyles) {
407 				char *allocation = AllocateAnnotation(pahSource->length, IndividualStyles);
408 				AnnotationHeader *pahAlloc = reinterpret_cast<AnnotationHeader *>(allocation);
409 				pahAlloc->length = pahSource->length;
410 				pahAlloc->lines = pahSource->lines;
411 				memcpy(allocation + sizeof(AnnotationHeader), annotations[line].get() + sizeof(AnnotationHeader), pahSource->length);
412 				annotations[line].reset(allocation);
413 			}
414 		}
415 		AnnotationHeader *pah = reinterpret_cast<AnnotationHeader *>(annotations[line].get());
416 		pah->style = IndividualStyles;
417 		memcpy(annotations[line].get() + sizeof(AnnotationHeader) + pah->length, styles, pah->length);
418 	}
419 }
420 
Length(Sci::Line line) const421 int LineAnnotation::Length(Sci::Line line) const {
422 	if (annotations.Length() && (line >= 0) && (line < annotations.Length()) && annotations[line])
423 		return reinterpret_cast<AnnotationHeader *>(annotations[line].get())->length;
424 	else
425 		return 0;
426 }
427 
Lines(Sci::Line line) const428 int LineAnnotation::Lines(Sci::Line line) const {
429 	if (annotations.Length() && (line >= 0) && (line < annotations.Length()) && annotations[line])
430 		return reinterpret_cast<AnnotationHeader *>(annotations[line].get())->lines;
431 	else
432 		return 0;
433 }
434 
~LineTabstops()435 LineTabstops::~LineTabstops() {
436 	tabstops.DeleteAll();
437 }
438 
Init()439 void LineTabstops::Init() {
440 	tabstops.DeleteAll();
441 }
442 
InsertLine(Sci::Line line)443 void LineTabstops::InsertLine(Sci::Line line) {
444 	if (tabstops.Length()) {
445 		tabstops.EnsureLength(line);
446 		tabstops.Insert(line, nullptr);
447 	}
448 }
449 
RemoveLine(Sci::Line line)450 void LineTabstops::RemoveLine(Sci::Line line) {
451 	if (tabstops.Length() > line) {
452 		tabstops[line].reset();
453 		tabstops.Delete(line);
454 	}
455 }
456 
ClearTabstops(Sci::Line line)457 bool LineTabstops::ClearTabstops(Sci::Line line) {
458 	if (line < tabstops.Length()) {
459 		TabstopList *tl = tabstops[line].get();
460 		if (tl) {
461 			tl->clear();
462 			return true;
463 		}
464 	}
465 	return false;
466 }
467 
AddTabstop(Sci::Line line,int x)468 bool LineTabstops::AddTabstop(Sci::Line line, int x) {
469 	tabstops.EnsureLength(line + 1);
470 	if (!tabstops[line]) {
471 		tabstops[line].reset(new TabstopList());
472 	}
473 
474 	TabstopList *tl = tabstops[line].get();
475 	if (tl) {
476 		// tabstop positions are kept in order - insert in the right place
477 		std::vector<int>::iterator it = std::lower_bound(tl->begin(), tl->end(), x);
478 		// don't insert duplicates
479 		if (it == tl->end() || *it != x) {
480 			tl->insert(it, x);
481 			return true;
482 		}
483 	}
484 	return false;
485 }
486 
GetNextTabstop(Sci::Line line,int x) const487 int LineTabstops::GetNextTabstop(Sci::Line line, int x) const {
488 	if (line < tabstops.Length()) {
489 		TabstopList *tl = tabstops[line].get();
490 		if (tl) {
491 			for (const int i : *tl) {
492 				if (i > x) {
493 					return i;
494 				}
495 			}
496 		}
497 	}
498 	return 0;
499 }
500