1 // Scintilla source code edit control
2 /** @file CellBuffer.cxx
3  ** Manages a buffer of cells.
4  **/
5 // Copyright 1998-2001 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 <cstdlib>
10 #include <cstring>
11 #include <cstdio>
12 #include <cstdarg>
13 
14 #include <stdexcept>
15 #include <vector>
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 "UniConversion.h"
27 
28 #ifdef SCI_NAMESPACE
29 using namespace Scintilla;
30 #endif
31 
32 /* CHANGEBAR begin */
LineChanges()33 LineChanges::LineChanges() : collecting(0), edition(0) {
34 }
35 
~LineChanges()36 LineChanges::~LineChanges() {
37 }
38 
AdvanceEdition()39 void LineChanges::AdvanceEdition() {
40 	edition = (edition + 1) % 0x40000000;
41 }
42 
GetEdition() const43 int LineChanges::GetEdition() const {
44 	return edition;
45 }
46 
PersistantForm() const47 char *LineChanges::PersistantForm() const {
48 	if (collecting)
49 		return state.PersistantForm();
50 	else
51 		return 0;
52 }
53 
SetChanges(const char * changesState)54 void LineChanges::SetChanges(const char *changesState) {
55 	if (collecting && changesState) {
56 		state.FromPersistant(changesState);
57 		AdvanceEdition();
58 	}
59 }
60 
InsertText(int line,int edition,bool undoing)61 void LineChanges::InsertText(int line, int edition, bool undoing) {
62 	if (collecting && !undoing) {
63 		int position = line;
64 		int fillLength = 1;
65 		if (state.FillRange(position, edition, fillLength)) {
66 			if (fillLength > 0) {
67 				AdvanceEdition();
68 			}
69 		}
70 	}
71 }
72 
InsertLine(int line,int edition,bool undoing)73 void LineChanges::InsertLine(int line, int edition, bool undoing) {
74 	if (collecting && !undoing) {
75 		state.InsertSpace(line, 1);
76 		int linePosition = line;
77 		int fillLength = 1;
78 		if (state.FillRange(linePosition, edition, fillLength))
79 			AdvanceEdition();
80 	}
81 }
82 
RemoveLine(int line,bool undoing)83 void LineChanges::RemoveLine(int line, bool undoing) {
84 	if (collecting && !undoing) {
85 		state.DeleteRange(line, 1);
86 		AdvanceEdition();
87 	}
88 }
89 
EnableChangeCollection(bool collecting_,int lines)90 void LineChanges::EnableChangeCollection(bool collecting_, int lines) {
91 	collecting = collecting_;
92 	if (collecting) {
93 		state.InsertSpace(0, lines);
94 	}
95 }
96 
ClearChanged()97 void LineChanges::ClearChanged() {
98 	if (collecting) {
99 		int position = 0;
100 		int length = state.Length();
101 		if (state.FillRange(position, 0, length))
102 			AdvanceEdition();
103 	}
104 }
105 
GetChanged(int line) const106 int LineChanges::GetChanged(int line) const {
107 	if (collecting) {
108 		return state.ValueAt(line);
109 	}
110 	return 0;
111 }
112 /* CHANGEBAR end */
113 
LineVector()114 LineVector::LineVector() : starts(256), perLine(0) {
115 	Init();
116 }
117 
~LineVector()118 LineVector::~LineVector() {
119 	starts.DeleteAll();
120 }
121 
Init()122 void LineVector::Init() {
123 	starts.DeleteAll();
124 	if (perLine) {
125 		perLine->Init();
126 	}
127 }
128 
SetPerLine(PerLine * pl)129 void LineVector::SetPerLine(PerLine *pl) {
130 	perLine = pl;
131 }
132 
133 /* CHANGEBAR begin */
InsertText(Sci::Line line,Sci::Position delta,int edition,bool undoing,bool lineUnchanged)134 void LineVector::InsertText(Sci::Line line, Sci::Position delta, int edition, bool undoing, bool lineUnchanged) {
135 /* CHANGEBAR end */
136 	starts.InsertText(line, delta);
137 /* CHANGEBAR begin */
138 	// Line stays unchanged if inserted/deleted "something\n" at line start
139 	// or "\nsomething" at line end
140 	if (!lineUnchanged) {
141 		changes.InsertText(line, edition, undoing);
142 	}
143 /* CHANGEBAR end */
144 }
145 
146 /* CHANGEBAR begin */
InsertLine(Sci::Line line,Sci::Position position,bool lineStart,int edition,bool undoing)147 void LineVector::InsertLine(Sci::Line line, Sci::Position position, bool lineStart, int edition, bool undoing) {
148 /* CHANGEBAR end */
149 	starts.InsertPartition(line, position);
150 	if (perLine) {
151 		if ((line > 0) && lineStart)
152 			line--;
153 		perLine->InsertLine(line);
154 	}
155 /* CHANGEBAR begin */
156 	changes.InsertLine(line, edition, undoing);
157 /* CHANGEBAR end */
158 }
159 
SetLineStart(Sci::Line line,Sci::Position position)160 void LineVector::SetLineStart(Sci::Line line, Sci::Position position) {
161 	starts.SetPartitionStartPosition(line, position);
162 }
163 
164 /* CHANGEBAR begin */
RemoveLine(Sci::Line line,bool undoing)165 void LineVector::RemoveLine(Sci::Line line, bool undoing) {
166 /* CHANGEBAR end */
167 	starts.RemovePartition(line);
168 	if (perLine) {
169 		perLine->RemoveLine(line);
170 	}
171 /* CHANGEBAR begin */
172 	changes.RemoveLine(line, undoing);
173 /* CHANGEBAR end */
174 }
175 
LineFromPosition(Sci::Position pos) const176 Sci::Line LineVector::LineFromPosition(Sci::Position pos) const {
177 	return starts.PartitionFromPosition(pos);
178 }
179 /* CHANGEBAR begin */
EnableChangeCollection(bool changesCollecting_)180 void LineVector::EnableChangeCollection(bool changesCollecting_) {
181     DeleteChangeCollection();
182     changes.EnableChangeCollection(changesCollecting_, Lines());
183 }
184 
DeleteChangeCollection()185 void LineVector::DeleteChangeCollection() {
186     changes.ClearChanged();
187 }
188 
GetChanged(Sci::Line line) const189 int LineVector::GetChanged(Sci::Line line) const {
190     return changes.GetChanged(line);
191 }
192 
GetChangesEdition() const193 int LineVector::GetChangesEdition() const {
194     return changes.GetEdition();
195 }
196 
SetSavePoint()197 void LineVector::SetSavePoint() {
198     changes.AdvanceEdition();
199 }
200 
PersistantForm() const201 char *LineVector::PersistantForm() const {
202     return changes.PersistantForm();
203 }
204 
SetChanges(const char * changesState)205 void LineVector::SetChanges(const char *changesState) {
206     changes.SetChanges(changesState);
207 }
208 /* CHANGEBAR end */
209 
Action()210 Action::Action() {
211 	at = startAction;
212 	position = 0;
213 	lenData = 0;
214 	mayCoalesce = false;
215 }
216 
Action(Action && other)217 Action::Action(Action &&other) {
218 	at = other.at;
219 	position = other.position;
220 	data = std::move(other.data);
221 	lenData = other.lenData;
222 	mayCoalesce = other.mayCoalesce;
223 }
224 
~Action()225 Action::~Action() {
226 }
227 
Create(actionType at_,Sci::Position position_,const char * data_,Sci::Position lenData_,bool mayCoalesce_)228 void Action::Create(actionType at_, Sci::Position position_, const char *data_, Sci::Position lenData_, bool mayCoalesce_) {
229 	data = nullptr;
230 	position = position_;
231 	at = at_;
232 	if (lenData_) {
233 		data = std::unique_ptr<char []>(new char[lenData_]);
234 		memcpy(&data[0], data_, lenData_);
235 	}
236 	lenData = lenData_;
237 	mayCoalesce = mayCoalesce_;
238 }
239 
Clear()240 void Action::Clear() {
241 	data = nullptr;
242 	lenData = 0;
243 }
244 
245 // The undo history stores a sequence of user operations that represent the user's view of the
246 // commands executed on the text.
247 // Each user operation contains a sequence of text insertion and text deletion actions.
248 // All the user operations are stored in a list of individual actions with 'start' actions used
249 // as delimiters between user operations.
250 // Initially there is one start action in the history.
251 // As each action is performed, it is recorded in the history. The action may either become
252 // part of the current user operation or may start a new user operation. If it is to be part of the
253 // current operation, then it overwrites the current last action. If it is to be part of a new
254 // operation, it is appended after the current last action.
255 // After writing the new action, a new start action is appended at the end of the history.
256 // The decision of whether to start a new user operation is based upon two factors. If a
257 // compound operation has been explicitly started by calling BeginUndoAction and no matching
258 // EndUndoAction (these calls nest) has been called, then the action is coalesced into the current
259 // operation. If there is no outstanding BeginUndoAction call then a new operation is started
260 // unless it looks as if the new action is caused by the user typing or deleting a stream of text.
261 // Sequences that look like typing or deletion are coalesced into a single user operation.
262 
UndoHistory()263 UndoHistory::UndoHistory() {
264 
265 	actions.resize(3);
266 	maxAction = 0;
267 	currentAction = 0;
268 	undoSequenceDepth = 0;
269 	savePoint = 0;
270 	tentativePoint = -1;
271 /* CHANGEBAR begin */
272 	savePointEffective = 0;
273 
274 	changeActions = 0;
275 /* CHANGEBAR end */
276 
277 	actions[currentAction].Create(startAction);
278 }
279 
~UndoHistory()280 UndoHistory::~UndoHistory() {
281 /* CHANGEBAR begin */
282 	DeleteChangeHistory();
283 /* CHANGEBAR end */
284 }
285 
EnsureUndoRoom()286 void UndoHistory::EnsureUndoRoom() {
287 	// Have to test that there is room for 2 more actions in the array
288 	// as two actions may be created by the calling function
289 	if (static_cast<size_t>(currentAction) >= (actions.size() - 2)) {
290 		// Run out of undo nodes so extend the array
291 		actions.resize(actions.size() * 2);
292 
293 /* CHANGEBAR begin */
294 		if (changeActions) {
295             int lenActionsNew = actions.size();
296 			int **changeActionsNew = new int *[lenActionsNew];
297 			if (!changeActionsNew)
298 				return;
299 			for (int i=0;i<lenActionsNew;i++) {
300 				changeActionsNew[i] = (i < lenActionsNew/2) ? changeActions[i] : 0;
301 			}
302 			delete []changeActions;
303 			changeActions = changeActionsNew;
304 		}
305 /* CHANGEBAR end */
306 	}
307 }
308 
309 /* CHANGEBAR begin */
AppendAction(actionType at,Sci::Position position,const char * data,Sci::Position lengthData,bool & startSequence,char * persistantChanges,bool mayCoalesce)310 const char * UndoHistory::AppendAction(actionType at, Sci::Position position, const char *data, Sci::Position lengthData,
311 	bool &startSequence, char *persistantChanges, bool mayCoalesce) {
312 /* CHANGEBAR end */
313 	EnsureUndoRoom();
314 	//Platform::DebugPrintf("%% %d action %d %d %d\n", at, position, lengthData, currentAction);
315 	//Platform::DebugPrintf("^ %d action %d %d\n", actions[currentAction - 1].at,
316 	//	actions[currentAction - 1].position, actions[currentAction - 1].lenData);
317 	if (currentAction < savePoint) {
318 /* CHANGEBAR begin */
319 		savePointEffective = currentAction;
320 /* CHANGEBAR end */
321 		savePoint = -1;
322 	}
323 	int oldCurrentAction = currentAction;
324 	if (currentAction >= 1) {
325 		if (0 == undoSequenceDepth) {
326 			// Top level actions may not always be coalesced
327 			int targetAct = -1;
328 			const Action *actPrevious = &(actions[currentAction + targetAct]);
329 			// Container actions may forward the coalesce state of Scintilla Actions.
330 			while ((actPrevious->at == containerAction) && actPrevious->mayCoalesce) {
331 				targetAct--;
332 				actPrevious = &(actions[currentAction + targetAct]);
333 			}
334 			// See if current action can be coalesced into previous action
335 			// Will work if both are inserts or deletes and position is same
336 			if ((currentAction == savePoint) || (currentAction == tentativePoint)) {
337 				currentAction++;
338 			} else if (!actions[currentAction].mayCoalesce) {
339 				// Not allowed to coalesce if this set
340 				currentAction++;
341 			} else if (!mayCoalesce || !actPrevious->mayCoalesce) {
342 				currentAction++;
343 			} else if (at == containerAction || actions[currentAction].at == containerAction) {
344 				;	// A coalescible containerAction
345 			} else if ((at != actPrevious->at) && (actPrevious->at != startAction)) {
346 				currentAction++;
347 			} else if ((at == insertAction) &&
348 			           (position != (actPrevious->position + actPrevious->lenData))) {
349 				// Insertions must be immediately after to coalesce
350 				currentAction++;
351 			} else if (at == removeAction) {
352 				if ((lengthData == 1) || (lengthData == 2)) {
353 					if ((position + lengthData) == actPrevious->position) {
354 						; // Backspace -> OK
355 					} else if (position == actPrevious->position) {
356 						; // Delete -> OK
357 					} else {
358 						// Removals must be at same position to coalesce
359 						currentAction++;
360 					}
361 				} else {
362 					// Removals must be of one character to coalesce
363 					currentAction++;
364 				}
365 			} else {
366 				// Action coalesced.
367 			}
368 
369 		} else {
370 			// Actions not at top level are always coalesced unless this is after return to top level
371 			if (!actions[currentAction].mayCoalesce)
372 				currentAction++;
373 		}
374 	} else {
375 		currentAction++;
376 	}
377 	startSequence = oldCurrentAction != currentAction;
378 	const int actionWithData = currentAction;
379 	actions[currentAction].Create(at, position, data, lengthData, mayCoalesce);
380 
381 /* CHANGEBAR begin */
382 	if (changeActions) {
383 		delete []changeActions[currentAction];
384 		changeActions[currentAction] = (int *)persistantChanges;
385 	}
386 /* CHANGEBAR end */
387 
388 	currentAction++;
389 	actions[currentAction].Create(startAction);
390 	maxAction = currentAction;
391 	return actions[actionWithData].data.get();
392 }
393 
BeginUndoAction()394 void UndoHistory::BeginUndoAction() {
395 	EnsureUndoRoom();
396 	if (undoSequenceDepth == 0) {
397 		if (actions[currentAction].at != startAction) {
398 			currentAction++;
399 			actions[currentAction].Create(startAction);
400 			maxAction = currentAction;
401 		}
402 		actions[currentAction].mayCoalesce = false;
403 	}
404 	undoSequenceDepth++;
405 }
406 
EndUndoAction()407 void UndoHistory::EndUndoAction() {
408 	PLATFORM_ASSERT(undoSequenceDepth > 0);
409 	EnsureUndoRoom();
410 	undoSequenceDepth--;
411 	if (0 == undoSequenceDepth) {
412 		if (actions[currentAction].at != startAction) {
413 			currentAction++;
414 			actions[currentAction].Create(startAction);
415 			maxAction = currentAction;
416 		}
417 		actions[currentAction].mayCoalesce = false;
418 	}
419 }
420 
DropUndoSequence()421 void UndoHistory::DropUndoSequence() {
422 	undoSequenceDepth = 0;
423 }
424 
DeleteUndoHistory()425 void UndoHistory::DeleteUndoHistory() {
426 	for (int i = 1; i < maxAction; i++)
427 		actions[i].Clear();
428 	maxAction = 0;
429 	currentAction = 0;
430 	actions[currentAction].Create(startAction);
431 	savePoint = 0;
432 	tentativePoint = -1;
433 /* CHANGEBAR begin */
434 	savePointEffective = 0;
435 /* CHANGEBAR end */
436 }
437 
438 /* CHANGEBAR begin */
DeleteChangeHistory()439 void UndoHistory::DeleteChangeHistory() {
440     if (changeActions) {
441         for (std::vector<Action>::size_type i=0;i<actions.size();i++) {
442             delete []changeActions[i];
443         }
444         delete []changeActions;
445         changeActions = 0;
446     }
447 }
448 
EnableChangeHistory(bool enable)449 void UndoHistory::EnableChangeHistory(bool enable) {
450     if (enable) {
451         if (!changeActions) {
452             changeActions = new int *[actions.size()];
453             for (std::vector<Action>::size_type i=0;i<actions.size();i++) {
454                 changeActions[i] = 0;
455             }
456         }
457     } else {
458         DeleteChangeHistory();
459     }
460 }
461 /* CHANGEBAR end */
462 
SetSavePoint()463 void UndoHistory::SetSavePoint() {
464 	savePoint = currentAction;
465 /* CHANGEBAR begin */
466 	savePointEffective = currentAction;
467 /* CHANGEBAR end */
468 }
469 
IsSavePoint() const470 bool UndoHistory::IsSavePoint() const {
471 	return savePoint == currentAction;
472 }
473 
474 /* CHANGEBAR begin */
BeforeSavePointEffective(int action) const475 bool UndoHistory::BeforeSavePointEffective(int action) const {
476 	return action <= savePointEffective;
477 }
478 /* CHANGEBAR end */
479 
TentativeStart()480 void UndoHistory::TentativeStart() {
481 	tentativePoint = currentAction;
482 }
483 
TentativeCommit()484 void UndoHistory::TentativeCommit() {
485 	tentativePoint = -1;
486 	// Truncate undo history
487 	maxAction = currentAction;
488 }
489 
TentativeSteps()490 int UndoHistory::TentativeSteps() {
491 	// Drop any trailing startAction
492 	if (actions[currentAction].at == startAction && currentAction > 0)
493 		currentAction--;
494 	if (tentativePoint >= 0)
495 		return currentAction - tentativePoint;
496 	else
497 		return -1;
498 }
499 
CanUndo() const500 bool UndoHistory::CanUndo() const {
501 	return (currentAction > 0) && (maxAction > 0);
502 }
503 
StartUndo()504 int UndoHistory::StartUndo() {
505 	// Drop any trailing startAction
506 	if (actions[currentAction].at == startAction && currentAction > 0)
507 		currentAction--;
508 
509 	// Count the steps in this action
510 	int act = currentAction;
511 	while (actions[act].at != startAction && act > 0) {
512 		act--;
513 	}
514 	return currentAction - act;
515 }
516 
GetUndoStep() const517 const Action &UndoHistory::GetUndoStep() const {
518 	return actions[currentAction];
519 }
520 
CompletedUndoStep()521 void UndoHistory::CompletedUndoStep() {
522 	currentAction--;
523 }
524 
525 /* CHANGEBAR begin */
GetChangesStep() const526 char *UndoHistory::GetChangesStep() const {
527 	return changeActions ? (char *)changeActions[currentAction] : 0;
528 }
529 /* CHANGEBAR end */
530 
CanRedo() const531 bool UndoHistory::CanRedo() const {
532 	return maxAction > currentAction;
533 }
534 
StartRedo()535 int UndoHistory::StartRedo() {
536 	// Drop any leading startAction
537 	if (currentAction < maxAction && actions[currentAction].at == startAction)
538 		currentAction++;
539 
540 	// Count the steps in this action
541 	int act = currentAction;
542 	while (act < maxAction && actions[act].at != startAction) {
543 		act++;
544 	}
545 	return act - currentAction;
546 }
547 
GetRedoStep() const548 const Action &UndoHistory::GetRedoStep() const {
549 	return actions[currentAction];
550 }
551 
CompletedRedoStep()552 void UndoHistory::CompletedRedoStep() {
553 	currentAction++;
554 }
555 
556 /* CHANGEBAR begin */
Edition() const557 int UndoHistory::Edition() const {
558 	return currentAction;
559 }
560 /* CHANGEBAR end */
561 
CellBuffer()562 CellBuffer::CellBuffer() {
563 	readOnly = false;
564 	utf8LineEnds = 0;
565 	collectingUndo = true;
566 }
567 
~CellBuffer()568 CellBuffer::~CellBuffer() {
569 }
570 
CharAt(Sci::Position position) const571 char CellBuffer::CharAt(Sci::Position position) const {
572 	return substance.ValueAt(position);
573 }
574 
GetCharRange(char * buffer,Sci::Position position,Sci::Position lengthRetrieve) const575 void CellBuffer::GetCharRange(char *buffer, Sci::Position position, Sci::Position lengthRetrieve) const {
576 	if (lengthRetrieve <= 0)
577 		return;
578 	if (position < 0)
579 		return;
580 	if ((position + lengthRetrieve) > substance.Length()) {
581 		Platform::DebugPrintf("Bad GetCharRange %d for %d of %d\n", position,
582 		                      lengthRetrieve, substance.Length());
583 		return;
584 	}
585 	substance.GetRange(buffer, position, lengthRetrieve);
586 }
587 
StyleAt(Sci::Position position) const588 char CellBuffer::StyleAt(Sci::Position position) const {
589 	return style.ValueAt(position);
590 }
591 
GetStyleRange(unsigned char * buffer,Sci::Position position,Sci::Position lengthRetrieve) const592 void CellBuffer::GetStyleRange(unsigned char *buffer, Sci::Position position, Sci::Position lengthRetrieve) const {
593 	if (lengthRetrieve < 0)
594 		return;
595 	if (position < 0)
596 		return;
597 	if ((position + lengthRetrieve) > style.Length()) {
598 		Platform::DebugPrintf("Bad GetStyleRange %d for %d of %d\n", position,
599 		                      lengthRetrieve, style.Length());
600 		return;
601 	}
602 	style.GetRange(reinterpret_cast<char *>(buffer), position, lengthRetrieve);
603 }
604 
BufferPointer()605 const char *CellBuffer::BufferPointer() {
606 	return substance.BufferPointer();
607 }
608 
RangePointer(Sci::Position position,Sci::Position rangeLength)609 const char *CellBuffer::RangePointer(Sci::Position position, Sci::Position rangeLength) {
610 	return substance.RangePointer(position, rangeLength);
611 }
612 
GapPosition() const613 Sci::Position CellBuffer::GapPosition() const {
614 	return substance.GapPosition();
615 }
616 
617 // The char* returned is to an allocation owned by the undo history
InsertString(Sci::Position position,const char * s,Sci::Position insertLength,bool & startSequence)618 const char *CellBuffer::InsertString(Sci::Position position, const char *s, Sci::Position insertLength, bool &startSequence) {
619 	// InsertString and DeleteChars are the bottleneck though which all changes occur
620 	const char *data = s;
621 	if (!readOnly) {
622 		if (collectingUndo) {
623 			// Save into the undo/redo stack, but only the characters - not the formatting
624 			// This takes up about half load time
625 /* CHANGEBAR begin */
626 			char *persistantForm = lv.PersistantForm();
627 			data = uh.AppendAction(insertAction, position, s, insertLength, startSequence, persistantForm);
628 /* CHANGEBAR end */
629 		}
630 
631 /* CHANGEBAR begin */
632 		BasicInsertString(position, s, insertLength, false);
633 /* CHANGEBAR end */
634 	}
635 	return data;
636 }
637 
SetStyleAt(Sci::Position position,char styleValue)638 bool CellBuffer::SetStyleAt(Sci::Position position, char styleValue) {
639 	const char curVal = style.ValueAt(position);
640 	if (curVal != styleValue) {
641 		style.SetValueAt(position, styleValue);
642 		return true;
643 	} else {
644 		return false;
645 	}
646 }
647 
SetStyleFor(Sci::Position position,Sci::Position lengthStyle,char styleValue)648 bool CellBuffer::SetStyleFor(Sci::Position position, Sci::Position lengthStyle, char styleValue) {
649 	bool changed = false;
650 	PLATFORM_ASSERT(lengthStyle == 0 ||
651 		(lengthStyle > 0 && lengthStyle + position <= style.Length()));
652 	while (lengthStyle--) {
653 		const char curVal = style.ValueAt(position);
654 		if (curVal != styleValue) {
655 			style.SetValueAt(position, styleValue);
656 			changed = true;
657 		}
658 		position++;
659 	}
660 	return changed;
661 }
662 
663 // The char* returned is to an allocation owned by the undo history
DeleteChars(Sci::Position position,Sci::Position deleteLength,bool & startSequence)664 const char *CellBuffer::DeleteChars(Sci::Position position, Sci::Position deleteLength, bool &startSequence) {
665 	// InsertString and DeleteChars are the bottleneck though which all changes occur
666 	PLATFORM_ASSERT(deleteLength > 0);
667 	const char *data = 0;
668 	if (!readOnly) {
669 		if (collectingUndo) {
670 			// Save into the undo/redo stack, but only the characters - not the formatting
671 			// The gap would be moved to position anyway for the deletion so this doesn't cost extra
672 			data = substance.RangePointer(position, deleteLength);
673 /* CHANGEBAR begin */
674 			char *persistantForm = lv.PersistantForm();
675 			data = uh.AppendAction(removeAction, position, data, deleteLength, startSequence, persistantForm);
676 /* CHANGEBAR end */
677 		}
678 
679 /* CHANGEBAR begin */
680 		BasicDeleteChars(position, deleteLength, false);
681 /* CHANGEBAR end */
682 	}
683 	return data;
684 }
685 
Length() const686 Sci::Position CellBuffer::Length() const {
687 	return substance.Length();
688 }
689 
Allocate(Sci::Position newSize)690 void CellBuffer::Allocate(Sci::Position newSize) {
691 	substance.ReAllocate(newSize);
692 	style.ReAllocate(newSize);
693 }
694 
SetLineEndTypes(int utf8LineEnds_)695 void CellBuffer::SetLineEndTypes(int utf8LineEnds_) {
696 	if (utf8LineEnds != utf8LineEnds_) {
697 		utf8LineEnds = utf8LineEnds_;
698 /* CHANGEBAR begin */
699 		ResetLineEnds(false);
700 /* CHANGEBAR end */
701 	}
702 }
703 
ContainsLineEnd(const char * s,Sci::Position length) const704 bool CellBuffer::ContainsLineEnd(const char *s, Sci::Position length) const {
705 	unsigned char chBeforePrev = 0;
706 	unsigned char chPrev = 0;
707 	for (Sci::Position i = 0; i < length; i++) {
708 		const unsigned char ch = s[i];
709 		if ((ch == '\r') || (ch == '\n')) {
710 			return true;
711 		} else if (utf8LineEnds) {
712 			const unsigned char back3[3] = { chBeforePrev, chPrev, ch };
713 			if (UTF8IsSeparator(back3) || UTF8IsNEL(back3 + 1)) {
714 				return true;
715 			}
716 		}
717 		chBeforePrev = chPrev;
718 		chPrev = ch;
719 	}
720 	return false;
721 }
722 
SetPerLine(PerLine * pl)723 void CellBuffer::SetPerLine(PerLine *pl) {
724 	lv.SetPerLine(pl);
725 }
726 
Lines() const727 Sci::Line CellBuffer::Lines() const {
728 	return lv.Lines();
729 }
730 
LineStart(Sci::Line line) const731 Sci::Position CellBuffer::LineStart(Sci::Line line) const {
732 	if (line < 0)
733 		return 0;
734 	else if (line >= Lines())
735 		return Length();
736 	else
737 		return lv.LineStart(line);
738 }
739 
IsReadOnly() const740 bool CellBuffer::IsReadOnly() const {
741 	return readOnly;
742 }
743 
SetReadOnly(bool set)744 void CellBuffer::SetReadOnly(bool set) {
745 	readOnly = set;
746 }
747 
SetSavePoint()748 void CellBuffer::SetSavePoint() {
749 	uh.SetSavePoint();
750 /* CHANGEBAR begin */
751 	lv.SetSavePoint();
752 /* CHANGEBAR end */
753 }
754 
IsSavePoint() const755 bool CellBuffer::IsSavePoint() const {
756 	return uh.IsSavePoint();
757 }
758 
TentativeStart()759 void CellBuffer::TentativeStart() {
760 	uh.TentativeStart();
761 }
762 
TentativeCommit()763 void CellBuffer::TentativeCommit() {
764 	uh.TentativeCommit();
765 }
766 
TentativeSteps()767 int CellBuffer::TentativeSteps() {
768 	return uh.TentativeSteps();
769 }
770 
TentativeActive() const771 bool CellBuffer::TentativeActive() const {
772 	return uh.TentativeActive();
773 }
774 
775 // Without undo
776 
777 /* CHANGEBAR begin */
InsertLine(Sci::Line line,Sci::Position position,bool lineStart,int edition,bool undoing)778 void CellBuffer::InsertLine(Sci::Line line, Sci::Position position, bool lineStart, int edition, bool undoing) {
779 	lv.InsertLine(line, position, lineStart, edition, undoing);
780 /* CHANGEBAR end */
781 }
782 
783 /* CHANGEBAR begin */
RemoveLine(Sci::Line line,bool undoing)784 void CellBuffer::RemoveLine(Sci::Line line, bool undoing) {
785 	lv.RemoveLine(line, undoing);
786 /* CHANGEBAR end */
787 }
788 
UTF8LineEndOverlaps(Sci::Position position) const789 bool CellBuffer::UTF8LineEndOverlaps(Sci::Position position) const {
790 	const unsigned char bytes[] = {
791 		static_cast<unsigned char>(substance.ValueAt(position-2)),
792 		static_cast<unsigned char>(substance.ValueAt(position-1)),
793 		static_cast<unsigned char>(substance.ValueAt(position)),
794 		static_cast<unsigned char>(substance.ValueAt(position+1)),
795 	};
796 	return UTF8IsSeparator(bytes) || UTF8IsSeparator(bytes+1) || UTF8IsNEL(bytes+1);
797 }
798 
799 /* CHANGEBAR begin */
ResetLineEnds(bool undoing)800 void CellBuffer::ResetLineEnds(bool undoing) {
801 /* CHANGEBAR end */
802 	// Reinitialize line data -- too much work to preserve
803 	lv.Init();
804 
805 	Sci::Position position = 0;
806 	Sci::Position length = Length();
807 	Sci::Line lineInsert = 1;
808 	bool atLineStart = true;
809 /* CHANGEBAR begin */
810 	lv.InsertText(lineInsert-1, length, uh.Edition(), undoing, false);
811 /* CHANGEBAR end */
812 	unsigned char chBeforePrev = 0;
813 	unsigned char chPrev = 0;
814 	for (Sci::Position i = 0; i < length; i++) {
815 		const unsigned char ch = substance.ValueAt(position + i);
816 		if (ch == '\r') {
817 /* CHANGEBAR begin */
818 			InsertLine(lineInsert, (position + i) + 1, atLineStart, uh.Edition(), undoing);
819 /* CHANGEBAR end */
820 			lineInsert++;
821 		} else if (ch == '\n') {
822 			if (chPrev == '\r') {
823 				// Patch up what was end of line
824 				lv.SetLineStart(lineInsert - 1, (position + i) + 1);
825 			} else {
826 /* CHANGEBAR begin */
827 				InsertLine(lineInsert, (position + i) + 1, atLineStart, uh.Edition(), undoing);
828 /* CHANGEBAR end */
829 				lineInsert++;
830 			}
831 		} else if (utf8LineEnds) {
832 			const unsigned char back3[3] = {chBeforePrev, chPrev, ch};
833 			if (UTF8IsSeparator(back3) || UTF8IsNEL(back3+1)) {
834 /* CHANGEBAR begin */
835 				InsertLine(lineInsert, (position + i) + 1, atLineStart, uh.Edition(), undoing);
836 /* CHANGEBAR end */
837 				lineInsert++;
838 			}
839 		}
840 		chBeforePrev = chPrev;
841 		chPrev = ch;
842 	}
843 }
844 
845 /* CHANGEBAR begin */
BasicInsertString(Sci::Position position,const char * s,Sci::Position insertLength,bool undoing)846 void CellBuffer::BasicInsertString(Sci::Position position, const char *s, Sci::Position insertLength, bool undoing) {
847 	bool atFileEnd = position == substance.Length();
848 /* CHANGEBAR end */
849 	if (insertLength == 0)
850 		return;
851 	PLATFORM_ASSERT(insertLength > 0);
852 
853 	const unsigned char chAfter = substance.ValueAt(position);
854 	bool breakingUTF8LineEnd = false;
855 	if (utf8LineEnds && UTF8IsTrailByte(chAfter)) {
856 		breakingUTF8LineEnd = UTF8LineEndOverlaps(position);
857 	}
858 
859 	substance.InsertFromArray(position, s, 0, insertLength);
860 	style.InsertValue(position, insertLength, 0);
861 
862 	Sci::Line lineInsert = lv.LineFromPosition(position) + 1;
863 	bool atLineStart = lv.LineStart(lineInsert-1) == position;
864 	// Point all the lines after the insertion point further along in the buffer
865 /* CHANGEBAR begin */
866 	bool atLineEnd = (lv.LineStart(lineInsert) == position+1) || atFileEnd;
867 	bool lineUnchanged = (atLineStart && (s[insertLength-1] == '\n')) ||
868 		(atLineEnd && (s[0] == '\r' || s[0] == '\n'));
869 	lv.InsertText(lineInsert-1, insertLength, uh.Edition(), undoing, lineUnchanged);
870 /* CHANGEBAR end */
871 	unsigned char chBeforePrev = substance.ValueAt(position - 2);
872 	unsigned char chPrev = substance.ValueAt(position - 1);
873 	if (chPrev == '\r' && chAfter == '\n') {
874 		// Splitting up a crlf pair at position
875 /* CHANGEBAR begin */
876 		InsertLine(lineInsert, position, false, uh.Edition(), undoing);
877 /* CHANGEBAR end */
878 		lineInsert++;
879 	}
880 	if (breakingUTF8LineEnd) {
881 /* CHANGEBAR begin */
882 		RemoveLine(lineInsert, undoing);
883 /* CHANGEBAR end */
884 	}
885 	unsigned char ch = ' ';
886 	for (Sci::Position i = 0; i < insertLength; i++) {
887 		ch = s[i];
888 		if (ch == '\r') {
889 /* CHANGEBAR begin */
890 			InsertLine(lineInsert, (position + i) + 1, atLineStart, uh.Edition(), undoing);
891 /* CHANGEBAR end */
892 			lineInsert++;
893 		} else if (ch == '\n') {
894 			if (chPrev == '\r') {
895 				// Patch up what was end of line
896 				lv.SetLineStart(lineInsert - 1, (position + i) + 1);
897 			} else {
898 /* CHANGEBAR begin */
899 				InsertLine(lineInsert, (position + i) + 1, atLineStart, uh.Edition(), undoing);
900 /* CHANGEBAR end */
901 				lineInsert++;
902 			}
903 		} else if (utf8LineEnds) {
904 			const unsigned char back3[3] = {chBeforePrev, chPrev, ch};
905 			if (UTF8IsSeparator(back3) || UTF8IsNEL(back3+1)) {
906 /* CHANGEBAR begin */
907 				InsertLine(lineInsert, (position + i) + 1, atLineStart, uh.Edition(), undoing);
908 /* CHANGEBAR end */
909 				lineInsert++;
910 			}
911 		}
912 		chBeforePrev = chPrev;
913 		chPrev = ch;
914 	}
915 	// Joining two lines where last insertion is cr and following substance starts with lf
916 	if (chAfter == '\n') {
917 		if (ch == '\r') {
918 			// End of line already in buffer so drop the newly created one
919 /* CHANGEBAR begin */
920 			RemoveLine(lineInsert - 1, undoing);
921 /* CHANGEBAR end */
922 		}
923 	} else if (utf8LineEnds && !UTF8IsAscii(chAfter)) {
924 		// May have end of UTF-8 line end in buffer and start in insertion
925 		for (int j = 0; j < UTF8SeparatorLength-1; j++) {
926 			const unsigned char chAt = substance.ValueAt(position + insertLength + j);
927 			const unsigned char back3[3] = {chBeforePrev, chPrev, chAt};
928 			if (UTF8IsSeparator(back3)) {
929 /* CHANGEBAR begin */
930 				InsertLine(lineInsert, (position + insertLength + j) + 1, atLineStart, uh.Edition(), undoing);
931 /* CHANGEBAR end */
932 				lineInsert++;
933 			}
934 			if ((j == 0) && UTF8IsNEL(back3+1)) {
935 /* CHANGEBAR begin */
936 				InsertLine(lineInsert, (position + insertLength + j) + 1, atLineStart, uh.Edition(), undoing);
937 /* CHANGEBAR end */
938 				lineInsert++;
939 			}
940 			chBeforePrev = chPrev;
941 			chPrev = chAt;
942 		}
943 	}
944 }
945 
946 /* CHANGEBAR begin */
BasicDeleteChars(Sci::Position position,Sci::Position deleteLength,bool undoing)947 void CellBuffer::BasicDeleteChars(Sci::Position position, Sci::Position deleteLength, bool undoing) {
948 /* CHANGEBAR end */
949 	if (deleteLength == 0)
950 		return;
951 
952 	if ((position == 0) && (deleteLength == substance.Length())) {
953 		// If whole buffer is being deleted, faster to reinitialise lines data
954 		// than to delete each line.
955 		lv.Init();
956 /* CHANGEBAR begin */
957 		lv.InsertText(0, 0, uh.Edition(), undoing, false);
958 /* CHANGEBAR end */
959 	} else {
960 		// Have to fix up line positions before doing deletion as looking at text in buffer
961 		// to work out which lines have been removed
962 
963 		Sci::Line lineRemove = lv.LineFromPosition(position) + 1;
964 /* CHANGEBAR begin */
965 		bool atLineEnd = (lv.LineStart(lineRemove) == position+1);
966 		char chAfter = substance.ValueAt(position + deleteLength);
967 		bool lineUnchanged = (atLineEnd && (chAfter == '\r' || chAfter == '\n'));
968 		lv.InsertText(lineRemove-1, - (deleteLength), uh.Edition(), undoing, lineUnchanged);
969 /* CHANGEBAR end */
970 		const unsigned char chPrev = substance.ValueAt(position - 1);
971 		const unsigned char chBefore = chPrev;
972 		unsigned char chNext = substance.ValueAt(position);
973 		bool ignoreNL = false;
974 		if (chPrev == '\r' && chNext == '\n') {
975 			// Move back one
976 			lv.SetLineStart(lineRemove, position);
977 			lineRemove++;
978 			ignoreNL = true; 	// First \n is not real deletion
979 		}
980 		if (utf8LineEnds && UTF8IsTrailByte(chNext)) {
981 			if (UTF8LineEndOverlaps(position)) {
982 /* CHANGEBAR begin */
983 				RemoveLine(lineRemove, undoing);
984 /* CHANGEBAR end */
985 			}
986 		}
987 
988 		unsigned char ch = chNext;
989 		for (Sci::Position i = 0; i < deleteLength; i++) {
990 			chNext = substance.ValueAt(position + i + 1);
991 			if (ch == '\r') {
992 				if (chNext != '\n') {
993 /* CHANGEBAR begin */
994 					RemoveLine(lineRemove, undoing);
995 /* CHANGEBAR end */
996 				}
997 			} else if (ch == '\n') {
998 				if (ignoreNL) {
999 					ignoreNL = false; 	// Further \n are real deletions
1000 				} else {
1001 /* CHANGEBAR begin */
1002 					RemoveLine(lineRemove, undoing);
1003 /* CHANGEBAR end */
1004 				}
1005 			} else if (utf8LineEnds) {
1006 				if (!UTF8IsAscii(ch)) {
1007 					const unsigned char next3[3] = {ch, chNext,
1008 						static_cast<unsigned char>(substance.ValueAt(position + i + 2))};
1009 					if (UTF8IsSeparator(next3) || UTF8IsNEL(next3)) {
1010 /* CHANGEBAR begin */
1011 						RemoveLine(lineRemove, undoing);
1012 /* CHANGEBAR end */
1013 					}
1014 				}
1015 			}
1016 
1017 			ch = chNext;
1018 		}
1019 		// May have to fix up end if last deletion causes cr to be next to lf
1020 		// or removes one of a crlf pair
1021 /* CHANGEBAR begin */
1022 		// const char chAfter = substance.ValueAt(position + deleteLength);
1023 /* CHANGEBAR end */
1024 		if (chBefore == '\r' && chAfter == '\n') {
1025 			// Using lineRemove-1 as cr ended line before start of deletion
1026 /* CHANGEBAR begin */
1027 			RemoveLine(lineRemove - 1, undoing);
1028 /* CHANGEBAR end */
1029 			lv.SetLineStart(lineRemove - 1, position + 1);
1030 		}
1031 	}
1032 	substance.DeleteRange(position, deleteLength);
1033 	style.DeleteRange(position, deleteLength);
1034 }
1035 
SetUndoCollection(bool collectUndo)1036 bool CellBuffer::SetUndoCollection(bool collectUndo) {
1037 	collectingUndo = collectUndo;
1038 	uh.DropUndoSequence();
1039 	return collectingUndo;
1040 }
1041 
IsCollectingUndo() const1042 bool CellBuffer::IsCollectingUndo() const {
1043 	return collectingUndo;
1044 }
1045 
BeginUndoAction()1046 void CellBuffer::BeginUndoAction() {
1047 	uh.BeginUndoAction();
1048 }
1049 
EndUndoAction()1050 void CellBuffer::EndUndoAction() {
1051 	uh.EndUndoAction();
1052 }
1053 
AddUndoAction(Sci::Position token,bool mayCoalesce)1054 void CellBuffer::AddUndoAction(Sci::Position token, bool mayCoalesce) {
1055 	bool startSequence;
1056 /* CHANGEBAR begin */
1057 	char *persistantForm = lv.PersistantForm();
1058 	uh.AppendAction(containerAction, token, 0, 0, startSequence, persistantForm, mayCoalesce);
1059 /* CHANGEBAR end */
1060 }
1061 
1062 /* CHANGEBAR begin */
DeleteUndoHistory(bool collectChangeHistory)1063 void CellBuffer::DeleteUndoHistory(bool collectChangeHistory) {
1064 /* CHANGEBAR end */
1065 	uh.DeleteUndoHistory();
1066 /* CHANGEBAR begin */
1067 	uh.EnableChangeHistory(collectChangeHistory);
1068 	lv.EnableChangeCollection(collectChangeHistory);
1069 /* CHANGEBAR end */
1070 }
1071 
1072 /* CHANGEBAR begin */
SetChangeCollection(bool collectChange)1073 bool CellBuffer::SetChangeCollection(bool collectChange) {
1074 	uh.EnableChangeHistory(collectChange);
1075 	lv.EnableChangeCollection(collectChange);
1076 	return collectChange;
1077 }
1078 
DeleteChangeCollection()1079 void CellBuffer::DeleteChangeCollection() {
1080 	uh.DeleteChangeHistory();
1081 	lv.DeleteChangeCollection();
1082 }
1083 /* CHANGEBAR end */
1084 
CanUndo() const1085 bool CellBuffer::CanUndo() const {
1086 	return uh.CanUndo();
1087 }
1088 
StartUndo()1089 int CellBuffer::StartUndo() {
1090 	return uh.StartUndo();
1091 }
1092 
GetUndoStep() const1093 const Action &CellBuffer::GetUndoStep() const {
1094 	return uh.GetUndoStep();
1095 }
1096 
PerformUndoStep()1097 void CellBuffer::PerformUndoStep() {
1098 /* CHANGEBAR begin */
1099 	const char *changesState = uh.GetChangesStep();
1100 	lv.SetChanges(changesState);
1101 /* CHANGEBAR end */
1102 	const Action &actionStep = uh.GetUndoStep();
1103 	if (actionStep.at == insertAction) {
1104 		if (substance.Length() < actionStep.lenData) {
1105 			throw std::runtime_error(
1106 				"CellBuffer::PerformUndoStep: deletion must be less than document length.");
1107 		}
1108 /* CHANGEBAR begin */
1109 		BasicDeleteChars(actionStep.position, actionStep.lenData, true);
1110 /* CHANGEBAR end */
1111 	} else if (actionStep.at == removeAction) {
1112 /* CHANGEBAR begin */
1113 		BasicInsertString(actionStep.position, actionStep.data.get(), actionStep.lenData, true);
1114 /* CHANGEBAR end */
1115 	}
1116 	uh.CompletedUndoStep();
1117 }
1118 
CanRedo() const1119 bool CellBuffer::CanRedo() const {
1120 	return uh.CanRedo();
1121 }
1122 
StartRedo()1123 int CellBuffer::StartRedo() {
1124 	return uh.StartRedo();
1125 }
1126 
GetRedoStep() const1127 const Action &CellBuffer::GetRedoStep() const {
1128 	return uh.GetRedoStep();
1129 }
1130 
PerformRedoStep()1131 void CellBuffer::PerformRedoStep() {
1132 	const Action &actionStep = uh.GetRedoStep();
1133 	if (actionStep.at == insertAction) {
1134 /* CHANGEBAR begin */
1135 		BasicInsertString(actionStep.position, actionStep.data.get(), actionStep.lenData, false);
1136 /* CHANGEBAR end */
1137 	} else if (actionStep.at == removeAction) {
1138 /* CHANGEBAR begin */
1139 		BasicDeleteChars(actionStep.position, actionStep.lenData, false);
1140 /* CHANGEBAR end */
1141 	}
1142 	uh.CompletedRedoStep();
1143 /* CHANGEBAR begin */
1144 	if (IsSavePoint()) {
1145 		lv.SetSavePoint();
1146 	}
1147 /* CHANGEBAR end */
1148 }
1149 
1150 /* CHANGEBAR begin */
GetChanged(int line) const1151 int CellBuffer::GetChanged(int line) const {
1152 	int changed = lv.GetChanged(line);
1153 	if (changed == 0)
1154 		return 0;
1155 	else if (uh.BeforeSavePointEffective(changed))
1156 		return 2;
1157 	else
1158 		return 1;
1159 }
1160 
GetChangesEdition() const1161 int CellBuffer::GetChangesEdition() const {
1162     return lv.GetChangesEdition();
1163 }
1164 /* CHANGEBAR end */
1165