Update Scintilla to version 3.5.2
[TortoiseGit.git] / ext / scintilla / src / CellBuffer.cxx
blobf69ab62d4a8fb7f0ac40f72baa52f7df6b26f04d
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.
8 #include <stdlib.h>
9 #include <string.h>
10 #include <stdio.h>
11 #include <stdarg.h>
13 #include <algorithm>
15 #include "Platform.h"
17 #include "Scintilla.h"
18 #include "SplitVector.h"
19 #include "Partitioning.h"
20 #include "CellBuffer.h"
21 #include "UniConversion.h"
23 #ifdef SCI_NAMESPACE
24 using namespace Scintilla;
25 #endif
27 LineVector::LineVector() : starts(256), perLine(0) {
28 Init();
31 LineVector::~LineVector() {
32 starts.DeleteAll();
35 void LineVector::Init() {
36 starts.DeleteAll();
37 if (perLine) {
38 perLine->Init();
42 void LineVector::SetPerLine(PerLine *pl) {
43 perLine = pl;
46 void LineVector::InsertText(int line, int delta) {
47 starts.InsertText(line, delta);
50 void LineVector::InsertLine(int line, int position, bool lineStart) {
51 starts.InsertPartition(line, position);
52 if (perLine) {
53 if ((line > 0) && lineStart)
54 line--;
55 perLine->InsertLine(line);
59 void LineVector::SetLineStart(int line, int position) {
60 starts.SetPartitionStartPosition(line, position);
63 void LineVector::RemoveLine(int line) {
64 starts.RemovePartition(line);
65 if (perLine) {
66 perLine->RemoveLine(line);
70 int LineVector::LineFromPosition(int pos) const {
71 return starts.PartitionFromPosition(pos);
74 Action::Action() {
75 at = startAction;
76 position = 0;
77 data = 0;
78 lenData = 0;
79 mayCoalesce = false;
82 Action::~Action() {
83 Destroy();
86 void Action::Create(actionType at_, int position_, const char *data_, int lenData_, bool mayCoalesce_) {
87 delete []data;
88 data = NULL;
89 position = position_;
90 at = at_;
91 if (lenData_) {
92 data = new char[lenData_];
93 memcpy(data, data_, lenData_);
95 lenData = lenData_;
96 mayCoalesce = mayCoalesce_;
99 void Action::Destroy() {
100 delete []data;
101 data = 0;
104 void Action::Grab(Action *source) {
105 delete []data;
107 position = source->position;
108 at = source->at;
109 data = source->data;
110 lenData = source->lenData;
111 mayCoalesce = source->mayCoalesce;
113 // Ownership of source data transferred to this
114 source->position = 0;
115 source->at = startAction;
116 source->data = 0;
117 source->lenData = 0;
118 source->mayCoalesce = true;
121 // The undo history stores a sequence of user operations that represent the user's view of the
122 // commands executed on the text.
123 // Each user operation contains a sequence of text insertion and text deletion actions.
124 // All the user operations are stored in a list of individual actions with 'start' actions used
125 // as delimiters between user operations.
126 // Initially there is one start action in the history.
127 // As each action is performed, it is recorded in the history. The action may either become
128 // part of the current user operation or may start a new user operation. If it is to be part of the
129 // current operation, then it overwrites the current last action. If it is to be part of a new
130 // operation, it is appended after the current last action.
131 // After writing the new action, a new start action is appended at the end of the history.
132 // The decision of whether to start a new user operation is based upon two factors. If a
133 // compound operation has been explicitly started by calling BeginUndoAction and no matching
134 // EndUndoAction (these calls nest) has been called, then the action is coalesced into the current
135 // operation. If there is no outstanding BeginUndoAction call then a new operation is started
136 // unless it looks as if the new action is caused by the user typing or deleting a stream of text.
137 // Sequences that look like typing or deletion are coalesced into a single user operation.
139 UndoHistory::UndoHistory() {
141 lenActions = 100;
142 actions = new Action[lenActions];
143 maxAction = 0;
144 currentAction = 0;
145 undoSequenceDepth = 0;
146 savePoint = 0;
147 tentativePoint = -1;
149 actions[currentAction].Create(startAction);
152 UndoHistory::~UndoHistory() {
153 delete []actions;
154 actions = 0;
157 void UndoHistory::EnsureUndoRoom() {
158 // Have to test that there is room for 2 more actions in the array
159 // as two actions may be created by the calling function
160 if (currentAction >= (lenActions - 2)) {
161 // Run out of undo nodes so extend the array
162 int lenActionsNew = lenActions * 2;
163 Action *actionsNew = new Action[lenActionsNew];
164 for (int act = 0; act <= currentAction; act++)
165 actionsNew[act].Grab(&actions[act]);
166 delete []actions;
167 lenActions = lenActionsNew;
168 actions = actionsNew;
172 const char *UndoHistory::AppendAction(actionType at, int position, const char *data, int lengthData,
173 bool &startSequence, bool mayCoalesce) {
174 EnsureUndoRoom();
175 //Platform::DebugPrintf("%% %d action %d %d %d\n", at, position, lengthData, currentAction);
176 //Platform::DebugPrintf("^ %d action %d %d\n", actions[currentAction - 1].at,
177 // actions[currentAction - 1].position, actions[currentAction - 1].lenData);
178 if (currentAction < savePoint) {
179 savePoint = -1;
181 int oldCurrentAction = currentAction;
182 if (currentAction >= 1) {
183 if (0 == undoSequenceDepth) {
184 // Top level actions may not always be coalesced
185 int targetAct = -1;
186 const Action *actPrevious = &(actions[currentAction + targetAct]);
187 // Container actions may forward the coalesce state of Scintilla Actions.
188 while ((actPrevious->at == containerAction) && actPrevious->mayCoalesce) {
189 targetAct--;
190 actPrevious = &(actions[currentAction + targetAct]);
192 // See if current action can be coalesced into previous action
193 // Will work if both are inserts or deletes and position is same
194 #if defined(_MSC_VER) && defined(_PREFAST_)
195 // Visual Studio 2013 Code Analysis wrongly believes actions can be NULL at its next reference
196 __analysis_assume(actions);
197 #endif
198 if ((currentAction == savePoint) || (currentAction == tentativePoint)) {
199 currentAction++;
200 } else if (!actions[currentAction].mayCoalesce) {
201 // Not allowed to coalesce if this set
202 currentAction++;
203 } else if (!mayCoalesce || !actPrevious->mayCoalesce) {
204 currentAction++;
205 } else if (at == containerAction || actions[currentAction].at == containerAction) {
206 ; // A coalescible containerAction
207 } else if ((at != actPrevious->at) && (actPrevious->at != startAction)) {
208 currentAction++;
209 } else if ((at == insertAction) &&
210 (position != (actPrevious->position + actPrevious->lenData))) {
211 // Insertions must be immediately after to coalesce
212 currentAction++;
213 } else if (at == removeAction) {
214 if ((lengthData == 1) || (lengthData == 2)) {
215 if ((position + lengthData) == actPrevious->position) {
216 ; // Backspace -> OK
217 } else if (position == actPrevious->position) {
218 ; // Delete -> OK
219 } else {
220 // Removals must be at same position to coalesce
221 currentAction++;
223 } else {
224 // Removals must be of one character to coalesce
225 currentAction++;
227 } else {
228 // Action coalesced.
231 } else {
232 // Actions not at top level are always coalesced unless this is after return to top level
233 if (!actions[currentAction].mayCoalesce)
234 currentAction++;
236 } else {
237 currentAction++;
239 startSequence = oldCurrentAction != currentAction;
240 int actionWithData = currentAction;
241 actions[currentAction].Create(at, position, data, lengthData, mayCoalesce);
242 currentAction++;
243 actions[currentAction].Create(startAction);
244 maxAction = currentAction;
245 return actions[actionWithData].data;
248 void UndoHistory::BeginUndoAction() {
249 EnsureUndoRoom();
250 if (undoSequenceDepth == 0) {
251 if (actions[currentAction].at != startAction) {
252 currentAction++;
253 actions[currentAction].Create(startAction);
254 maxAction = currentAction;
256 actions[currentAction].mayCoalesce = false;
258 undoSequenceDepth++;
261 void UndoHistory::EndUndoAction() {
262 PLATFORM_ASSERT(undoSequenceDepth > 0);
263 EnsureUndoRoom();
264 undoSequenceDepth--;
265 if (0 == undoSequenceDepth) {
266 if (actions[currentAction].at != startAction) {
267 currentAction++;
268 actions[currentAction].Create(startAction);
269 maxAction = currentAction;
271 actions[currentAction].mayCoalesce = false;
275 void UndoHistory::DropUndoSequence() {
276 undoSequenceDepth = 0;
279 void UndoHistory::DeleteUndoHistory() {
280 for (int i = 1; i < maxAction; i++)
281 actions[i].Destroy();
282 maxAction = 0;
283 currentAction = 0;
284 actions[currentAction].Create(startAction);
285 savePoint = 0;
286 tentativePoint = -1;
289 void UndoHistory::SetSavePoint() {
290 savePoint = currentAction;
293 bool UndoHistory::IsSavePoint() const {
294 return savePoint == currentAction;
297 void UndoHistory::TentativeStart() {
298 tentativePoint = currentAction;
301 void UndoHistory::TentativeCommit() {
302 tentativePoint = -1;
303 // Truncate undo history
304 maxAction = currentAction;
307 int UndoHistory::TentativeSteps() {
308 // Drop any trailing startAction
309 if (actions[currentAction].at == startAction && currentAction > 0)
310 currentAction--;
311 if (tentativePoint >= 0)
312 return currentAction - tentativePoint;
313 else
314 return -1;
317 bool UndoHistory::CanUndo() const {
318 return (currentAction > 0) && (maxAction > 0);
321 int UndoHistory::StartUndo() {
322 // Drop any trailing startAction
323 if (actions[currentAction].at == startAction && currentAction > 0)
324 currentAction--;
326 // Count the steps in this action
327 int act = currentAction;
328 while (actions[act].at != startAction && act > 0) {
329 act--;
331 return currentAction - act;
334 const Action &UndoHistory::GetUndoStep() const {
335 return actions[currentAction];
338 void UndoHistory::CompletedUndoStep() {
339 currentAction--;
342 bool UndoHistory::CanRedo() const {
343 return maxAction > currentAction;
346 int UndoHistory::StartRedo() {
347 // Drop any leading startAction
348 if (actions[currentAction].at == startAction && currentAction < maxAction)
349 currentAction++;
351 // Count the steps in this action
352 int act = currentAction;
353 while (actions[act].at != startAction && act < maxAction) {
354 act++;
356 return act - currentAction;
359 const Action &UndoHistory::GetRedoStep() const {
360 return actions[currentAction];
363 void UndoHistory::CompletedRedoStep() {
364 currentAction++;
367 CellBuffer::CellBuffer() {
368 readOnly = false;
369 utf8LineEnds = 0;
370 collectingUndo = true;
373 CellBuffer::~CellBuffer() {
376 char CellBuffer::CharAt(int position) const {
377 return substance.ValueAt(position);
380 void CellBuffer::GetCharRange(char *buffer, int position, int lengthRetrieve) const {
381 if (lengthRetrieve <= 0)
382 return;
383 if (position < 0)
384 return;
385 if ((position + lengthRetrieve) > substance.Length()) {
386 Platform::DebugPrintf("Bad GetCharRange %d for %d of %d\n", position,
387 lengthRetrieve, substance.Length());
388 return;
390 substance.GetRange(buffer, position, lengthRetrieve);
393 char CellBuffer::StyleAt(int position) const {
394 return style.ValueAt(position);
397 void CellBuffer::GetStyleRange(unsigned char *buffer, int position, int lengthRetrieve) const {
398 if (lengthRetrieve < 0)
399 return;
400 if (position < 0)
401 return;
402 if ((position + lengthRetrieve) > style.Length()) {
403 Platform::DebugPrintf("Bad GetStyleRange %d for %d of %d\n", position,
404 lengthRetrieve, style.Length());
405 return;
407 style.GetRange(reinterpret_cast<char *>(buffer), position, lengthRetrieve);
410 const char *CellBuffer::BufferPointer() {
411 return substance.BufferPointer();
414 const char *CellBuffer::RangePointer(int position, int rangeLength) {
415 return substance.RangePointer(position, rangeLength);
418 int CellBuffer::GapPosition() const {
419 return substance.GapPosition();
422 // The char* returned is to an allocation owned by the undo history
423 const char *CellBuffer::InsertString(int position, const char *s, int insertLength, bool &startSequence) {
424 // InsertString and DeleteChars are the bottleneck though which all changes occur
425 const char *data = s;
426 if (!readOnly) {
427 if (collectingUndo) {
428 // Save into the undo/redo stack, but only the characters - not the formatting
429 // This takes up about half load time
430 data = uh.AppendAction(insertAction, position, s, insertLength, startSequence);
433 BasicInsertString(position, s, insertLength);
435 return data;
438 bool CellBuffer::SetStyleAt(int position, char styleValue) {
439 char curVal = style.ValueAt(position);
440 if (curVal != styleValue) {
441 style.SetValueAt(position, styleValue);
442 return true;
443 } else {
444 return false;
448 bool CellBuffer::SetStyleFor(int position, int lengthStyle, char styleValue) {
449 bool changed = false;
450 PLATFORM_ASSERT(lengthStyle == 0 ||
451 (lengthStyle > 0 && lengthStyle + position <= style.Length()));
452 while (lengthStyle--) {
453 char curVal = style.ValueAt(position);
454 if (curVal != styleValue) {
455 style.SetValueAt(position, styleValue);
456 changed = true;
458 position++;
460 return changed;
463 // The char* returned is to an allocation owned by the undo history
464 const char *CellBuffer::DeleteChars(int position, int deleteLength, bool &startSequence) {
465 // InsertString and DeleteChars are the bottleneck though which all changes occur
466 PLATFORM_ASSERT(deleteLength > 0);
467 const char *data = 0;
468 if (!readOnly) {
469 if (collectingUndo) {
470 // Save into the undo/redo stack, but only the characters - not the formatting
471 // The gap would be moved to position anyway for the deletion so this doesn't cost extra
472 data = substance.RangePointer(position, deleteLength);
473 data = uh.AppendAction(removeAction, position, data, deleteLength, startSequence);
476 BasicDeleteChars(position, deleteLength);
478 return data;
481 int CellBuffer::Length() const {
482 return substance.Length();
485 void CellBuffer::Allocate(int newSize) {
486 substance.ReAllocate(newSize);
487 style.ReAllocate(newSize);
490 void CellBuffer::SetLineEndTypes(int utf8LineEnds_) {
491 if (utf8LineEnds != utf8LineEnds_) {
492 utf8LineEnds = utf8LineEnds_;
493 ResetLineEnds();
497 void CellBuffer::SetPerLine(PerLine *pl) {
498 lv.SetPerLine(pl);
501 int CellBuffer::Lines() const {
502 return lv.Lines();
505 int CellBuffer::LineStart(int line) const {
506 if (line < 0)
507 return 0;
508 else if (line >= Lines())
509 return Length();
510 else
511 return lv.LineStart(line);
514 bool CellBuffer::IsReadOnly() const {
515 return readOnly;
518 void CellBuffer::SetReadOnly(bool set) {
519 readOnly = set;
522 void CellBuffer::SetSavePoint() {
523 uh.SetSavePoint();
526 bool CellBuffer::IsSavePoint() const {
527 return uh.IsSavePoint();
530 void CellBuffer::TentativeStart() {
531 uh.TentativeStart();
534 void CellBuffer::TentativeCommit() {
535 uh.TentativeCommit();
538 int CellBuffer::TentativeSteps() {
539 return uh.TentativeSteps();
542 bool CellBuffer::TentativeActive() const {
543 return uh.TentativeActive();
546 // Without undo
548 void CellBuffer::InsertLine(int line, int position, bool lineStart) {
549 lv.InsertLine(line, position, lineStart);
552 void CellBuffer::RemoveLine(int line) {
553 lv.RemoveLine(line);
556 bool CellBuffer::UTF8LineEndOverlaps(int position) const {
557 unsigned char bytes[] = {
558 static_cast<unsigned char>(substance.ValueAt(position-2)),
559 static_cast<unsigned char>(substance.ValueAt(position-1)),
560 static_cast<unsigned char>(substance.ValueAt(position)),
561 static_cast<unsigned char>(substance.ValueAt(position+1)),
563 return UTF8IsSeparator(bytes) || UTF8IsSeparator(bytes+1) || UTF8IsNEL(bytes+1);
566 void CellBuffer::ResetLineEnds() {
567 // Reinitialize line data -- too much work to preserve
568 lv.Init();
570 int position = 0;
571 int length = Length();
572 int lineInsert = 1;
573 bool atLineStart = true;
574 lv.InsertText(lineInsert-1, length);
575 unsigned char chBeforePrev = 0;
576 unsigned char chPrev = 0;
577 for (int i = 0; i < length; i++) {
578 unsigned char ch = substance.ValueAt(position + i);
579 if (ch == '\r') {
580 InsertLine(lineInsert, (position + i) + 1, atLineStart);
581 lineInsert++;
582 } else if (ch == '\n') {
583 if (chPrev == '\r') {
584 // Patch up what was end of line
585 lv.SetLineStart(lineInsert - 1, (position + i) + 1);
586 } else {
587 InsertLine(lineInsert, (position + i) + 1, atLineStart);
588 lineInsert++;
590 } else if (utf8LineEnds) {
591 unsigned char back3[3] = {chBeforePrev, chPrev, ch};
592 if (UTF8IsSeparator(back3) || UTF8IsNEL(back3+1)) {
593 InsertLine(lineInsert, (position + i) + 1, atLineStart);
594 lineInsert++;
597 chBeforePrev = chPrev;
598 chPrev = ch;
602 void CellBuffer::BasicInsertString(int position, const char *s, int insertLength) {
603 if (insertLength == 0)
604 return;
605 PLATFORM_ASSERT(insertLength > 0);
607 unsigned char chAfter = substance.ValueAt(position);
608 bool breakingUTF8LineEnd = false;
609 if (utf8LineEnds && UTF8IsTrailByte(chAfter)) {
610 breakingUTF8LineEnd = UTF8LineEndOverlaps(position);
613 substance.InsertFromArray(position, s, 0, insertLength);
614 style.InsertValue(position, insertLength, 0);
616 int lineInsert = lv.LineFromPosition(position) + 1;
617 bool atLineStart = lv.LineStart(lineInsert-1) == position;
618 // Point all the lines after the insertion point further along in the buffer
619 lv.InsertText(lineInsert-1, insertLength);
620 unsigned char chBeforePrev = substance.ValueAt(position - 2);
621 unsigned char chPrev = substance.ValueAt(position - 1);
622 if (chPrev == '\r' && chAfter == '\n') {
623 // Splitting up a crlf pair at position
624 InsertLine(lineInsert, position, false);
625 lineInsert++;
627 if (breakingUTF8LineEnd) {
628 RemoveLine(lineInsert);
630 unsigned char ch = ' ';
631 for (int i = 0; i < insertLength; i++) {
632 ch = s[i];
633 if (ch == '\r') {
634 InsertLine(lineInsert, (position + i) + 1, atLineStart);
635 lineInsert++;
636 } else if (ch == '\n') {
637 if (chPrev == '\r') {
638 // Patch up what was end of line
639 lv.SetLineStart(lineInsert - 1, (position + i) + 1);
640 } else {
641 InsertLine(lineInsert, (position + i) + 1, atLineStart);
642 lineInsert++;
644 } else if (utf8LineEnds) {
645 unsigned char back3[3] = {chBeforePrev, chPrev, ch};
646 if (UTF8IsSeparator(back3) || UTF8IsNEL(back3+1)) {
647 InsertLine(lineInsert, (position + i) + 1, atLineStart);
648 lineInsert++;
651 chBeforePrev = chPrev;
652 chPrev = ch;
654 // Joining two lines where last insertion is cr and following substance starts with lf
655 if (chAfter == '\n') {
656 if (ch == '\r') {
657 // End of line already in buffer so drop the newly created one
658 RemoveLine(lineInsert - 1);
660 } else if (utf8LineEnds && !UTF8IsAscii(chAfter)) {
661 // May have end of UTF-8 line end in buffer and start in insertion
662 for (int j = 0; j < UTF8SeparatorLength-1; j++) {
663 unsigned char chAt = substance.ValueAt(position + insertLength + j);
664 unsigned char back3[3] = {chBeforePrev, chPrev, chAt};
665 if (UTF8IsSeparator(back3)) {
666 InsertLine(lineInsert, (position + insertLength + j) + 1, atLineStart);
667 lineInsert++;
669 if ((j == 0) && UTF8IsNEL(back3+1)) {
670 InsertLine(lineInsert, (position + insertLength + j) + 1, atLineStart);
671 lineInsert++;
673 chBeforePrev = chPrev;
674 chPrev = chAt;
679 void CellBuffer::BasicDeleteChars(int position, int deleteLength) {
680 if (deleteLength == 0)
681 return;
683 if ((position == 0) && (deleteLength == substance.Length())) {
684 // If whole buffer is being deleted, faster to reinitialise lines data
685 // than to delete each line.
686 lv.Init();
687 } else {
688 // Have to fix up line positions before doing deletion as looking at text in buffer
689 // to work out which lines have been removed
691 int lineRemove = lv.LineFromPosition(position) + 1;
692 lv.InsertText(lineRemove-1, - (deleteLength));
693 unsigned char chPrev = substance.ValueAt(position - 1);
694 unsigned char chBefore = chPrev;
695 unsigned char chNext = substance.ValueAt(position);
696 bool ignoreNL = false;
697 if (chPrev == '\r' && chNext == '\n') {
698 // Move back one
699 lv.SetLineStart(lineRemove, position);
700 lineRemove++;
701 ignoreNL = true; // First \n is not real deletion
703 if (utf8LineEnds && UTF8IsTrailByte(chNext)) {
704 if (UTF8LineEndOverlaps(position)) {
705 RemoveLine(lineRemove);
709 unsigned char ch = chNext;
710 for (int i = 0; i < deleteLength; i++) {
711 chNext = substance.ValueAt(position + i + 1);
712 if (ch == '\r') {
713 if (chNext != '\n') {
714 RemoveLine(lineRemove);
716 } else if (ch == '\n') {
717 if (ignoreNL) {
718 ignoreNL = false; // Further \n are real deletions
719 } else {
720 RemoveLine(lineRemove);
722 } else if (utf8LineEnds) {
723 if (!UTF8IsAscii(ch)) {
724 unsigned char next3[3] = {ch, chNext,
725 static_cast<unsigned char>(substance.ValueAt(position + i + 2))};
726 if (UTF8IsSeparator(next3) || UTF8IsNEL(next3)) {
727 RemoveLine(lineRemove);
732 ch = chNext;
734 // May have to fix up end if last deletion causes cr to be next to lf
735 // or removes one of a crlf pair
736 char chAfter = substance.ValueAt(position + deleteLength);
737 if (chBefore == '\r' && chAfter == '\n') {
738 // Using lineRemove-1 as cr ended line before start of deletion
739 RemoveLine(lineRemove - 1);
740 lv.SetLineStart(lineRemove - 1, position + 1);
743 substance.DeleteRange(position, deleteLength);
744 style.DeleteRange(position, deleteLength);
747 bool CellBuffer::SetUndoCollection(bool collectUndo) {
748 collectingUndo = collectUndo;
749 uh.DropUndoSequence();
750 return collectingUndo;
753 bool CellBuffer::IsCollectingUndo() const {
754 return collectingUndo;
757 void CellBuffer::BeginUndoAction() {
758 uh.BeginUndoAction();
761 void CellBuffer::EndUndoAction() {
762 uh.EndUndoAction();
765 void CellBuffer::AddUndoAction(int token, bool mayCoalesce) {
766 bool startSequence;
767 uh.AppendAction(containerAction, token, 0, 0, startSequence, mayCoalesce);
770 void CellBuffer::DeleteUndoHistory() {
771 uh.DeleteUndoHistory();
774 bool CellBuffer::CanUndo() const {
775 return uh.CanUndo();
778 int CellBuffer::StartUndo() {
779 return uh.StartUndo();
782 const Action &CellBuffer::GetUndoStep() const {
783 return uh.GetUndoStep();
786 void CellBuffer::PerformUndoStep() {
787 const Action &actionStep = uh.GetUndoStep();
788 if (actionStep.at == insertAction) {
789 BasicDeleteChars(actionStep.position, actionStep.lenData);
790 } else if (actionStep.at == removeAction) {
791 BasicInsertString(actionStep.position, actionStep.data, actionStep.lenData);
793 uh.CompletedUndoStep();
796 bool CellBuffer::CanRedo() const {
797 return uh.CanRedo();
800 int CellBuffer::StartRedo() {
801 return uh.StartRedo();
804 const Action &CellBuffer::GetRedoStep() const {
805 return uh.GetRedoStep();
808 void CellBuffer::PerformRedoStep() {
809 const Action &actionStep = uh.GetRedoStep();
810 if (actionStep.at == insertAction) {
811 BasicInsertString(actionStep.position, actionStep.data, actionStep.lenData);
812 } else if (actionStep.at == removeAction) {
813 BasicDeleteChars(actionStep.position, actionStep.lenData);
815 uh.CompletedRedoStep();