]> git.saurik.com Git - wxWidgets.git/blob - src/stc/scintilla/src/CellBuffer.cxx
be0440304d1bac27d7e1c427ade6e95661d28fef
[wxWidgets.git] / src / stc / scintilla / src / CellBuffer.cxx
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 <stdio.h>
9 #include <string.h>
10 #include <stdlib.h>
11 #include <stdarg.h>
12
13 #include "Platform.h"
14
15 #include "Scintilla.h"
16 #include "SplitVector.h"
17 #include "Partitioning.h"
18 #include "CellBuffer.h"
19
20 #ifdef SCI_NAMESPACE
21 using namespace Scintilla;
22 #endif
23
24 LineVector::LineVector() : starts(256), perLine(0) {
25 Init();
26 }
27
28 LineVector::~LineVector() {
29 starts.DeleteAll();
30 }
31
32 void LineVector::Init() {
33 starts.DeleteAll();
34 if (perLine) {
35 perLine->Init();
36 }
37 }
38
39 void LineVector::SetPerLine(PerLine *pl) {
40 perLine = pl;
41 }
42
43 void LineVector::InsertText(int line, int delta) {
44 starts.InsertText(line, delta);
45 }
46
47 void LineVector::InsertLine(int line, int position) {
48 starts.InsertPartition(line, position);
49 if (perLine) {
50 perLine->InsertLine(line);
51 }
52 }
53
54 void LineVector::SetLineStart(int line, int position) {
55 starts.SetPartitionStartPosition(line, position);
56 }
57
58 void LineVector::RemoveLine(int line) {
59 starts.RemovePartition(line);
60 if (perLine) {
61 perLine->RemoveLine(line);
62 }
63 }
64
65 int LineVector::LineFromPosition(int pos) const {
66 return starts.PartitionFromPosition(pos);
67 }
68
69 Action::Action() {
70 at = startAction;
71 position = 0;
72 data = 0;
73 lenData = 0;
74 mayCoalesce = false;
75 }
76
77 Action::~Action() {
78 Destroy();
79 }
80
81 void Action::Create(actionType at_, int position_, char *data_, int lenData_, bool mayCoalesce_) {
82 delete []data;
83 position = position_;
84 at = at_;
85 data = data_;
86 lenData = lenData_;
87 mayCoalesce = mayCoalesce_;
88 }
89
90 void Action::Destroy() {
91 delete []data;
92 data = 0;
93 }
94
95 void Action::Grab(Action *source) {
96 delete []data;
97
98 position = source->position;
99 at = source->at;
100 data = source->data;
101 lenData = source->lenData;
102 mayCoalesce = source->mayCoalesce;
103
104 // Ownership of source data transferred to this
105 source->position = 0;
106 source->at = startAction;
107 source->data = 0;
108 source->lenData = 0;
109 source->mayCoalesce = true;
110 }
111
112 // The undo history stores a sequence of user operations that represent the user's view of the
113 // commands executed on the text.
114 // Each user operation contains a sequence of text insertion and text deletion actions.
115 // All the user operations are stored in a list of individual actions with 'start' actions used
116 // as delimiters between user operations.
117 // Initially there is one start action in the history.
118 // As each action is performed, it is recorded in the history. The action may either become
119 // part of the current user operation or may start a new user operation. If it is to be part of the
120 // current operation, then it overwrites the current last action. If it is to be part of a new
121 // operation, it is appended after the current last action.
122 // After writing the new action, a new start action is appended at the end of the history.
123 // The decision of whether to start a new user operation is based upon two factors. If a
124 // compound operation has been explicitly started by calling BeginUndoAction and no matching
125 // EndUndoAction (these calls nest) has been called, then the action is coalesced into the current
126 // operation. If there is no outstanding BeginUndoAction call then a new operation is started
127 // unless it looks as if the new action is caused by the user typing or deleting a stream of text.
128 // Sequences that look like typing or deletion are coalesced into a single user operation.
129
130 UndoHistory::UndoHistory() {
131
132 lenActions = 100;
133 actions = new Action[lenActions];
134 maxAction = 0;
135 currentAction = 0;
136 undoSequenceDepth = 0;
137 savePoint = 0;
138
139 actions[currentAction].Create(startAction);
140 }
141
142 UndoHistory::~UndoHistory() {
143 delete []actions;
144 actions = 0;
145 }
146
147 void UndoHistory::EnsureUndoRoom() {
148 // Have to test that there is room for 2 more actions in the array
149 // as two actions may be created by the calling function
150 if (currentAction >= (lenActions - 2)) {
151 // Run out of undo nodes so extend the array
152 int lenActionsNew = lenActions * 2;
153 Action *actionsNew = new Action[lenActionsNew];
154 for (int act = 0; act <= currentAction; act++)
155 actionsNew[act].Grab(&actions[act]);
156 delete []actions;
157 lenActions = lenActionsNew;
158 actions = actionsNew;
159 }
160 }
161
162 void UndoHistory::AppendAction(actionType at, int position, char *data, int lengthData,
163 bool &startSequence, bool mayCoalesce) {
164 EnsureUndoRoom();
165 //Platform::DebugPrintf("%% %d action %d %d %d\n", at, position, lengthData, currentAction);
166 //Platform::DebugPrintf("^ %d action %d %d\n", actions[currentAction - 1].at,
167 // actions[currentAction - 1].position, actions[currentAction - 1].lenData);
168 if (currentAction < savePoint) {
169 savePoint = -1;
170 }
171 int oldCurrentAction = currentAction;
172 if (currentAction >= 1) {
173 if (0 == undoSequenceDepth) {
174 // Top level actions may not always be coalesced
175 int targetAct = -1;
176 const Action *actPrevious = &(actions[currentAction + targetAct]);
177 // Container actions may forward the coalesce state of Scintilla Actions.
178 while ((actPrevious->at == containerAction) && actPrevious->mayCoalesce) {
179 targetAct--;
180 actPrevious = &(actions[currentAction + targetAct]);
181 }
182 // See if current action can be coalesced into previous action
183 // Will work if both are inserts or deletes and position is same
184 if (currentAction == savePoint) {
185 currentAction++;
186 } else if (!actions[currentAction].mayCoalesce) {
187 // Not allowed to coalesce if this set
188 currentAction++;
189 } else if (!mayCoalesce || !actPrevious->mayCoalesce) {
190 currentAction++;
191 } else if (at == containerAction || actions[currentAction].at == containerAction) {
192 ; // A coalescible containerAction
193 } else if ((at != actPrevious->at) && (actPrevious->at != startAction)) {
194 currentAction++;
195 } else if ((at == insertAction) &&
196 (position != (actPrevious->position + actPrevious->lenData))) {
197 // Insertions must be immediately after to coalesce
198 currentAction++;
199 } else if (at == removeAction) {
200 if ((lengthData == 1) || (lengthData == 2)){
201 if ((position + lengthData) == actPrevious->position) {
202 ; // Backspace -> OK
203 } else if (position == actPrevious->position) {
204 ; // Delete -> OK
205 } else {
206 // Removals must be at same position to coalesce
207 currentAction++;
208 }
209 } else {
210 // Removals must be of one character to coalesce
211 currentAction++;
212 }
213 } else {
214 // Action coalesced.
215 }
216
217 } else {
218 // Actions not at top level are always coalesced unless this is after return to top level
219 if (!actions[currentAction].mayCoalesce)
220 currentAction++;
221 }
222 } else {
223 currentAction++;
224 }
225 startSequence = oldCurrentAction != currentAction;
226 actions[currentAction].Create(at, position, data, lengthData, mayCoalesce);
227 currentAction++;
228 actions[currentAction].Create(startAction);
229 maxAction = currentAction;
230 }
231
232 void UndoHistory::BeginUndoAction() {
233 EnsureUndoRoom();
234 if (undoSequenceDepth == 0) {
235 if (actions[currentAction].at != startAction) {
236 currentAction++;
237 actions[currentAction].Create(startAction);
238 maxAction = currentAction;
239 }
240 actions[currentAction].mayCoalesce = false;
241 }
242 undoSequenceDepth++;
243 }
244
245 void UndoHistory::EndUndoAction() {
246 PLATFORM_ASSERT(undoSequenceDepth > 0);
247 EnsureUndoRoom();
248 undoSequenceDepth--;
249 if (0 == undoSequenceDepth) {
250 if (actions[currentAction].at != startAction) {
251 currentAction++;
252 actions[currentAction].Create(startAction);
253 maxAction = currentAction;
254 }
255 actions[currentAction].mayCoalesce = false;
256 }
257 }
258
259 void UndoHistory::DropUndoSequence() {
260 undoSequenceDepth = 0;
261 }
262
263 void UndoHistory::DeleteUndoHistory() {
264 for (int i = 1; i < maxAction; i++)
265 actions[i].Destroy();
266 maxAction = 0;
267 currentAction = 0;
268 actions[currentAction].Create(startAction);
269 savePoint = 0;
270 }
271
272 void UndoHistory::SetSavePoint() {
273 savePoint = currentAction;
274 }
275
276 bool UndoHistory::IsSavePoint() const {
277 return savePoint == currentAction;
278 }
279
280 bool UndoHistory::CanUndo() const {
281 return (currentAction > 0) && (maxAction > 0);
282 }
283
284 int UndoHistory::StartUndo() {
285 // Drop any trailing startAction
286 if (actions[currentAction].at == startAction && currentAction > 0)
287 currentAction--;
288
289 // Count the steps in this action
290 int act = currentAction;
291 while (actions[act].at != startAction && act > 0) {
292 act--;
293 }
294 return currentAction - act;
295 }
296
297 const Action &UndoHistory::GetUndoStep() const {
298 return actions[currentAction];
299 }
300
301 void UndoHistory::CompletedUndoStep() {
302 currentAction--;
303 }
304
305 bool UndoHistory::CanRedo() const {
306 return maxAction > currentAction;
307 }
308
309 int UndoHistory::StartRedo() {
310 // Drop any leading startAction
311 if (actions[currentAction].at == startAction && currentAction < maxAction)
312 currentAction++;
313
314 // Count the steps in this action
315 int act = currentAction;
316 while (actions[act].at != startAction && act < maxAction) {
317 act++;
318 }
319 return act - currentAction;
320 }
321
322 const Action &UndoHistory::GetRedoStep() const {
323 return actions[currentAction];
324 }
325
326 void UndoHistory::CompletedRedoStep() {
327 currentAction++;
328 }
329
330 CellBuffer::CellBuffer() {
331 readOnly = false;
332 collectingUndo = true;
333 }
334
335 CellBuffer::~CellBuffer() {
336 }
337
338 char CellBuffer::CharAt(int position) const {
339 return substance.ValueAt(position);
340 }
341
342 void CellBuffer::GetCharRange(char *buffer, int position, int lengthRetrieve) {
343 if (lengthRetrieve < 0)
344 return;
345 if (position < 0)
346 return;
347 if ((position + lengthRetrieve) > substance.Length()) {
348 Platform::DebugPrintf("Bad GetCharRange %d for %d of %d\n", position,
349 lengthRetrieve, substance.Length());
350 return;
351 }
352
353 for (int i=0; i<lengthRetrieve; i++) {
354 *buffer++ = substance.ValueAt(position + i);
355 }
356 }
357
358 char CellBuffer::StyleAt(int position) {
359 return style.ValueAt(position);
360 }
361
362 const char *CellBuffer::BufferPointer() {
363 return substance.BufferPointer();
364 }
365
366 // The char* returned is to an allocation owned by the undo history
367 const char *CellBuffer::InsertString(int position, const char *s, int insertLength, bool &startSequence) {
368 char *data = 0;
369 // InsertString and DeleteChars are the bottleneck though which all changes occur
370 if (!readOnly) {
371 if (collectingUndo) {
372 // Save into the undo/redo stack, but only the characters - not the formatting
373 // This takes up about half load time
374 data = new char[insertLength];
375 for (int i = 0; i < insertLength; i++) {
376 data[i] = s[i];
377 }
378 uh.AppendAction(insertAction, position, data, insertLength, startSequence);
379 }
380
381 BasicInsertString(position, s, insertLength);
382 }
383 return data;
384 }
385
386 bool CellBuffer::SetStyleAt(int position, char styleValue, char mask) {
387 styleValue &= mask;
388 char curVal = style.ValueAt(position);
389 if ((curVal & mask) != styleValue) {
390 style.SetValueAt(position, static_cast<char>((curVal & ~mask) | styleValue));
391 return true;
392 } else {
393 return false;
394 }
395 }
396
397 bool CellBuffer::SetStyleFor(int position, int lengthStyle, char styleValue, char mask) {
398 bool changed = false;
399 PLATFORM_ASSERT(lengthStyle == 0 ||
400 (lengthStyle > 0 && lengthStyle + position <= style.Length()));
401 while (lengthStyle--) {
402 char curVal = style.ValueAt(position);
403 if ((curVal & mask) != styleValue) {
404 style.SetValueAt(position, static_cast<char>((curVal & ~mask) | styleValue));
405 changed = true;
406 }
407 position++;
408 }
409 return changed;
410 }
411
412 // The char* returned is to an allocation owned by the undo history
413 const char *CellBuffer::DeleteChars(int position, int deleteLength, bool &startSequence) {
414 // InsertString and DeleteChars are the bottleneck though which all changes occur
415 PLATFORM_ASSERT(deleteLength > 0);
416 char *data = 0;
417 if (!readOnly) {
418 if (collectingUndo) {
419 // Save into the undo/redo stack, but only the characters - not the formatting
420 data = new char[deleteLength];
421 for (int i = 0; i < deleteLength; i++) {
422 data[i] = substance.ValueAt(position + i);
423 }
424 uh.AppendAction(removeAction, position, data, deleteLength, startSequence);
425 }
426
427 BasicDeleteChars(position, deleteLength);
428 }
429 return data;
430 }
431
432 int CellBuffer::Length() const {
433 return substance.Length();
434 }
435
436 void CellBuffer::Allocate(int newSize) {
437 substance.ReAllocate(newSize);
438 style.ReAllocate(newSize);
439 }
440
441 void CellBuffer::SetPerLine(PerLine *pl) {
442 lv.SetPerLine(pl);
443 }
444
445 int CellBuffer::Lines() const {
446 return lv.Lines();
447 }
448
449 int CellBuffer::LineStart(int line) const {
450 if (line < 0)
451 return 0;
452 else if (line >= Lines())
453 return Length();
454 else
455 return lv.LineStart(line);
456 }
457
458 bool CellBuffer::IsReadOnly() {
459 return readOnly;
460 }
461
462 void CellBuffer::SetReadOnly(bool set) {
463 readOnly = set;
464 }
465
466 void CellBuffer::SetSavePoint() {
467 uh.SetSavePoint();
468 }
469
470 bool CellBuffer::IsSavePoint() {
471 return uh.IsSavePoint();
472 }
473
474 // Without undo
475
476 void CellBuffer::InsertLine(int line, int position) {
477 lv.InsertLine(line, position);
478 }
479
480 void CellBuffer::RemoveLine(int line) {
481 lv.RemoveLine(line);
482 }
483
484 void CellBuffer::BasicInsertString(int position, const char *s, int insertLength) {
485 if (insertLength == 0)
486 return;
487 PLATFORM_ASSERT(insertLength > 0);
488
489 substance.InsertFromArray(position, s, 0, insertLength);
490 style.InsertValue(position, insertLength, 0);
491
492 int lineInsert = lv.LineFromPosition(position) + 1;
493 // Point all the lines after the insertion point further along in the buffer
494 lv.InsertText(lineInsert-1, insertLength);
495 char chPrev = substance.ValueAt(position - 1);
496 char chAfter = substance.ValueAt(position + insertLength);
497 if (chPrev == '\r' && chAfter == '\n') {
498 // Splitting up a crlf pair at position
499 InsertLine(lineInsert, position);
500 lineInsert++;
501 }
502 char ch = ' ';
503 for (int i = 0; i < insertLength; i++) {
504 ch = s[i];
505 if (ch == '\r') {
506 InsertLine(lineInsert, (position + i) + 1);
507 lineInsert++;
508 } else if (ch == '\n') {
509 if (chPrev == '\r') {
510 // Patch up what was end of line
511 lv.SetLineStart(lineInsert - 1, (position + i) + 1);
512 } else {
513 InsertLine(lineInsert, (position + i) + 1);
514 lineInsert++;
515 }
516 }
517 chPrev = ch;
518 }
519 // Joining two lines where last insertion is cr and following substance starts with lf
520 if (chAfter == '\n') {
521 if (ch == '\r') {
522 // End of line already in buffer so drop the newly created one
523 RemoveLine(lineInsert - 1);
524 }
525 }
526 }
527
528 void CellBuffer::BasicDeleteChars(int position, int deleteLength) {
529 if (deleteLength == 0)
530 return;
531
532 if ((position == 0) && (deleteLength == substance.Length())) {
533 // If whole buffer is being deleted, faster to reinitialise lines data
534 // than to delete each line.
535 lv.Init();
536 } else {
537 // Have to fix up line positions before doing deletion as looking at text in buffer
538 // to work out which lines have been removed
539
540 int lineRemove = lv.LineFromPosition(position) + 1;
541 lv.InsertText(lineRemove-1, - (deleteLength));
542 char chPrev = substance.ValueAt(position - 1);
543 char chBefore = chPrev;
544 char chNext = substance.ValueAt(position);
545 bool ignoreNL = false;
546 if (chPrev == '\r' && chNext == '\n') {
547 // Move back one
548 lv.SetLineStart(lineRemove, position);
549 lineRemove++;
550 ignoreNL = true; // First \n is not real deletion
551 }
552
553 char ch = chNext;
554 for (int i = 0; i < deleteLength; i++) {
555 chNext = substance.ValueAt(position + i + 1);
556 if (ch == '\r') {
557 if (chNext != '\n') {
558 RemoveLine(lineRemove);
559 }
560 } else if (ch == '\n') {
561 if (ignoreNL) {
562 ignoreNL = false; // Further \n are real deletions
563 } else {
564 RemoveLine(lineRemove);
565 }
566 }
567
568 ch = chNext;
569 }
570 // May have to fix up end if last deletion causes cr to be next to lf
571 // or removes one of a crlf pair
572 char chAfter = substance.ValueAt(position + deleteLength);
573 if (chBefore == '\r' && chAfter == '\n') {
574 // Using lineRemove-1 as cr ended line before start of deletion
575 RemoveLine(lineRemove - 1);
576 lv.SetLineStart(lineRemove - 1, position + 1);
577 }
578 }
579 substance.DeleteRange(position, deleteLength);
580 style.DeleteRange(position, deleteLength);
581 }
582
583 bool CellBuffer::SetUndoCollection(bool collectUndo) {
584 collectingUndo = collectUndo;
585 uh.DropUndoSequence();
586 return collectingUndo;
587 }
588
589 bool CellBuffer::IsCollectingUndo() {
590 return collectingUndo;
591 }
592
593 void CellBuffer::BeginUndoAction() {
594 uh.BeginUndoAction();
595 }
596
597 void CellBuffer::EndUndoAction() {
598 uh.EndUndoAction();
599 }
600
601 void CellBuffer::AddUndoAction(int token, bool mayCoalesce) {
602 bool startSequence;
603 uh.AppendAction(containerAction, token, 0, 0, startSequence, mayCoalesce);
604 }
605
606 void CellBuffer::DeleteUndoHistory() {
607 uh.DeleteUndoHistory();
608 }
609
610 bool CellBuffer::CanUndo() {
611 return uh.CanUndo();
612 }
613
614 int CellBuffer::StartUndo() {
615 return uh.StartUndo();
616 }
617
618 const Action &CellBuffer::GetUndoStep() const {
619 return uh.GetUndoStep();
620 }
621
622 void CellBuffer::PerformUndoStep() {
623 const Action &actionStep = uh.GetUndoStep();
624 if (actionStep.at == insertAction) {
625 BasicDeleteChars(actionStep.position, actionStep.lenData);
626 } else if (actionStep.at == removeAction) {
627 BasicInsertString(actionStep.position, actionStep.data, actionStep.lenData);
628 }
629 uh.CompletedUndoStep();
630 }
631
632 bool CellBuffer::CanRedo() {
633 return uh.CanRedo();
634 }
635
636 int CellBuffer::StartRedo() {
637 return uh.StartRedo();
638 }
639
640 const Action &CellBuffer::GetRedoStep() const {
641 return uh.GetRedoStep();
642 }
643
644 void CellBuffer::PerformRedoStep() {
645 const Action &actionStep = uh.GetRedoStep();
646 if (actionStep.at == insertAction) {
647 BasicInsertString(actionStep.position, actionStep.data, actionStep.lenData);
648 } else if (actionStep.at == removeAction) {
649 BasicDeleteChars(actionStep.position, actionStep.lenData);
650 }
651 uh.CompletedRedoStep();
652 }
653