Set the working directory on Windows very early to also help code which uses relative...
[geany-mirror.git] / scintilla / src / CellBuffer.cxx
blob11b8b4acda2754dfe93b9b69c6a948562bfedfdd
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 <stdio.h>
9 #include <string.h>
10 #include <stdlib.h>
11 #include <stdarg.h>
13 #include "Platform.h"
15 #include "Scintilla.h"
16 #include "SplitVector.h"
17 #include "Partitioning.h"
18 #include "CellBuffer.h"
20 #ifdef SCI_NAMESPACE
21 using namespace Scintilla;
22 #endif
24 LineVector::LineVector() : starts(256), perLine(0) {
25 Init();
28 LineVector::~LineVector() {
29 starts.DeleteAll();
32 void LineVector::Init() {
33 starts.DeleteAll();
34 if (perLine) {
35 perLine->Init();
39 void LineVector::SetPerLine(PerLine *pl) {
40 perLine = pl;
43 void LineVector::InsertText(int line, int delta) {
44 starts.InsertText(line, delta);
47 void LineVector::InsertLine(int line, int position, bool lineStart) {
48 starts.InsertPartition(line, position);
49 if (perLine) {
50 if ((line > 0) && lineStart)
51 line--;
52 perLine->InsertLine(line);
56 void LineVector::SetLineStart(int line, int position) {
57 starts.SetPartitionStartPosition(line, position);
60 void LineVector::RemoveLine(int line) {
61 starts.RemovePartition(line);
62 if (perLine) {
63 perLine->RemoveLine(line);
67 int LineVector::LineFromPosition(int pos) const {
68 return starts.PartitionFromPosition(pos);
71 Action::Action() {
72 at = startAction;
73 position = 0;
74 data = 0;
75 lenData = 0;
76 mayCoalesce = false;
79 Action::~Action() {
80 Destroy();
83 void Action::Create(actionType at_, int position_, char *data_, int lenData_, bool mayCoalesce_) {
84 delete []data;
85 position = position_;
86 at = at_;
87 data = data_;
88 lenData = lenData_;
89 mayCoalesce = mayCoalesce_;
92 void Action::Destroy() {
93 delete []data;
94 data = 0;
97 void Action::Grab(Action *source) {
98 delete []data;
100 position = source->position;
101 at = source->at;
102 data = source->data;
103 lenData = source->lenData;
104 mayCoalesce = source->mayCoalesce;
106 // Ownership of source data transferred to this
107 source->position = 0;
108 source->at = startAction;
109 source->data = 0;
110 source->lenData = 0;
111 source->mayCoalesce = true;
114 // The undo history stores a sequence of user operations that represent the user's view of the
115 // commands executed on the text.
116 // Each user operation contains a sequence of text insertion and text deletion actions.
117 // All the user operations are stored in a list of individual actions with 'start' actions used
118 // as delimiters between user operations.
119 // Initially there is one start action in the history.
120 // As each action is performed, it is recorded in the history. The action may either become
121 // part of the current user operation or may start a new user operation. If it is to be part of the
122 // current operation, then it overwrites the current last action. If it is to be part of a new
123 // operation, it is appended after the current last action.
124 // After writing the new action, a new start action is appended at the end of the history.
125 // The decision of whether to start a new user operation is based upon two factors. If a
126 // compound operation has been explicitly started by calling BeginUndoAction and no matching
127 // EndUndoAction (these calls nest) has been called, then the action is coalesced into the current
128 // operation. If there is no outstanding BeginUndoAction call then a new operation is started
129 // unless it looks as if the new action is caused by the user typing or deleting a stream of text.
130 // Sequences that look like typing or deletion are coalesced into a single user operation.
132 UndoHistory::UndoHistory() {
134 lenActions = 100;
135 actions = new Action[lenActions];
136 maxAction = 0;
137 currentAction = 0;
138 undoSequenceDepth = 0;
139 savePoint = 0;
141 actions[currentAction].Create(startAction);
144 UndoHistory::~UndoHistory() {
145 delete []actions;
146 actions = 0;
149 void UndoHistory::EnsureUndoRoom() {
150 // Have to test that there is room for 2 more actions in the array
151 // as two actions may be created by the calling function
152 if (currentAction >= (lenActions - 2)) {
153 // Run out of undo nodes so extend the array
154 int lenActionsNew = lenActions * 2;
155 Action *actionsNew = new Action[lenActionsNew];
156 for (int act = 0; act <= currentAction; act++)
157 actionsNew[act].Grab(&actions[act]);
158 delete []actions;
159 lenActions = lenActionsNew;
160 actions = actionsNew;
164 void UndoHistory::AppendAction(actionType at, int position, char *data, int lengthData,
165 bool &startSequence, bool mayCoalesce) {
166 EnsureUndoRoom();
167 //Platform::DebugPrintf("%% %d action %d %d %d\n", at, position, lengthData, currentAction);
168 //Platform::DebugPrintf("^ %d action %d %d\n", actions[currentAction - 1].at,
169 // actions[currentAction - 1].position, actions[currentAction - 1].lenData);
170 if (currentAction < savePoint) {
171 savePoint = -1;
173 int oldCurrentAction = currentAction;
174 if (currentAction >= 1) {
175 if (0 == undoSequenceDepth) {
176 // Top level actions may not always be coalesced
177 int targetAct = -1;
178 const Action *actPrevious = &(actions[currentAction + targetAct]);
179 // Container actions may forward the coalesce state of Scintilla Actions.
180 while ((actPrevious->at == containerAction) && actPrevious->mayCoalesce) {
181 targetAct--;
182 actPrevious = &(actions[currentAction + targetAct]);
184 // See if current action can be coalesced into previous action
185 // Will work if both are inserts or deletes and position is same
186 if (currentAction == savePoint) {
187 currentAction++;
188 } else if (!actions[currentAction].mayCoalesce) {
189 // Not allowed to coalesce if this set
190 currentAction++;
191 } else if (!mayCoalesce || !actPrevious->mayCoalesce) {
192 currentAction++;
193 } else if (at == containerAction || actions[currentAction].at == containerAction) {
194 ; // A coalescible containerAction
195 } else if ((at != actPrevious->at) && (actPrevious->at != startAction)) {
196 currentAction++;
197 } else if ((at == insertAction) &&
198 (position != (actPrevious->position + actPrevious->lenData))) {
199 // Insertions must be immediately after to coalesce
200 currentAction++;
201 } else if (at == removeAction) {
202 if ((lengthData == 1) || (lengthData == 2)) {
203 if ((position + lengthData) == actPrevious->position) {
204 ; // Backspace -> OK
205 } else if (position == actPrevious->position) {
206 ; // Delete -> OK
207 } else {
208 // Removals must be at same position to coalesce
209 currentAction++;
211 } else {
212 // Removals must be of one character to coalesce
213 currentAction++;
215 } else {
216 // Action coalesced.
219 } else {
220 // Actions not at top level are always coalesced unless this is after return to top level
221 if (!actions[currentAction].mayCoalesce)
222 currentAction++;
224 } else {
225 currentAction++;
227 startSequence = oldCurrentAction != currentAction;
228 actions[currentAction].Create(at, position, data, lengthData, mayCoalesce);
229 currentAction++;
230 actions[currentAction].Create(startAction);
231 maxAction = currentAction;
234 void UndoHistory::BeginUndoAction() {
235 EnsureUndoRoom();
236 if (undoSequenceDepth == 0) {
237 if (actions[currentAction].at != startAction) {
238 currentAction++;
239 actions[currentAction].Create(startAction);
240 maxAction = currentAction;
242 actions[currentAction].mayCoalesce = false;
244 undoSequenceDepth++;
247 void UndoHistory::EndUndoAction() {
248 PLATFORM_ASSERT(undoSequenceDepth > 0);
249 EnsureUndoRoom();
250 undoSequenceDepth--;
251 if (0 == undoSequenceDepth) {
252 if (actions[currentAction].at != startAction) {
253 currentAction++;
254 actions[currentAction].Create(startAction);
255 maxAction = currentAction;
257 actions[currentAction].mayCoalesce = false;
261 void UndoHistory::DropUndoSequence() {
262 undoSequenceDepth = 0;
265 void UndoHistory::DeleteUndoHistory() {
266 for (int i = 1; i < maxAction; i++)
267 actions[i].Destroy();
268 maxAction = 0;
269 currentAction = 0;
270 actions[currentAction].Create(startAction);
271 savePoint = 0;
274 void UndoHistory::SetSavePoint() {
275 savePoint = currentAction;
278 bool UndoHistory::IsSavePoint() const {
279 return savePoint == currentAction;
282 bool UndoHistory::CanUndo() const {
283 return (currentAction > 0) && (maxAction > 0);
286 int UndoHistory::StartUndo() {
287 // Drop any trailing startAction
288 if (actions[currentAction].at == startAction && currentAction > 0)
289 currentAction--;
291 // Count the steps in this action
292 int act = currentAction;
293 while (actions[act].at != startAction && act > 0) {
294 act--;
296 return currentAction - act;
299 const Action &UndoHistory::GetUndoStep() const {
300 return actions[currentAction];
303 void UndoHistory::CompletedUndoStep() {
304 currentAction--;
307 bool UndoHistory::CanRedo() const {
308 return maxAction > currentAction;
311 int UndoHistory::StartRedo() {
312 // Drop any leading startAction
313 if (actions[currentAction].at == startAction && currentAction < maxAction)
314 currentAction++;
316 // Count the steps in this action
317 int act = currentAction;
318 while (actions[act].at != startAction && act < maxAction) {
319 act++;
321 return act - currentAction;
324 const Action &UndoHistory::GetRedoStep() const {
325 return actions[currentAction];
328 void UndoHistory::CompletedRedoStep() {
329 currentAction++;
332 CellBuffer::CellBuffer() {
333 readOnly = false;
334 collectingUndo = true;
337 CellBuffer::~CellBuffer() {
340 char CellBuffer::CharAt(int position) const {
341 return substance.ValueAt(position);
344 void CellBuffer::GetCharRange(char *buffer, int position, int lengthRetrieve) const {
345 if (lengthRetrieve < 0)
346 return;
347 if (position < 0)
348 return;
349 if ((position + lengthRetrieve) > substance.Length()) {
350 Platform::DebugPrintf("Bad GetCharRange %d for %d of %d\n", position,
351 lengthRetrieve, substance.Length());
352 return;
354 substance.GetRange(buffer, position, lengthRetrieve);
357 char CellBuffer::StyleAt(int position) const {
358 return style.ValueAt(position);
361 void CellBuffer::GetStyleRange(unsigned char *buffer, int position, int lengthRetrieve) const {
362 if (lengthRetrieve < 0)
363 return;
364 if (position < 0)
365 return;
366 if ((position + lengthRetrieve) > style.Length()) {
367 Platform::DebugPrintf("Bad GetStyleRange %d for %d of %d\n", position,
368 lengthRetrieve, style.Length());
369 return;
371 style.GetRange(reinterpret_cast<char *>(buffer), position, lengthRetrieve);
374 const char *CellBuffer::BufferPointer() {
375 return substance.BufferPointer();
378 const char *CellBuffer::RangePointer(int position, int rangeLength) {
379 return substance.RangePointer(position, rangeLength);
382 int CellBuffer::GapPosition() const {
383 return substance.GapPosition();
386 // The char* returned is to an allocation owned by the undo history
387 const char *CellBuffer::InsertString(int position, const char *s, int insertLength, bool &startSequence) {
388 char *data = 0;
389 // InsertString and DeleteChars are the bottleneck though which all changes occur
390 if (!readOnly) {
391 if (collectingUndo) {
392 // Save into the undo/redo stack, but only the characters - not the formatting
393 // This takes up about half load time
394 data = new char[insertLength];
395 for (int i = 0; i < insertLength; i++) {
396 data[i] = s[i];
398 uh.AppendAction(insertAction, position, data, insertLength, startSequence);
401 BasicInsertString(position, s, insertLength);
403 return data;
406 bool CellBuffer::SetStyleAt(int position, char styleValue, char mask) {
407 styleValue &= mask;
408 char curVal = style.ValueAt(position);
409 if ((curVal & mask) != styleValue) {
410 style.SetValueAt(position, static_cast<char>((curVal & ~mask) | styleValue));
411 return true;
412 } else {
413 return false;
417 bool CellBuffer::SetStyleFor(int position, int lengthStyle, char styleValue, char mask) {
418 bool changed = false;
419 PLATFORM_ASSERT(lengthStyle == 0 ||
420 (lengthStyle > 0 && lengthStyle + position <= style.Length()));
421 while (lengthStyle--) {
422 char curVal = style.ValueAt(position);
423 if ((curVal & mask) != styleValue) {
424 style.SetValueAt(position, static_cast<char>((curVal & ~mask) | styleValue));
425 changed = true;
427 position++;
429 return changed;
432 // The char* returned is to an allocation owned by the undo history
433 const char *CellBuffer::DeleteChars(int position, int deleteLength, bool &startSequence) {
434 // InsertString and DeleteChars are the bottleneck though which all changes occur
435 PLATFORM_ASSERT(deleteLength > 0);
436 char *data = 0;
437 if (!readOnly) {
438 if (collectingUndo) {
439 // Save into the undo/redo stack, but only the characters - not the formatting
440 data = new char[deleteLength];
441 for (int i = 0; i < deleteLength; i++) {
442 data[i] = substance.ValueAt(position + i);
444 uh.AppendAction(removeAction, position, data, deleteLength, startSequence);
447 BasicDeleteChars(position, deleteLength);
449 return data;
452 int CellBuffer::Length() const {
453 return substance.Length();
456 void CellBuffer::Allocate(int newSize) {
457 substance.ReAllocate(newSize);
458 style.ReAllocate(newSize);
461 void CellBuffer::SetPerLine(PerLine *pl) {
462 lv.SetPerLine(pl);
465 int CellBuffer::Lines() const {
466 return lv.Lines();
469 int CellBuffer::LineStart(int line) const {
470 if (line < 0)
471 return 0;
472 else if (line >= Lines())
473 return Length();
474 else
475 return lv.LineStart(line);
478 bool CellBuffer::IsReadOnly() const {
479 return readOnly;
482 void CellBuffer::SetReadOnly(bool set) {
483 readOnly = set;
486 void CellBuffer::SetSavePoint() {
487 uh.SetSavePoint();
490 bool CellBuffer::IsSavePoint() {
491 return uh.IsSavePoint();
494 // Without undo
496 void CellBuffer::InsertLine(int line, int position, bool lineStart) {
497 lv.InsertLine(line, position, lineStart);
500 void CellBuffer::RemoveLine(int line) {
501 lv.RemoveLine(line);
504 void CellBuffer::BasicInsertString(int position, const char *s, int insertLength) {
505 if (insertLength == 0)
506 return;
507 PLATFORM_ASSERT(insertLength > 0);
509 substance.InsertFromArray(position, s, 0, insertLength);
510 style.InsertValue(position, insertLength, 0);
512 int lineInsert = lv.LineFromPosition(position) + 1;
513 bool atLineStart = lv.LineStart(lineInsert-1) == position;
514 // Point all the lines after the insertion point further along in the buffer
515 lv.InsertText(lineInsert-1, insertLength);
516 char chPrev = substance.ValueAt(position - 1);
517 char chAfter = substance.ValueAt(position + insertLength);
518 if (chPrev == '\r' && chAfter == '\n') {
519 // Splitting up a crlf pair at position
520 InsertLine(lineInsert, position, false);
521 lineInsert++;
523 char ch = ' ';
524 for (int i = 0; i < insertLength; i++) {
525 ch = s[i];
526 if (ch == '\r') {
527 InsertLine(lineInsert, (position + i) + 1, atLineStart);
528 lineInsert++;
529 } else if (ch == '\n') {
530 if (chPrev == '\r') {
531 // Patch up what was end of line
532 lv.SetLineStart(lineInsert - 1, (position + i) + 1);
533 } else {
534 InsertLine(lineInsert, (position + i) + 1, atLineStart);
535 lineInsert++;
538 chPrev = ch;
540 // Joining two lines where last insertion is cr and following substance starts with lf
541 if (chAfter == '\n') {
542 if (ch == '\r') {
543 // End of line already in buffer so drop the newly created one
544 RemoveLine(lineInsert - 1);
549 void CellBuffer::BasicDeleteChars(int position, int deleteLength) {
550 if (deleteLength == 0)
551 return;
553 if ((position == 0) && (deleteLength == substance.Length())) {
554 // If whole buffer is being deleted, faster to reinitialise lines data
555 // than to delete each line.
556 lv.Init();
557 } else {
558 // Have to fix up line positions before doing deletion as looking at text in buffer
559 // to work out which lines have been removed
561 int lineRemove = lv.LineFromPosition(position) + 1;
562 lv.InsertText(lineRemove-1, - (deleteLength));
563 char chPrev = substance.ValueAt(position - 1);
564 char chBefore = chPrev;
565 char chNext = substance.ValueAt(position);
566 bool ignoreNL = false;
567 if (chPrev == '\r' && chNext == '\n') {
568 // Move back one
569 lv.SetLineStart(lineRemove, position);
570 lineRemove++;
571 ignoreNL = true; // First \n is not real deletion
574 char ch = chNext;
575 for (int i = 0; i < deleteLength; i++) {
576 chNext = substance.ValueAt(position + i + 1);
577 if (ch == '\r') {
578 if (chNext != '\n') {
579 RemoveLine(lineRemove);
581 } else if (ch == '\n') {
582 if (ignoreNL) {
583 ignoreNL = false; // Further \n are real deletions
584 } else {
585 RemoveLine(lineRemove);
589 ch = chNext;
591 // May have to fix up end if last deletion causes cr to be next to lf
592 // or removes one of a crlf pair
593 char chAfter = substance.ValueAt(position + deleteLength);
594 if (chBefore == '\r' && chAfter == '\n') {
595 // Using lineRemove-1 as cr ended line before start of deletion
596 RemoveLine(lineRemove - 1);
597 lv.SetLineStart(lineRemove - 1, position + 1);
600 substance.DeleteRange(position, deleteLength);
601 style.DeleteRange(position, deleteLength);
604 bool CellBuffer::SetUndoCollection(bool collectUndo) {
605 collectingUndo = collectUndo;
606 uh.DropUndoSequence();
607 return collectingUndo;
610 bool CellBuffer::IsCollectingUndo() const {
611 return collectingUndo;
614 void CellBuffer::BeginUndoAction() {
615 uh.BeginUndoAction();
618 void CellBuffer::EndUndoAction() {
619 uh.EndUndoAction();
622 void CellBuffer::AddUndoAction(int token, bool mayCoalesce) {
623 bool startSequence;
624 uh.AppendAction(containerAction, token, 0, 0, startSequence, mayCoalesce);
627 void CellBuffer::DeleteUndoHistory() {
628 uh.DeleteUndoHistory();
631 bool CellBuffer::CanUndo() {
632 return uh.CanUndo();
635 int CellBuffer::StartUndo() {
636 return uh.StartUndo();
639 const Action &CellBuffer::GetUndoStep() const {
640 return uh.GetUndoStep();
643 void CellBuffer::PerformUndoStep() {
644 const Action &actionStep = uh.GetUndoStep();
645 if (actionStep.at == insertAction) {
646 BasicDeleteChars(actionStep.position, actionStep.lenData);
647 } else if (actionStep.at == removeAction) {
648 BasicInsertString(actionStep.position, actionStep.data, actionStep.lenData);
650 uh.CompletedUndoStep();
653 bool CellBuffer::CanRedo() {
654 return uh.CanRedo();
657 int CellBuffer::StartRedo() {
658 return uh.StartRedo();
661 const Action &CellBuffer::GetRedoStep() const {
662 return uh.GetRedoStep();
665 void CellBuffer::PerformRedoStep() {
666 const Action &actionStep = uh.GetRedoStep();
667 if (actionStep.at == insertAction) {
668 BasicInsertString(actionStep.position, actionStep.data, actionStep.lenData);
669 } else if (actionStep.at == removeAction) {
670 BasicDeleteChars(actionStep.position, actionStep.lenData);
672 uh.CompletedRedoStep();