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