48
void LineVector::InsertText(int line, int delta) {
51
void LineVector::InsertText(Sci::Line line, Sci::Position delta) {
49
52
starts.InsertText(line, delta);
52
void LineVector::InsertLine(int line, int position, bool lineStart) {
55
void LineVector::InsertLine(Sci::Line line, Sci::Position position, bool lineStart) {
53
56
starts.InsertPartition(line, position);
55
58
if ((line > 0) && lineStart)
61
void LineVector::SetLineStart(int line, int position) {
64
void LineVector::SetLineStart(Sci::Line line, Sci::Position position) {
62
65
starts.SetPartitionStartPosition(line, position);
65
void LineVector::RemoveLine(int line) {
68
void LineVector::RemoveLine(Sci::Line line) {
66
69
starts.RemovePartition(line);
68
71
perLine->RemoveLine(line);
72
int LineVector::LineFromPosition(int pos) const {
75
Sci::Line LineVector::LineFromPosition(Sci::Position pos) const {
73
76
return starts.PartitionFromPosition(pos);
81
83
mayCoalesce = false;
86
Action::Action(Action &&other) {
88
position = other.position;
89
data = std::move(other.data);
90
lenData = other.lenData;
91
mayCoalesce = other.mayCoalesce;
84
94
Action::~Action() {
88
void Action::Create(actionType at_, int position_, const char *data_, int lenData_, bool mayCoalesce_) {
97
void Action::Create(actionType at_, Sci::Position position_, const char *data_, Sci::Position lenData_, bool mayCoalesce_) {
91
99
position = position_;
94
data = new char[lenData_];
95
memcpy(data, data_, lenData_);
102
data = std::unique_ptr<char []>(new char[lenData_]);
103
memcpy(&data[0], data_, lenData_);
97
105
lenData = lenData_;
98
106
mayCoalesce = mayCoalesce_;
101
void Action::Destroy() {
106
void Action::Grab(Action *source) {
109
position = source->position;
112
lenData = source->lenData;
113
mayCoalesce = source->mayCoalesce;
115
// Ownership of source data transferred to this
116
source->position = 0;
117
source->at = startAction;
120
source->mayCoalesce = true;
109
void Action::Clear() {
123
114
// The undo history stores a sequence of user operations that represent the user's view of the
154
144
UndoHistory::~UndoHistory() {
159
147
void UndoHistory::EnsureUndoRoom() {
160
148
// Have to test that there is room for 2 more actions in the array
161
149
// as two actions may be created by the calling function
162
if (currentAction >= (lenActions - 2)) {
150
if (static_cast<size_t>(currentAction) >= (actions.size() - 2)) {
163
151
// Run out of undo nodes so extend the array
164
int lenActionsNew = lenActions * 2;
165
Action *actionsNew = new Action[lenActionsNew];
166
for (int act = 0; act <= currentAction; act++)
167
actionsNew[act].Grab(&actions[act]);
169
lenActions = lenActionsNew;
170
actions = actionsNew;
152
actions.resize(actions.size() * 2);
174
const char *UndoHistory::AppendAction(actionType at, int position, const char *data, int lengthData,
156
const char *UndoHistory::AppendAction(actionType at, Sci::Position position, const char *data, Sci::Position lengthData,
175
157
bool &startSequence, bool mayCoalesce) {
176
158
EnsureUndoRoom();
177
159
//Platform::DebugPrintf("%% %d action %d %d %d\n", at, position, lengthData, currentAction);
194
176
// See if current action can be coalesced into previous action
195
177
// Will work if both are inserts or deletes and position is same
196
#if defined(_MSC_VER) && defined(_PREFAST_)
197
// Visual Studio 2013 Code Analysis wrongly believes actions can be NULL at its next reference
198
__analysis_assume(actions);
200
178
if ((currentAction == savePoint) || (currentAction == tentativePoint)) {
202
180
} else if (!actions[currentAction].mayCoalesce) {
241
219
startSequence = oldCurrentAction != currentAction;
242
int actionWithData = currentAction;
220
const int actionWithData = currentAction;
243
221
actions[currentAction].Create(at, position, data, lengthData, mayCoalesce);
245
223
actions[currentAction].Create(startAction);
246
224
maxAction = currentAction;
247
return actions[actionWithData].data;
225
return actions[actionWithData].data.get();
250
228
void UndoHistory::BeginUndoAction() {
348
326
int UndoHistory::StartRedo() {
349
327
// Drop any leading startAction
350
if (actions[currentAction].at == startAction && currentAction < maxAction)
328
if (currentAction < maxAction && actions[currentAction].at == startAction)
353
331
// Count the steps in this action
354
332
int act = currentAction;
355
while (actions[act].at != startAction && act < maxAction) {
333
while (act < maxAction && actions[act].at != startAction) {
358
336
return act - currentAction;
375
353
CellBuffer::~CellBuffer() {
378
char CellBuffer::CharAt(int position) const {
356
char CellBuffer::CharAt(Sci::Position position) const {
379
357
return substance.ValueAt(position);
382
void CellBuffer::GetCharRange(char *buffer, int position, int lengthRetrieve) const {
360
void CellBuffer::GetCharRange(char *buffer, Sci::Position position, Sci::Position lengthRetrieve) const {
383
361
if (lengthRetrieve <= 0)
385
363
if (position < 0)
392
370
substance.GetRange(buffer, position, lengthRetrieve);
395
char CellBuffer::StyleAt(int position) const {
373
char CellBuffer::StyleAt(Sci::Position position) const {
396
374
return style.ValueAt(position);
399
void CellBuffer::GetStyleRange(unsigned char *buffer, int position, int lengthRetrieve) const {
377
void CellBuffer::GetStyleRange(unsigned char *buffer, Sci::Position position, Sci::Position lengthRetrieve) const {
400
378
if (lengthRetrieve < 0)
402
380
if (position < 0)
413
391
return substance.BufferPointer();
416
const char *CellBuffer::RangePointer(int position, int rangeLength) {
394
const char *CellBuffer::RangePointer(Sci::Position position, Sci::Position rangeLength) {
417
395
return substance.RangePointer(position, rangeLength);
420
int CellBuffer::GapPosition() const {
398
Sci::Position CellBuffer::GapPosition() const {
421
399
return substance.GapPosition();
424
402
// The char* returned is to an allocation owned by the undo history
425
const char *CellBuffer::InsertString(int position, const char *s, int insertLength, bool &startSequence) {
403
const char *CellBuffer::InsertString(Sci::Position position, const char *s, Sci::Position insertLength, bool &startSequence) {
426
404
// InsertString and DeleteChars are the bottleneck though which all changes occur
427
405
const char *data = s;
440
bool CellBuffer::SetStyleAt(int position, char styleValue) {
441
char curVal = style.ValueAt(position);
418
bool CellBuffer::SetStyleAt(Sci::Position position, char styleValue) {
419
const char curVal = style.ValueAt(position);
442
420
if (curVal != styleValue) {
443
421
style.SetValueAt(position, styleValue);
450
bool CellBuffer::SetStyleFor(int position, int lengthStyle, char styleValue) {
428
bool CellBuffer::SetStyleFor(Sci::Position position, Sci::Position lengthStyle, char styleValue) {
451
429
bool changed = false;
452
430
PLATFORM_ASSERT(lengthStyle == 0 ||
453
431
(lengthStyle > 0 && lengthStyle + position <= style.Length()));
454
432
while (lengthStyle--) {
455
char curVal = style.ValueAt(position);
433
const char curVal = style.ValueAt(position);
456
434
if (curVal != styleValue) {
457
435
style.SetValueAt(position, styleValue);
465
443
// The char* returned is to an allocation owned by the undo history
466
const char *CellBuffer::DeleteChars(int position, int deleteLength, bool &startSequence) {
444
const char *CellBuffer::DeleteChars(Sci::Position position, Sci::Position deleteLength, bool &startSequence) {
467
445
// InsertString and DeleteChars are the bottleneck though which all changes occur
468
446
PLATFORM_ASSERT(deleteLength > 0);
469
447
const char *data = 0;
483
int CellBuffer::Length() const {
461
Sci::Position CellBuffer::Length() const {
484
462
return substance.Length();
487
void CellBuffer::Allocate(int newSize) {
465
void CellBuffer::Allocate(Sci::Position newSize) {
488
466
substance.ReAllocate(newSize);
489
467
style.ReAllocate(newSize);
499
bool CellBuffer::ContainsLineEnd(const char *s, int length) const {
477
bool CellBuffer::ContainsLineEnd(const char *s, Sci::Position length) const {
500
478
unsigned char chBeforePrev = 0;
501
479
unsigned char chPrev = 0;
502
for (int i = 0; i < length; i++) {
480
for (Sci::Position i = 0; i < length; i++) {
503
481
const unsigned char ch = s[i];
504
482
if ((ch == '\r') || (ch == '\n')) {
506
484
} else if (utf8LineEnds) {
507
unsigned char back3[3] = { chBeforePrev, chPrev, ch };
485
const unsigned char back3[3] = { chBeforePrev, chPrev, ch };
508
486
if (UTF8IsSeparator(back3) || UTF8IsNEL(back3 + 1)) {
519
497
lv.SetPerLine(pl);
522
int CellBuffer::Lines() const {
500
Sci::Line CellBuffer::Lines() const {
523
501
return lv.Lines();
526
int CellBuffer::LineStart(int line) const {
504
Sci::Position CellBuffer::LineStart(Sci::Line line) const {
529
507
else if (line >= Lines())
569
void CellBuffer::InsertLine(int line, int position, bool lineStart) {
547
void CellBuffer::InsertLine(Sci::Line line, Sci::Position position, bool lineStart) {
570
548
lv.InsertLine(line, position, lineStart);
573
void CellBuffer::RemoveLine(int line) {
551
void CellBuffer::RemoveLine(Sci::Line line) {
574
552
lv.RemoveLine(line);
577
bool CellBuffer::UTF8LineEndOverlaps(int position) const {
578
unsigned char bytes[] = {
555
bool CellBuffer::UTF8LineEndOverlaps(Sci::Position position) const {
556
const unsigned char bytes[] = {
579
557
static_cast<unsigned char>(substance.ValueAt(position-2)),
580
558
static_cast<unsigned char>(substance.ValueAt(position-1)),
581
559
static_cast<unsigned char>(substance.ValueAt(position)),
588
566
// Reinitialize line data -- too much work to preserve
592
int length = Length();
569
Sci::Position position = 0;
570
Sci::Position length = Length();
571
Sci::Line lineInsert = 1;
594
572
bool atLineStart = true;
595
573
lv.InsertText(lineInsert-1, length);
596
574
unsigned char chBeforePrev = 0;
597
575
unsigned char chPrev = 0;
598
for (int i = 0; i < length; i++) {
599
unsigned char ch = substance.ValueAt(position + i);
576
for (Sci::Position i = 0; i < length; i++) {
577
const unsigned char ch = substance.ValueAt(position + i);
600
578
if (ch == '\r') {
601
579
InsertLine(lineInsert, (position + i) + 1, atLineStart);
611
589
} else if (utf8LineEnds) {
612
unsigned char back3[3] = {chBeforePrev, chPrev, ch};
590
const unsigned char back3[3] = {chBeforePrev, chPrev, ch};
613
591
if (UTF8IsSeparator(back3) || UTF8IsNEL(back3+1)) {
614
592
InsertLine(lineInsert, (position + i) + 1, atLineStart);
623
void CellBuffer::BasicInsertString(int position, const char *s, int insertLength) {
601
void CellBuffer::BasicInsertString(Sci::Position position, const char *s, Sci::Position insertLength) {
624
602
if (insertLength == 0)
626
604
PLATFORM_ASSERT(insertLength > 0);
628
unsigned char chAfter = substance.ValueAt(position);
606
const unsigned char chAfter = substance.ValueAt(position);
629
607
bool breakingUTF8LineEnd = false;
630
608
if (utf8LineEnds && UTF8IsTrailByte(chAfter)) {
631
609
breakingUTF8LineEnd = UTF8LineEndOverlaps(position);
634
612
substance.InsertFromArray(position, s, 0, insertLength);
635
613
style.InsertValue(position, insertLength, 0);
637
int lineInsert = lv.LineFromPosition(position) + 1;
615
Sci::Line lineInsert = lv.LineFromPosition(position) + 1;
638
616
bool atLineStart = lv.LineStart(lineInsert-1) == position;
639
617
// Point all the lines after the insertion point further along in the buffer
640
618
lv.InsertText(lineInsert-1, insertLength);
665
643
} else if (utf8LineEnds) {
666
unsigned char back3[3] = {chBeforePrev, chPrev, ch};
644
const unsigned char back3[3] = {chBeforePrev, chPrev, ch};
667
645
if (UTF8IsSeparator(back3) || UTF8IsNEL(back3+1)) {
668
646
InsertLine(lineInsert, (position + i) + 1, atLineStart);
681
659
} else if (utf8LineEnds && !UTF8IsAscii(chAfter)) {
682
660
// May have end of UTF-8 line end in buffer and start in insertion
683
661
for (int j = 0; j < UTF8SeparatorLength-1; j++) {
684
unsigned char chAt = substance.ValueAt(position + insertLength + j);
685
unsigned char back3[3] = {chBeforePrev, chPrev, chAt};
662
const unsigned char chAt = substance.ValueAt(position + insertLength + j);
663
const unsigned char back3[3] = {chBeforePrev, chPrev, chAt};
686
664
if (UTF8IsSeparator(back3)) {
687
665
InsertLine(lineInsert, (position + insertLength + j) + 1, atLineStart);
709
687
// Have to fix up line positions before doing deletion as looking at text in buffer
710
688
// to work out which lines have been removed
712
int lineRemove = lv.LineFromPosition(position) + 1;
690
Sci::Line lineRemove = lv.LineFromPosition(position) + 1;
713
691
lv.InsertText(lineRemove-1, - (deleteLength));
714
unsigned char chPrev = substance.ValueAt(position - 1);
715
unsigned char chBefore = chPrev;
692
const unsigned char chPrev = substance.ValueAt(position - 1);
693
const unsigned char chBefore = chPrev;
716
694
unsigned char chNext = substance.ValueAt(position);
717
695
bool ignoreNL = false;
718
696
if (chPrev == '\r' && chNext == '\n') {
743
721
} else if (utf8LineEnds) {
744
722
if (!UTF8IsAscii(ch)) {
745
unsigned char next3[3] = {ch, chNext,
723
const unsigned char next3[3] = {ch, chNext,
746
724
static_cast<unsigned char>(substance.ValueAt(position + i + 2))};
747
725
if (UTF8IsSeparator(next3) || UTF8IsNEL(next3)) {
748
726
RemoveLine(lineRemove);
755
733
// May have to fix up end if last deletion causes cr to be next to lf
756
734
// or removes one of a crlf pair
757
char chAfter = substance.ValueAt(position + deleteLength);
735
const char chAfter = substance.ValueAt(position + deleteLength);
758
736
if (chBefore == '\r' && chAfter == '\n') {
759
737
// Using lineRemove-1 as cr ended line before start of deletion
760
738
RemoveLine(lineRemove - 1);
783
761
uh.EndUndoAction();
786
void CellBuffer::AddUndoAction(int token, bool mayCoalesce) {
764
void CellBuffer::AddUndoAction(Sci::Position token, bool mayCoalesce) {
787
765
bool startSequence;
788
766
uh.AppendAction(containerAction, token, 0, 0, startSequence, mayCoalesce);
814
792
BasicDeleteChars(actionStep.position, actionStep.lenData);
815
793
} else if (actionStep.at == removeAction) {
816
BasicInsertString(actionStep.position, actionStep.data, actionStep.lenData);
794
BasicInsertString(actionStep.position, actionStep.data.get(), actionStep.lenData);
818
796
uh.CompletedUndoStep();
833
811
void CellBuffer::PerformRedoStep() {
834
812
const Action &actionStep = uh.GetRedoStep();
835
813
if (actionStep.at == insertAction) {
836
BasicInsertString(actionStep.position, actionStep.data, actionStep.lenData);
814
BasicInsertString(actionStep.position, actionStep.data.get(), actionStep.lenData);
837
815
} else if (actionStep.at == removeAction) {
838
816
BasicDeleteChars(actionStep.position, actionStep.lenData);