diff options
author | nyamatongwe <unknown> | 2007-01-01 23:34:58 +0000 |
---|---|---|
committer | nyamatongwe <unknown> | 2007-01-01 23:34:58 +0000 |
commit | 9f6740c61ca15c172be2d5c04b672cd0eba7d0e4 (patch) | |
tree | 7ec27fd7c4ca3664e5ead3fb122a5aa87c713f41 | |
parent | c48897c8f67c8f4096fdb51269ec478b45694f7d (diff) | |
download | scintilla-mirror-9f6740c61ca15c172be2d5c04b672cd0eba7d0e4.tar.gz |
Major change to CellBuffer class with addition of Partitioning class and SplitVector
template. Inserting and deleting lines are made more efficient by lessening the
amount of per line information copied.
Marker data is only allocated for each line if markers are added.
-rw-r--r-- | gtk/ScintillaGTK.cxx | 2 | ||||
-rw-r--r-- | src/CellBuffer.cxx | 500 | ||||
-rw-r--r-- | src/CellBuffer.h | 69 | ||||
-rw-r--r-- | src/Document.cxx | 4 | ||||
-rw-r--r-- | src/DocumentAccessor.cxx | 2 | ||||
-rw-r--r-- | src/Editor.cxx | 4 | ||||
-rw-r--r-- | src/Partitioning.h | 184 | ||||
-rw-r--r-- | src/ScintillaBase.cxx | 2 | ||||
-rw-r--r-- | src/SplitVector.h | 221 | ||||
-rw-r--r-- | win32/ScintillaWin.cxx | 2 |
10 files changed, 607 insertions, 383 deletions
diff --git a/gtk/ScintillaGTK.cxx b/gtk/ScintillaGTK.cxx index 6ad3e3a24..01197f244 100644 --- a/gtk/ScintillaGTK.cxx +++ b/gtk/ScintillaGTK.cxx @@ -28,6 +28,8 @@ #endif #include "ContractionState.h" #include "SVector.h" +#include "SplitVector.h" +#include "Partitioning.h" #include "CellBuffer.h" #include "CallTip.h" #include "KeyMap.h" diff --git a/src/CellBuffer.cxx b/src/CellBuffer.cxx index bd124d011..7096d4419 100644 --- a/src/CellBuffer.cxx +++ b/src/CellBuffer.cxx @@ -14,6 +14,8 @@ #include "Scintilla.h" #include "SVector.h" +#include "SplitVector.h" +#include "Partitioning.h" #include "CellBuffer.h" MarkerHandleSet::MarkerHandleSet() { @@ -30,7 +32,7 @@ MarkerHandleSet::~MarkerHandleSet() { root = 0; } -int MarkerHandleSet::Length() { +int MarkerHandleSet::Length() const { int c = 0; MarkerHandleNumber *mhn = root; while (mhn) { @@ -40,7 +42,7 @@ int MarkerHandleSet::Length() { return c; } -int MarkerHandleSet::NumberFromHandle(int handle) { +int MarkerHandleSet::NumberFromHandle(int handle) const { MarkerHandleNumber *mhn = root; while (mhn) { if (mhn->handle == handle) { @@ -51,7 +53,7 @@ int MarkerHandleSet::NumberFromHandle(int handle) { return - 1; } -int MarkerHandleSet::MarkValue() { +int MarkerHandleSet::MarkValue() const { unsigned int m = 0; MarkerHandleNumber *mhn = root; while (mhn) { @@ -61,7 +63,7 @@ int MarkerHandleSet::MarkValue() { return m; } -bool MarkerHandleSet::Contains(int handle) { +bool MarkerHandleSet::Contains(int handle) const { MarkerHandleNumber *mhn = root; while (mhn) { if (mhn->handle == handle) { @@ -90,7 +92,7 @@ void MarkerHandleSet::RemoveHandle(int handle) { if (mhn->handle == handle) { *pmhn = mhn->next; delete mhn; - return ; + return; } pmhn = &((*pmhn)->next); } @@ -121,210 +123,154 @@ void MarkerHandleSet::CombineWith(MarkerHandleSet *other) { other->root = 0; } -LineVector::LineVector() { - linesData = 0; - lines = 0; - size = 0; - levels = 0; - sizeLevels = 0; +LineVector::LineVector() : starts(256) { handleCurrent = 1; - growSize = 1000; Init(); } LineVector::~LineVector() { - for (int line = 0; line < lines; line++) { - delete linesData[line].handleSet; - linesData[line].handleSet = 0; + starts.DeleteAll(); + for (int line = 0; line < markers.Length(); line++) { + delete markers[line]; + markers[line] = 0; } - delete []linesData; - linesData = 0; - delete []levels; - levels = 0; + markers.DeleteAll(); + levels.DeleteAll(); } void LineVector::Init() { - for (int line = 0; line < lines; line++) { - delete linesData[line].handleSet; - linesData[line].handleSet = 0; + starts.DeleteAll(); + for (int line = 0; line < markers.Length(); line++) { + delete markers[line]; + markers[line] = 0; } - delete []linesData; - linesData = new LineData[static_cast<int>(growSize)]; - size = growSize; - lines = 1; - delete []levels; - levels = 0; - sizeLevels = 0; -} - -void LineVector::Expand(int sizeNew) { - LineData *linesDataNew = new LineData[sizeNew]; - if (linesDataNew) { - for (int i = 0; i < size; i++) - linesDataNew[i] = linesData[i]; - // Do not delete handleSets here as they are transferred to new linesData - delete []linesData; - linesData = linesDataNew; - size = sizeNew; - } else { - Platform::DebugPrintf("No memory available\n"); - // TODO: Blow up - } - + markers.DeleteAll(); + levels.DeleteAll(); } void LineVector::ExpandLevels(int sizeNew) { - if (sizeNew == -1) - sizeNew = size; - int *levelsNew = new int[sizeNew]; - if (levelsNew) { - int i = 0; - for (; i < sizeLevels; i++) - levelsNew[i] = levels[i]; - for (; i < sizeNew; i++) - levelsNew[i] = SC_FOLDLEVELBASE; - delete []levels; - levels = levelsNew; - sizeLevels = sizeNew; - } else { - Platform::DebugPrintf("No memory available\n"); - // TODO: Blow up - } - + levels.InsertValue(levels.Length(), sizeNew - levels.Length(), SC_FOLDLEVELBASE); } void LineVector::ClearLevels() { - delete []levels; - levels = 0; - sizeLevels = 0; -} - -void LineVector::InsertValue(int pos, int value) { - //Platform::DebugPrintf("InsertValue[%d] = %d\n", pos, value); - if ((lines + 2) >= size) { - if (growSize * 6 < size) - growSize *= 2; - Expand(size + growSize); - if (levels) { - ExpandLevels(size + growSize); - } - } - lines++; - for (int i = lines; i > pos; i--) { - linesData[i] = linesData[i - 1]; - } - linesData[pos].startPosition = value; - linesData[pos].handleSet = 0; - if (levels) { - for (int j = lines; j > pos; j--) { - levels[j] = levels[j - 1]; + levels.DeleteAll(); +} + +int LineVector::SetLevel(int line, int level) { + int prev = 0; + if ((line >= 0) && (line < Lines())) { + if (!levels.Length()) { + ExpandLevels(Lines() + 1); } - if (pos == 0) { - levels[pos] = SC_FOLDLEVELBASE; - } else if (pos == (lines - 1)) { // Last line will not be a folder - levels[pos] = SC_FOLDLEVELBASE; - } else { - levels[pos] = levels[pos - 1] & ~SC_FOLDLEVELWHITEFLAG; + prev = levels[line]; + if (prev != level) { + levels[line] = level; } } + return prev; } -void LineVector::SetValue(int pos, int value) { - //Platform::DebugPrintf("SetValue[%d] = %d\n", pos, value); - if ((pos + 2) >= size) { - //Platform::DebugPrintf("Resize %d %d\n", size,pos); - Expand(pos + growSize); - //Platform::DebugPrintf("end Resize %d %d\n", size,pos); - lines = pos; - if (levels) { - ExpandLevels(pos + growSize); +int LineVector::GetLevel(int line) { + if (levels.Length() && (line >= 0) && (line < Lines())) { + return levels[line]; + } else { + return SC_FOLDLEVELBASE; + } +} + +void LineVector::InsertText(int line, int delta) { + starts.InsertText(line, delta); +} + +void LineVector::InsertLine(int line, int position) { + starts.InsertPartition(line, position); + if (markers.Length()) { + markers.Insert(line, 0); + } + if (levels.Length()) { + int level = SC_FOLDLEVELBASE; + if ((line > 0) && (line < Lines())) { + level = levels[line-1] & ~SC_FOLDLEVELWHITEFLAG; } + levels.InsertValue(line, 1, level); } - linesData[pos].startPosition = value; } -void LineVector::Remove(int pos) { - //Platform::DebugPrintf("Remove %d\n", pos); +void LineVector::SetLineStart(int line, int position) { + starts.SetPartitionStartPosition(line, position); +} + +void LineVector::RemoveLine(int line) { + starts.RemovePartition(line); // Retain the markers from the deleted line by oring them into the previous line - if (pos > 0) { - MergeMarkers(pos - 1); - } - for (int i = pos; i < lines; i++) { - linesData[i] = linesData[i + 1]; + if (markers.Length()) { + if (line > 0) { + MergeMarkers(line - 1); + } + markers.Delete(line); } - if (levels) { + if (levels.Length()) { // Move up following lines but merge header flag from this line // to line before to avoid a temporary disappearence causing expansion. - int firstHeader = levels[pos] & SC_FOLDLEVELHEADERFLAG; - for (int j = pos; j < lines; j++) { - levels[j] = levels[j + 1]; - } - if (pos > 0) - levels[pos-1] |= firstHeader; + int firstHeader = levels[line] & SC_FOLDLEVELHEADERFLAG; + levels.Delete(line); + if (line > 0) + levels[line-1] |= firstHeader; } - lines--; } int LineVector::LineFromPosition(int pos) { - //Platform::DebugPrintf("LineFromPostion %d lines=%d end = %d\n", pos, lines, linesData[lines].startPosition); - if (lines == 0) + return starts.PartitionFromPosition(pos); +} + +int LineVector::MarkValue(int line) { + if (markers.Length() && markers.ValueAt(line)) + return markers.ValueAt(line)->MarkValue(); + else return 0; - //Platform::DebugPrintf("LineFromPosition %d\n", pos); - if (pos >= linesData[lines].startPosition) - return lines - 1; - int lower = 0; - int upper = lines; - do { - int middle = (upper + lower + 1) / 2; // Round high - if (pos < linesData[middle].startPosition) { - upper = middle - 1; - } else { - lower = middle; - } - } while (lower < upper); - //Platform::DebugPrintf("LineFromPostion %d %d %d\n", pos, lower, linesData[lower].startPosition, linesData[lower > 1 ? lower - 1 : 0].startPosition); - return lower; } int LineVector::AddMark(int line, int markerNum) { handleCurrent++; - if (!linesData[line].handleSet) { + if (!markers.Length()) { + // No existing markers so allocate one element per line + markers.InsertValue(0, Lines(), 0); + } + if (!markers[line]) { // Need new structure to hold marker handle - linesData[line].handleSet = new MarkerHandleSet; - if (!linesData[line].handleSet) + markers[line] = new MarkerHandleSet(); + if (!markers[line]) return - 1; } - linesData[line].handleSet->InsertHandle(handleCurrent, markerNum); + markers[line]->InsertHandle(handleCurrent, markerNum); return handleCurrent; } void LineVector::MergeMarkers(int pos) { - if (linesData[pos + 1].handleSet != NULL) { - if (linesData[pos].handleSet == NULL ) - linesData[pos].handleSet = new MarkerHandleSet; - linesData[pos].handleSet->CombineWith(linesData[pos + 1].handleSet); - delete linesData[pos + 1].handleSet; - linesData[pos + 1].handleSet = NULL; + if (markers[pos + 1] != NULL) { + if (markers[pos] == NULL) + markers[pos] = new MarkerHandleSet; + markers[pos]->CombineWith(markers[pos + 1]); + delete markers[pos + 1]; + markers[pos + 1] = NULL; } } void LineVector::DeleteMark(int line, int markerNum, bool all) { - if (linesData[line].handleSet) { + if (markers.Length() && markers[line]) { if (markerNum == -1) { - delete linesData[line].handleSet; - linesData[line].handleSet = 0; + delete markers[line]; + markers[line] = NULL; } else { - bool performedDeletion = - linesData[line].handleSet->RemoveNumber(markerNum); + bool performedDeletion = markers[line]->RemoveNumber(markerNum); while (all && performedDeletion) { - performedDeletion = - linesData[line].handleSet->RemoveNumber(markerNum); + performedDeletion = markers[line]->RemoveNumber(markerNum); } - if (linesData[line].handleSet->Length() == 0) { - delete linesData[line].handleSet; - linesData[line].handleSet = 0; + if (markers[line]->Length() == 0) { + delete markers[line]; + markers[line] = NULL; } } } @@ -333,23 +279,25 @@ void LineVector::DeleteMark(int line, int markerNum, bool all) { void LineVector::DeleteMarkFromHandle(int markerHandle) { int line = LineFromHandle(markerHandle); if (line >= 0) { - linesData[line].handleSet->RemoveHandle(markerHandle); - if (linesData[line].handleSet->Length() == 0) { - delete linesData[line].handleSet; - linesData[line].handleSet = 0; + markers[line]->RemoveHandle(markerHandle); + if (markers[line]->Length() == 0) { + delete markers[line]; + markers[line] = NULL; } } } int LineVector::LineFromHandle(int markerHandle) { - for (int line = 0; line < lines; line++) { - if (linesData[line].handleSet) { - if (linesData[line].handleSet->Contains(markerHandle)) { - return line; + if (markers.Length()) { + for (int line = 0; line < Lines(); line++) { + if (markers[line]) { + if (markers[line]->Contains(markerHandle)) { + return line; + } } } } - return - 1; + return -1; } Action::Action() { @@ -437,7 +385,7 @@ void UndoHistory::EnsureUndoRoom() { int lenActionsNew = lenActions * 2; Action *actionsNew = new Action[lenActionsNew]; if (!actionsNew) - return ; + return; for (int act = 0; act <= currentAction; act++) actionsNew[act].Grab(&actions[act]); delete []actions; @@ -488,7 +436,7 @@ void UndoHistory::AppendAction(actionType at, int position, char *data, int leng currentAction++; } } else { - //Platform::DebugPrintf("action coalesced\n"); + // Action coalesced. } } else { @@ -603,91 +551,12 @@ void UndoHistory::CompletedRedoStep() { currentAction++; } -CellBuffer::CellBuffer(int initialLength) { - body = new char[initialLength]; - size = initialLength; - length = 0; - part1len = 0; - gaplen = initialLength; - part2body = body + gaplen; +CellBuffer::CellBuffer() { readOnly = false; collectingUndo = true; - growSize = 4000; } CellBuffer::~CellBuffer() { - delete []body; - body = 0; -} - -void CellBuffer::GapTo(int position) { - if (position == part1len) - return ; - if (position < part1len) { - int diff = part1len - position; - //Platform::DebugPrintf("Move gap backwards to %d diff = %d part1len=%d length=%d \n", position,diff, part1len, length); - for (int i = 0; i < diff; i++) - body[part1len + gaplen - i - 1] = body[part1len - i - 1]; - } else { // position > part1len - int diff = position - part1len; - //Platform::DebugPrintf("Move gap forwards to %d diff =%d\n", position,diff); - for (int i = 0; i < diff; i++) - body[part1len + i] = body[part1len + gaplen + i]; - } - part1len = position; - part2body = body + gaplen; -} - -void CellBuffer::RoomFor(int insertionLength) { - //Platform::DebugPrintf("need room %d %d\n", gaplen, insertionLength); - if (gaplen <= insertionLength) { - //Platform::DebugPrintf("need room %d %d\n", gaplen, insertionLength); - if (growSize * 6 < size) - growSize *= 2; - int newSize = size + insertionLength + growSize; - Allocate(newSize); - } -} - -// To make it easier to write code that uses ByteAt, a position outside the range of the buffer -// can be retrieved. All characters outside the range have the value '\0'. -char CellBuffer::ByteAt(int position) { - if (position < part1len) { - if (position < 0) { - return '\0'; - } else { - return body[position]; - } - } else { - if (position >= length) { - return '\0'; - } else { - return part2body[position]; - } - } -} - -void CellBuffer::SetByteAt(int position, char ch) { - - if (position < 0) { - //Platform::DebugPrintf("Bad position %d\n",position); - return ; - } - if (position >= length + 11) { - Platform::DebugPrintf("Very Bad position %d of %d\n", position, length); - //exit(2); - return ; - } - if (position >= length) { - //Platform::DebugPrintf("Bad position %d of %d\n",position,length); - return ; - } - - if (position < part1len) { - body[position] = ch; - } else { - part2body[position] = ch; - } } char CellBuffer::CharAt(int position) { @@ -696,20 +565,19 @@ char CellBuffer::CharAt(int position) { void CellBuffer::GetCharRange(char *buffer, int position, int lengthRetrieve) { if (lengthRetrieve < 0) - return ; + return; if (position < 0) - return ; + return; int bytePos = position * 2; - if ((bytePos + lengthRetrieve * 2) > length) { + if ((bytePos + lengthRetrieve * 2) > body.Length()) { Platform::DebugPrintf("Bad GetCharRange %d for %d of %d\n", bytePos, - lengthRetrieve, length); - return ; + lengthRetrieve, body.Length()); + return; } - GapTo(0); // Move the buffer so its easy to subscript into it - char *pb = part2body + bytePos; + int i = bytePos; while (lengthRetrieve--) { - *buffer++ = *pb; - pb += 2; + *buffer++ = body.ValueAt(i); + i += 2; } } @@ -740,7 +608,7 @@ bool CellBuffer::SetStyleAt(int position, char style, char mask) { style &= mask; char curVal = ByteAt(position * 2 + 1); if ((curVal & mask) != style) { - SetByteAt(position*2 + 1, static_cast<char>((curVal & ~mask) | style)); + body.SetValueAt(position*2 + 1, static_cast<char>((curVal & ~mask) | style)); return true; } else { return false; @@ -751,11 +619,11 @@ bool CellBuffer::SetStyleFor(int position, int lengthStyle, char style, char mas int bytePos = position * 2 + 1; bool changed = false; PLATFORM_ASSERT(lengthStyle == 0 || - (lengthStyle > 0 && lengthStyle + position < length)); + (lengthStyle > 0 && lengthStyle + position < body.Length())); while (lengthStyle--) { char curVal = ByteAt(bytePos); if ((curVal & mask) != style) { - SetByteAt(bytePos, static_cast<char>((curVal & ~mask) | style)); + body.SetValueAt(bytePos, static_cast<char>((curVal & ~mask) | style)); changed = true; } bytePos += 2; @@ -783,38 +651,29 @@ const char *CellBuffer::DeleteChars(int position, int deleteLength, bool &startS } int CellBuffer::ByteLength() { - return length; + return body.Length(); } int CellBuffer::Length() { return ByteLength() / 2; } + void CellBuffer::Allocate(int newSize) { - if (newSize > length) { - GapTo(length); - char *newBody = new char[newSize]; - memcpy(newBody, body, length); - delete []body; - body = newBody; - gaplen += newSize - size; - part2body = body + gaplen; - size = newSize; - } + body.ReAllocate(newSize); } int CellBuffer::Lines() { - //Platform::DebugPrintf("Lines = %d\n", lv.lines); - return lv.lines; + return lv.Lines(); } int CellBuffer::LineStart(int line) { if (line < 0) return 0; - else if (line > lv.lines) + else if (line >= Lines()) return Length(); else - return lv.linesData[line].startPosition; + return lv.LineStart(line); } bool CellBuffer::IsReadOnly() { @@ -834,14 +693,14 @@ bool CellBuffer::IsSavePoint() { } int CellBuffer::AddMark(int line, int markerNum) { - if ((line >= 0) && (line < lv.lines)) { + if ((line >= 0) && (line < Lines())) { return lv.AddMark(line, markerNum); } return - 1; } void CellBuffer::DeleteMark(int line, int markerNum) { - if ((line >= 0) && (line < lv.lines)) { + if ((line >= 0) && (line < Lines())) { lv.DeleteMark(line, markerNum, false); } } @@ -851,13 +710,13 @@ void CellBuffer::DeleteMarkFromHandle(int markerHandle) { } int CellBuffer::GetMark(int line) { - if ((line >= 0) && (line < lv.lines) && (lv.linesData[line].handleSet)) - return lv.linesData[line].handleSet->MarkValue(); + if ((line >= 0) && (line < Lines())) + return lv.MarkValue(line); return 0; } void CellBuffer::DeleteAllMarks(int markerNum) { - for (int line = 0; line < lv.lines; line++) { + for (int line = 0; line < Lines(); line++) { lv.DeleteMark(line, markerNum, true); } } @@ -869,51 +728,38 @@ int CellBuffer::LineFromHandle(int markerHandle) { // Without undo void CellBuffer::BasicInsertString(int position, char *s, int insertLength) { - //Platform::DebugPrintf("Inserting at %d for %d\n", position, insertLength); if (insertLength == 0) - return ; + return; PLATFORM_ASSERT(insertLength > 0); - RoomFor(insertLength); - GapTo(position); - memcpy(body + part1len, s, insertLength); - length += insertLength; - part1len += insertLength; - gaplen -= insertLength; - part2body = body + gaplen; + body.InsertFromArray(position, s, 0, insertLength); int lineInsert = lv.LineFromPosition(position / 2) + 1; // Point all the lines after the insertion point further along in the buffer - for (int lineAfter = lineInsert; lineAfter <= lv.lines; lineAfter++) { - lv.linesData[lineAfter].startPosition += insertLength / 2; - } + lv.InsertText(lineInsert-1, insertLength / 2); char chPrev = ' '; if ((position - 2) >= 0) chPrev = ByteAt(position - 2); char chAfter = ' '; - if ((position + insertLength) < length) + if ((position + insertLength) < body.Length()) chAfter = ByteAt(position + insertLength); if (chPrev == '\r' && chAfter == '\n') { - //Platform::DebugPrintf("Splitting a crlf pair at %d\n", lineInsert); // Splitting up a crlf pair at position - lv.InsertValue(lineInsert, position / 2); + lv.InsertLine(lineInsert, position / 2); lineInsert++; } char ch = ' '; for (int i = 0; i < insertLength; i += 2) { ch = s[i]; if (ch == '\r') { - //Platform::DebugPrintf("Inserting cr at %d\n", lineInsert); - lv.InsertValue(lineInsert, (position + i) / 2 + 1); + lv.InsertLine(lineInsert, (position + i) / 2 + 1); lineInsert++; } else if (ch == '\n') { if (chPrev == '\r') { - //Platform::DebugPrintf("Patching cr before lf at %d\n", lineInsert-1); // Patch up what was end of line - lv.SetValue(lineInsert - 1, (position + i) / 2 + 1); + lv.SetLineStart(lineInsert - 1, (position + i) / 2 + 1); } else { - //Platform::DebugPrintf("Inserting lf at %d\n", lineInsert); - lv.InsertValue(lineInsert, (position + i) / 2 + 1); + lv.InsertLine(lineInsert, (position + i) / 2 + 1); lineInsert++; } } @@ -922,44 +768,37 @@ void CellBuffer::BasicInsertString(int position, char *s, int insertLength) { // Joining two lines where last insertion is cr and following text starts with lf if (chAfter == '\n') { if (ch == '\r') { - //Platform::DebugPrintf("Joining cr before lf at %d\n", lineInsert-1); // End of line already in buffer so drop the newly created one - lv.Remove(lineInsert - 1); + lv.RemoveLine(lineInsert - 1); } } } void CellBuffer::BasicDeleteChars(int position, int deleteLength) { - //Platform::DebugPrintf("Deleting at %d for %d\n", position, deleteLength); if (deleteLength == 0) - return ; + return; - if ((position == 0) && (deleteLength == length)) { + if ((position == 0) && (deleteLength == body.Length())) { // If whole buffer is being deleted, faster to reinitialise lines data // than to delete each line. - //printf("Whole buffer being deleted\n"); lv.Init(); } else { // Have to fix up line positions before doing deletion as looking at text in buffer // to work out which lines have been removed int lineRemove = lv.LineFromPosition(position / 2) + 1; - // Point all the lines after the insertion point further along in the buffer - for (int lineAfter = lineRemove; lineAfter <= lv.lines; lineAfter++) { - lv.linesData[lineAfter].startPosition -= deleteLength / 2; - } + lv.InsertText(lineRemove-1, - (deleteLength / 2)); char chPrev = ' '; if (position >= 2) chPrev = ByteAt(position - 2); char chBefore = chPrev; char chNext = ' '; - if (position < length) + if (position < body.Length()) chNext = ByteAt(position); bool ignoreNL = false; if (chPrev == '\r' && chNext == '\n') { - //Platform::DebugPrintf("Deleting lf after cr, move line end to cr at %d\n", lineRemove); // Move back one - lv.SetValue(lineRemove, position / 2); + lv.SetLineStart(lineRemove, position / 2); lineRemove++; ignoreNL = true; // First \n is not real deletion } @@ -967,20 +806,17 @@ void CellBuffer::BasicDeleteChars(int position, int deleteLength) { char ch = chNext; for (int i = 0; i < deleteLength; i += 2) { chNext = ' '; - if ((position + i + 2) < length) + if ((position + i + 2) < body.Length()) chNext = ByteAt(position + i + 2); - //Platform::DebugPrintf("Deleting %d %x\n", i, ch); if (ch == '\r') { if (chNext != '\n') { - //Platform::DebugPrintf("Removing cr end of line\n"); - lv.Remove(lineRemove); + lv.RemoveLine(lineRemove); } } else if (ch == '\n') { if (ignoreNL) { ignoreNL = false; // Further \n are real deletions } else { - //Platform::DebugPrintf("Removing lf end of line\n"); - lv.Remove(lineRemove); + lv.RemoveLine(lineRemove); } } @@ -989,19 +825,15 @@ void CellBuffer::BasicDeleteChars(int position, int deleteLength) { // May have to fix up end if last deletion causes cr to be next to lf // or removes one of a crlf pair char chAfter = ' '; - if ((position + deleteLength) < length) + if ((position + deleteLength) < body.Length()) chAfter = ByteAt(position + deleteLength); if (chBefore == '\r' && chAfter == '\n') { - //d.printf("Joining cr before lf at %d\n", lineRemove); // Using lineRemove-1 as cr ended line before start of deletion - lv.Remove(lineRemove - 1); - lv.SetValue(lineRemove - 1, position / 2 + 1); + lv.RemoveLine(lineRemove - 1); + lv.SetLineStart(lineRemove - 1, position / 2 + 1); } } - GapTo(position); - length -= deleteLength; - gaplen += deleteLength; - part2body = body + gaplen; + body.DeleteRange(position, deleteLength); } bool CellBuffer::SetUndoCollection(bool collectUndo) { @@ -1097,25 +929,11 @@ int CellBuffer::GetMaxLineState() { } int CellBuffer::SetLevel(int line, int level) { - int prev = 0; - if ((line >= 0) && (line < lv.lines)) { - if (!lv.levels) { - lv.ExpandLevels(); - } - prev = lv.levels[line]; - if (lv.levels[line] != level) { - lv.levels[line] = level; - } - } - return prev; + return lv.SetLevel(line, level); } int CellBuffer::GetLevel(int line) { - if (lv.levels && (line >= 0) && (line < lv.lines)) { - return lv.levels[line]; - } else { - return SC_FOLDLEVELBASE; - } + return lv.GetLevel(line); } void CellBuffer::ClearLevels() { diff --git a/src/CellBuffer.h b/src/CellBuffer.h index 92bbfa0da..7faeecd23 100644 --- a/src/CellBuffer.h +++ b/src/CellBuffer.h @@ -27,10 +27,10 @@ class MarkerHandleSet { public: MarkerHandleSet(); ~MarkerHandleSet(); - int Length(); - int NumberFromHandle(int handle); - int MarkValue(); ///< Bit set of marker numbers. - bool Contains(int handle); + int Length() const; + int NumberFromHandle(int handle) const; + int MarkValue() const; ///< Bit set of marker numbers. + bool Contains(int handle) const; bool InsertHandle(int handle, int markerNum); void RemoveHandle(int handle); bool RemoveNumber(int markerNum); @@ -38,43 +38,40 @@ public: }; /** - * Each line stores the starting position of the first character of the line in the cell buffer - * and potentially a marker handle set. Often a line will not have any attached markers. - */ -struct LineData { - int startPosition; - MarkerHandleSet *handleSet; - LineData() : startPosition(0), handleSet(0) { - } -}; - -/** * The line vector contains information about each of the lines in a cell buffer. */ class LineVector { -public: - int growSize; - int lines; - LineData *linesData; - int size; - int *levels; - int sizeLevels; + Partitioning starts; + SplitVector<MarkerHandleSet *> markers; + SplitVector<int> levels; /// Handles are allocated sequentially and should never have to be reused as 32 bit ints are very big. int handleCurrent; +public: + LineVector(); ~LineVector(); void Init(); - void Expand(int sizeNew); void ExpandLevels(int sizeNew=-1); void ClearLevels(); - void InsertValue(int pos, int value); - void SetValue(int pos, int value); - void Remove(int pos); + int SetLevel(int line, int level); + int GetLevel(int line); + + void InsertText(int line, int delta); + void InsertLine(int line, int position); + void SetLineStart(int line, int position); + void RemoveLine(int line); + int Lines() { + return starts.Partitions(); + } int LineFromPosition(int pos); + int LineStart(int line) { + return starts.PositionFromPartition(line); + } + int MarkValue(int line); int AddMark(int line, int marker); void MergeMarkers(int pos); void DeleteMark(int line, int markerNum, bool all); @@ -150,16 +147,8 @@ public: */ class CellBuffer { private: - char *body; ///< The cell buffer itself. - int size; ///< Allocated size of the buffer. - int length; ///< Total length of the data. - int part1len; ///< Length of the first part. - int gaplen; ///< Length of the gap between the two parts. - char *part2body; ///< The second part of the cell buffer. - ///< Doesn't point after the gap but set so that - ///< part2body[position] is consistent with body[position]. + SplitVector<char> body; bool readOnly; - int growSize; bool collectingUndo; UndoHistory uh; @@ -168,15 +157,13 @@ private: SVector lineStates; - void GapTo(int position); - void RoomFor(int insertionLength); - - inline char ByteAt(int position); - void SetByteAt(int position, char ch); + char ByteAt(int position) { + return body.ValueAt(position); + } public: - CellBuffer(int initialLength = 4000); + CellBuffer(); ~CellBuffer(); /// Retrieving positions outside the range of the buffer works and returns 0 diff --git a/src/Document.cxx b/src/Document.cxx index e530b0423..2e21cfe7b 100644 --- a/src/Document.cxx +++ b/src/Document.cxx @@ -14,6 +14,8 @@ #include "Scintilla.h" #include "SVector.h" +#include "SplitVector.h" +#include "Partitioning.h" #include "CellBuffer.h" #include "CharClassify.h" #include "Document.h" @@ -858,7 +860,7 @@ int Document::ExtendWordSelect(int pos, int delta, bool onlyWordCharacters) { while (pos > 0 && (WordCharClass(cb.CharAt(pos - 1)) == ccStart)) pos--; } else { - if (!onlyWordCharacters) + if (!onlyWordCharacters && pos < Length()) ccStart = WordCharClass(cb.CharAt(pos)); while (pos < (Length()) && (WordCharClass(cb.CharAt(pos)) == ccStart)) pos++; diff --git a/src/DocumentAccessor.cxx b/src/DocumentAccessor.cxx index c695c5f51..f3c36db32 100644 --- a/src/DocumentAccessor.cxx +++ b/src/DocumentAccessor.cxx @@ -16,6 +16,8 @@ #include "SVector.h" #include "Accessor.h" #include "DocumentAccessor.h" +#include "SplitVector.h" +#include "Partitioning.h" #include "CellBuffer.h" #include "Scintilla.h" #include "CharClassify.h" diff --git a/src/Editor.cxx b/src/Editor.cxx index b2d29da13..b0bd99e83 100644 --- a/src/Editor.cxx +++ b/src/Editor.cxx @@ -19,6 +19,8 @@ #include "ContractionState.h" #include "SVector.h" +#include "SplitVector.h" +#include "Partitioning.h" #include "CellBuffer.h" #include "KeyMap.h" #include "Indicator.h" @@ -1915,6 +1917,7 @@ static bool IsSpaceOrTab(char ch) { LineLayout *Editor::RetrieveLineLayout(int lineNumber) { int posLineStart = pdoc->LineStart(lineNumber); int posLineEnd = pdoc->LineStart(lineNumber + 1); + PLATFORM_ASSERT(posLineEnd >= posLineStart); int lineCaret = pdoc->LineFromPosition(currentPos); return llc.Retrieve(lineNumber, lineCaret, posLineEnd - posLineStart, pdoc->GetStyleClock(), @@ -1930,6 +1933,7 @@ void Editor::LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayou if (!ll) return; PLATFORM_ASSERT(line < pdoc->LinesTotal()); + PLATFORM_ASSERT(ll->chars != NULL); int posLineStart = pdoc->LineStart(line); int posLineEnd = pdoc->LineStart(line + 1); // If the line is very long, limit the treatment to a length that should fit in the viewport diff --git a/src/Partitioning.h b/src/Partitioning.h new file mode 100644 index 000000000..e30189d75 --- /dev/null +++ b/src/Partitioning.h @@ -0,0 +1,184 @@ +// Scintilla source code edit control +/** @file Partitioning.h + ** Data structure used to partition an interval. Used for holding line start/end positions. + **/ +// Copyright 1998-2007 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef PARTITIONING_H +#define PARTITIONING_H + +/// A split vector of integers with a method for adding a value to all elements +/// in a range. +/// Used by the Partitioning class. + +class SplitVectorWithRangeAdd : public SplitVector<int> { +public: + SplitVectorWithRangeAdd(int growSize_) { + SetGrowSize(growSize_); + ReAllocate(growSize_); + } + ~SplitVectorWithRangeAdd() { + } + void RangeAddDelta(int start, int end, int delta) { + // end is 1 past end, so end-start is number of elements to change + int i = 0; + int rangeLength = end - start; + int range1Length = rangeLength; + int part1Left = part1Length - start; + if (range1Length > part1Left) + range1Length = part1Left; + while (i < range1Length) { + body[start++] += delta; + i++; + } + start += gapLength; + while (i < rangeLength) { + body[start++] += delta; + i++; + } + } +}; + +/// Divide an interval into multiple partitions. +/// Useful for breaking a document down into sections such as lines. + +class Partitioning { +private: + // To avoid calculating all the partition positions whenever any text is inserted + // there may be a step somewhere in the list. + int stepPartition; + int stepLength; + SplitVectorWithRangeAdd *body; + + // Move step forward + void ApplyStep(int partitionUpTo) { + if (stepLength != 0) { + body->RangeAddDelta(stepPartition+1, partitionUpTo + 1, stepLength); + } + stepPartition = partitionUpTo; + if (stepPartition >= body->Length()-1) { + stepPartition = body->Length()-1; + stepLength = 0; + } + } + + // Move step backward + void BackStep(int partitionDownTo) { + if (stepLength != 0) { + body->RangeAddDelta(partitionDownTo+1, stepPartition+1, -stepLength); + } + stepPartition = partitionDownTo; + } + + void Allocate(int growSize) { + body = new SplitVectorWithRangeAdd(growSize); + stepPartition = 0; + stepLength = 0; + body->Insert(0, 0); // This value stays 0 for ever + body->Insert(1, 0); // This is the end of the first partition and will be the start of the second + } + +public: + Partitioning(int growSize) { + Allocate(growSize); + } + + ~Partitioning() { + delete body; + body = NULL; + } + + int Partitions() { + return body->Length()-1; + } + + void InsertPartition(int partition, int pos) { + if (stepPartition < partition) { + ApplyStep(partition); + } + body->Insert(partition, pos); + stepPartition++; + } + + void SetPartitionStartPosition(int partition, int pos) { + ApplyStep(partition+1); + if ((partition < 0) || (partition > body->Length())) { + return; + } + body->SetValueAt(partition, pos); + } + + void InsertText(int partitionInsert, int delta) { + // Point all the partitions after the insertion point further along in the buffer + if (stepLength != 0) { + if (partitionInsert >= stepPartition) { + // Fill in up to the new insertion point + ApplyStep(partitionInsert); + stepLength += delta; + } else if (partitionInsert >= (stepPartition - body->Length() / 10)) { + // Close to step but before so move step back + BackStep(partitionInsert); + stepLength += delta; + } else { + ApplyStep(body->Length()-1); + stepPartition = partitionInsert; + stepLength = delta; + } + } else { + stepPartition = partitionInsert; + stepLength = delta; + } + } + + void RemovePartition(int partition) { + if (partition > stepPartition) { + ApplyStep(partition); + stepPartition--; + } else { + stepPartition--; + } + body->Delete(partition); + } + + int PositionFromPartition(int partition) { + PLATFORM_ASSERT(partition >= 0); + PLATFORM_ASSERT(partition < body->Length()); + if ((partition < 0) || (partition >= body->Length())) { + return 0; + } + int pos = body->ValueAt(partition); + if (partition > stepPartition) + pos += stepLength; + return pos; + } + + int PartitionFromPosition(int pos) { + if (body->Length() <= 1) + return 0; + if (pos >= (PositionFromPartition(body->Length()-1))) + return body->Length() - 1 - 1; + int lower = 0; + int upper = body->Length()-1; + do { + int middle = (upper + lower + 1) / 2; // Round high + int posMiddle = body->ValueAt(middle); + if (middle > stepPartition) + posMiddle += stepLength; + if (pos < posMiddle) { + upper = middle - 1; + } else { + lower = middle; + } + } while (lower < upper); + return lower; + } + + void DeleteAll() { + int growSize = body->GetGrowSize(); + delete body; + Allocate(growSize); + } +}; + +#endif diff --git a/src/ScintillaBase.cxx b/src/ScintillaBase.cxx index bcb0a77e2..e43411261 100644 --- a/src/ScintillaBase.cxx +++ b/src/ScintillaBase.cxx @@ -22,6 +22,8 @@ #endif #include "ContractionState.h" #include "SVector.h" +#include "SplitVector.h" +#include "Partitioning.h" #include "CellBuffer.h" #include "CallTip.h" #include "KeyMap.h" diff --git a/src/SplitVector.h b/src/SplitVector.h new file mode 100644 index 000000000..2847108fa --- /dev/null +++ b/src/SplitVector.h @@ -0,0 +1,221 @@ +// Scintilla source code edit control +/** @file SplitVector.h + ** Main data structure for holding arrays that handle insertions + ** and deletions efficiently. + **/ +// Copyright 1998-2007 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef SPLITVECTOR_H +#define SPLITVECTOR_H + +template <typename T> +class SplitVector { +protected: + T *body; + int size; + int lengthBody; + int part1Length; + int gapLength; /// invariant: gapLength == size - lengthBody + int growSize; + + /// Move the gap to a particular position so that insertion and + /// deletion at that point will not require much copying and + /// hence be fast. + void GapTo(int position) { + if (position != part1Length) { + if (position < part1Length) { + memmove( + body + position + gapLength, + body + position, + sizeof(T) * (part1Length - position)); + } else { // position > part1Length + memmove( + body + part1Length, + body + part1Length + gapLength, + sizeof(T) * (position - part1Length)); + } + part1Length = position; + } + } + + /// Check that there is room in the buffer for an insertion, + /// reallocating if more space needed. + void RoomFor(int insertionLength) { + if (gapLength <= insertionLength) { + if (growSize * 6 < size) + growSize *= 2; + ReAllocate(size + insertionLength + growSize); + } + } + +public: + /// Construct a split buffer. + SplitVector() { + body = NULL; + growSize = 8; + size = 0; + lengthBody = 0; + part1Length = 0; + gapLength = 0; + } + + ~SplitVector() { + delete []body; + body = NULL; + } + + void Create(int initialLength_, int growSize_); + + int GetGrowSize() const { + return growSize; + } + + void SetGrowSize(int growSize_) { + growSize = growSize_; + } + + /// Reallocate the storage for the buffer to be newSize and + /// copy exisiting contents to the new buffer. + /// Must not be used to decrease the size of the buffer. + void ReAllocate(int newSize) { + if (newSize > size) { + // Move the gap to the end + GapTo(lengthBody); + T *newBody = new T[newSize]; + if ((size != 0) && (body != NULL)) { + memmove(newBody, body, sizeof(T) * lengthBody); + delete []body; + } + body = newBody; + gapLength += newSize - size; + size = newSize; + } + } + + /// Retrieve the character at a particular position. + /// Retrieving positions outside the range of the buffer returns 0. + T ValueAt(int position) const { + if (position < part1Length) { + //PLATFORM_ASSERT(position >= 0); + if (position < 0) { + return 0; + } else { + return body[position]; + } + } else { + //PLATFORM_ASSERT(position < lengthBody); + if (position >= lengthBody) { + return 0; + } else { + return body[gapLength + position]; + } + } + } + + void SetValueAt(int position, T v) { + if (position < part1Length) { + PLATFORM_ASSERT(position >= 0); + if (position < 0) { + ; + } else { + body[position] = v; + } + } else { + PLATFORM_ASSERT(position < lengthBody); + if (position >= lengthBody) { + ; + } else { + body[gapLength + position] = v; + } + } + } + + T& operator[](int position) const { + PLATFORM_ASSERT(position >= 0 && position < lengthBody); + if (position < part1Length) { + return body[position]; + } else { + return body[gapLength + position]; + } + } + + /// Retrieve the length of the buffer. + int Length() const { + return lengthBody; + } + + /// Insert a single value into the buffer. + /// Inserting at positions outside the current range fails. + void Insert(int position, T v) { + PLATFORM_ASSERT((position >= 0) && (position <= lengthBody)); + if ((position < 0) || (position > lengthBody)) { + return; + } + RoomFor(1); + GapTo(position); + body[part1Length] = v; + lengthBody++; + part1Length++; + gapLength--; + } + + /// Insert a number of elements into the buffer setting their value. + /// Inserting at positions outside the current range fails. + void InsertValue(int position, int insertLength, T v) { + if (insertLength > 0) { + RoomFor(insertLength); + GapTo(position); + for (int i = 0; i < insertLength; i++) + body[part1Length + i] = v; + lengthBody += insertLength; + part1Length += insertLength; + gapLength -= insertLength; + } + } + + /// Insert text into the buffer from an array. + void InsertFromArray(int positionToInsert, T s[], int positionFrom, int insertLength) { + if (insertLength > 0) { + if ((positionToInsert < 0) || (positionToInsert > lengthBody)) { + return; + } + RoomFor(insertLength); + GapTo(positionToInsert); + memmove(body + part1Length, s + positionFrom, sizeof(T) * insertLength); + lengthBody += insertLength; + part1Length += insertLength; + gapLength -= insertLength; + } + } + + /// Delete one element from the buffer. + void Delete(int position) { + if ((position < 0) || (position >= lengthBody)) { + return; + } + DeleteRange(position, 1); + } + + /// Delete a range from the buffer. + /// Deleting positions outside the current range fails. + void DeleteRange(int position, int deleteLength) { + PLATFORM_ASSERT((position >= 0) && (position + deleteLength <= lengthBody)); + if ((position < 0) || ((position + deleteLength) > lengthBody)) { + return; + } + if (deleteLength > 0) { + GapTo(position); + lengthBody -= deleteLength; + gapLength += deleteLength; + } + } + + /// Delete all the buffer contents. + void DeleteAll() { + DeleteRange(0, lengthBody); + } + +}; + +#endif diff --git a/win32/ScintillaWin.cxx b/win32/ScintillaWin.cxx index bc7ff17b3..249e74646 100644 --- a/win32/ScintillaWin.cxx +++ b/win32/ScintillaWin.cxx @@ -30,6 +30,8 @@ #endif #include "ContractionState.h" #include "SVector.h" +#include "SplitVector.h" +#include "Partitioning.h" #include "CellBuffer.h" #include "CallTip.h" #include "KeyMap.h" |