diff options
author | nyamatongwe <unknown> | 2007-06-02 05:18:13 +0000 |
---|---|---|
committer | nyamatongwe <unknown> | 2007-06-02 05:18:13 +0000 |
commit | 284a7cde23d26ccc5c5b75aaa9c1e69e659a4adc (patch) | |
tree | 22d32a8aeccfda9902240bfca1ffad692136f45c /src | |
parent | b78f4e0f1eeb340c23b3c9df896da7405c2e0217 (diff) | |
download | scintilla-mirror-284a7cde23d26ccc5c5b75aaa9c1e69e659a4adc.tar.gz |
Addition of PositionCache module which adds cacing of string
to position information and segments long pieces of text so they
can be handled more efficiently.
Diffstat (limited to 'src')
-rw-r--r-- | src/Editor.cxx | 687 | ||||
-rw-r--r-- | src/Editor.h | 87 | ||||
-rw-r--r-- | src/PositionCache.cxx | 586 | ||||
-rw-r--r-- | src/PositionCache.h | 154 | ||||
-rw-r--r-- | src/ScintillaBase.cxx | 1 |
5 files changed, 962 insertions, 553 deletions
diff --git a/src/Editor.cxx b/src/Editor.cxx index 46684a7fc..6af15a297 100644 --- a/src/Editor.cxx +++ b/src/Editor.cxx @@ -32,6 +32,7 @@ #include "CharClassify.h" #include "Decoration.h" #include "Document.h" +#include "PositionCache.h" #include "Editor.h" #ifdef SCI_NAMESPACE @@ -78,266 +79,9 @@ ticking(false), ticksToWait(0), tickerID(0) {} Idler::Idler() : state(false), idlerID(0) {} -LineLayout::LineLayout(int maxLineLength_) : - lineStarts(0), - lenLineStarts(0), - lineNumber(-1), - inCache(false), - maxLineLength(-1), - numCharsInLine(0), - validity(llInvalid), - xHighlightGuide(0), - highlightColumn(0), - selStart(0), - selEnd(0), - containsCaret(false), - edgeColumn(0), - chars(0), - styles(0), - styleBitsSet(0), - indicators(0), - positions(0), - hsStart(0), - hsEnd(0), - widthLine(wrapWidthInfinite), - lines(1) { - Resize(maxLineLength_); -} - -LineLayout::~LineLayout() { - Free(); -} - -void LineLayout::Resize(int maxLineLength_) { - if (maxLineLength_ > maxLineLength) { - Free(); - chars = new char[maxLineLength_ + 1]; - styles = new unsigned char[maxLineLength_ + 1]; - indicators = new char[maxLineLength_ + 1]; - // Extra position allocated as sometimes the Windows - // GetTextExtentExPoint API writes an extra element. - positions = new int[maxLineLength_ + 1 + 1]; - maxLineLength = maxLineLength_; - } -} - -void LineLayout::Free() { - delete []chars; - chars = 0; - delete []styles; - styles = 0; - delete []indicators; - indicators = 0; - delete []positions; - positions = 0; - delete []lineStarts; - lineStarts = 0; -} - -void LineLayout::Invalidate(validLevel validity_) { - if (validity > validity_) - validity = validity_; -} - -void LineLayout::SetLineStart(int line, int start) { - if ((line >= lenLineStarts) && (line != 0)) { - int newMaxLines = line + 20; - int *newLineStarts = new int[newMaxLines]; - if (!newLineStarts) - return; - for (int i = 0; i < newMaxLines; i++) { - if (i < lenLineStarts) - newLineStarts[i] = lineStarts[i]; - else - newLineStarts[i] = 0; - } - delete []lineStarts; - lineStarts = newLineStarts; - lenLineStarts = newMaxLines; - } - lineStarts[line] = start; -} - -void LineLayout::SetBracesHighlight(Range rangeLine, Position braces[], - char bracesMatchStyle, int xHighlight) { - if (rangeLine.ContainsCharacter(braces[0])) { - int braceOffset = braces[0] - rangeLine.start; - if (braceOffset < numCharsInLine) { - bracePreviousStyles[0] = styles[braceOffset]; - styles[braceOffset] = bracesMatchStyle; - } - } - if (rangeLine.ContainsCharacter(braces[1])) { - int braceOffset = braces[1] - rangeLine.start; - if (braceOffset < numCharsInLine) { - bracePreviousStyles[1] = styles[braceOffset]; - styles[braceOffset] = bracesMatchStyle; - } - } - if ((braces[0] >= rangeLine.start && braces[1] <= rangeLine.end) || - (braces[1] >= rangeLine.start && braces[0] <= rangeLine.end)) { - xHighlightGuide = xHighlight; - } -} - -void LineLayout::RestoreBracesHighlight(Range rangeLine, Position braces[]) { - if (rangeLine.ContainsCharacter(braces[0])) { - int braceOffset = braces[0] - rangeLine.start; - if (braceOffset < numCharsInLine) { - styles[braceOffset] = bracePreviousStyles[0]; - } - } - if (rangeLine.ContainsCharacter(braces[1])) { - int braceOffset = braces[1] - rangeLine.start; - if (braceOffset < numCharsInLine) { - styles[braceOffset] = bracePreviousStyles[1]; - } - } - xHighlightGuide = 0; -} - -LineLayoutCache::LineLayoutCache() : - level(0), length(0), size(0), cache(0), - allInvalidated(false), styleClock(-1), useCount(0) { - Allocate(0); -} - -LineLayoutCache::~LineLayoutCache() { - Deallocate(); -} - -void LineLayoutCache::Allocate(int length_) { - PLATFORM_ASSERT(cache == NULL); - allInvalidated = false; - length = length_; - size = length; - if (size > 1) { - size = (size / 16 + 1) * 16; - } - if (size > 0) { - cache = new LineLayout * [size]; - } - for (int i = 0; i < size; i++) - cache[i] = 0; -} - -void LineLayoutCache::AllocateForLevel(int linesOnScreen, int linesInDoc) { - PLATFORM_ASSERT(useCount == 0); - int lengthForLevel = 0; - if (level == llcCaret) { - lengthForLevel = 1; - } else if (level == llcPage) { - lengthForLevel = linesOnScreen + 1; - } else if (level == llcDocument) { - lengthForLevel = linesInDoc; - } - if (lengthForLevel > size) { - Deallocate(); - Allocate(lengthForLevel); - } else { - if (lengthForLevel < length) { - for (int i = lengthForLevel; i < length; i++) { - delete cache[i]; - cache[i] = 0; - } - } - length = lengthForLevel; - } - PLATFORM_ASSERT(length == lengthForLevel); - PLATFORM_ASSERT(cache != NULL || length == 0); -} - -void LineLayoutCache::Deallocate() { - PLATFORM_ASSERT(useCount == 0); - for (int i = 0; i < length; i++) - delete cache[i]; - delete []cache; - cache = 0; - length = 0; - size = 0; -} - -void LineLayoutCache::Invalidate(LineLayout::validLevel validity_) { - if (cache && !allInvalidated) { - for (int i = 0; i < length; i++) { - if (cache[i]) { - cache[i]->Invalidate(validity_); - } - } - if (validity_ == LineLayout::llInvalid) { - allInvalidated = true; - } - } -} - -void LineLayoutCache::SetLevel(int level_) { - allInvalidated = false; - if ((level_ != -1) && (level != level_)) { - level = level_; - Deallocate(); - } -} - -LineLayout *LineLayoutCache::Retrieve(int lineNumber, int lineCaret, int maxChars, int styleClock_, - int linesOnScreen, int linesInDoc) { - AllocateForLevel(linesOnScreen, linesInDoc); - if (styleClock != styleClock_) { - Invalidate(LineLayout::llCheckTextAndStyle); - styleClock = styleClock_; - } - allInvalidated = false; - int pos = -1; - LineLayout *ret = 0; - if (level == llcCaret) { - pos = 0; - } else if (level == llcPage) { - if (lineNumber == lineCaret) { - pos = 0; - } else if (length > 1) { - pos = 1 + (lineNumber % (length - 1)); - } - } else if (level == llcDocument) { - pos = lineNumber; - } - if (pos >= 0) { - PLATFORM_ASSERT(useCount == 0); - if (cache && (pos < length)) { - if (cache[pos]) { - if ((cache[pos]->lineNumber != lineNumber) || - (cache[pos]->maxLineLength < maxChars)) { - delete cache[pos]; - cache[pos] = 0; - } - } - if (!cache[pos]) { - cache[pos] = new LineLayout(maxChars); - } - if (cache[pos]) { - cache[pos]->lineNumber = lineNumber; - cache[pos]->inCache = true; - ret = cache[pos]; - useCount++; - } - } - } - - if (!ret) { - ret = new LineLayout(maxChars); - ret->lineNumber = lineNumber; - } - - return ret; -} - -void LineLayoutCache::Dispose(LineLayout *ll) { - allInvalidated = false; - if (ll) { - if (!ll->inCache) { - delete ll; - } else { - useCount--; - } - } +static inline bool IsControlCharacter(int ch) { + // iscntrl returns true for lots of chars > 127 which are displayable + return ch >= 0 && ch < ' '; } Editor::Editor() { @@ -450,6 +194,7 @@ Editor::Editor() { hsEnd = -1; llc.SetLevel(LineLayoutCache::llcCaret); + posCache.SetSize(0x400); } Editor::~Editor() { @@ -482,6 +227,7 @@ void Editor::InvalidateStyleData() { palette.Release(); DropGraphics(); llc.Invalidate(LineLayout::llInvalid); + posCache.Clear(); if (selType == selRectangle) { xStartSelect = XFromPosition(anchor); xEndSelect = XFromPosition(currentPos); @@ -554,11 +300,6 @@ int Editor::MaxScrollPos() { } } -static inline bool IsControlCharacter(int ch) { - // iscntrl returns true for lots of chars > 127 which are displayable - return ch >= 0 && ch < ' '; -} - const char *ControlCharacterString(unsigned char ch) { const char *reps[] = { "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL", @@ -738,10 +479,6 @@ void Editor::SetTopLine(int topLineNew) { posTopLine = pdoc->LineStart(cs.DocFromDisplay(topLine)); } -static inline bool IsEOLChar(char ch) { - return (ch == '\r') || (ch == '\n'); -} - int Editor::PositionFromLocation(Point pt) { RefreshStyleData(); pt.x = pt.x - vs.fixedColumnWidth + xOffset; @@ -764,18 +501,19 @@ int Editor::PositionFromLocation(Point pt) { int subLine = visibleLine - lineStartSet; if (subLine < ll->lines) { int lineStart = ll->LineStart(subLine); - int lineEnd = ll->LineStart(subLine + 1); + int lineEnd = ll->LineLastVisible(subLine); int subLineStart = ll->positions[lineStart]; if (actualWrapVisualStartIndent != 0) { if (lineStart != 0) // Wrapped pt.x -= actualWrapVisualStartIndent * vs.aveCharWidth; } - for (int i = lineStart; i < lineEnd; i++) { - if (pt.x < (((ll->positions[i] + ll->positions[i + 1]) / 2) - subLineStart) || - IsEOLChar(ll->chars[i])) { + int i = ll->FindBefore(pt.x + subLineStart, lineStart, lineEnd); + while (i < lineEnd) { + if ((pt.x + subLineStart) < ((ll->positions[i] + ll->positions[i + 1]) / 2)) { return pdoc->MovePositionOutsideChar(i + posLineStart, 1); } + i++; } return lineEnd + posLineStart; } @@ -813,18 +551,19 @@ int Editor::PositionFromLocationClose(Point pt) { int subLine = visibleLine - lineStartSet; if (subLine < ll->lines) { int lineStart = ll->LineStart(subLine); - int lineEnd = ll->LineStart(subLine + 1); + int lineEnd = ll->LineLastVisible(subLine); int subLineStart = ll->positions[lineStart]; if (actualWrapVisualStartIndent != 0) { if (lineStart != 0) // Wrapped pt.x -= actualWrapVisualStartIndent * vs.aveCharWidth; } - for (int i = lineStart; i < lineEnd; i++) { - if (pt.x < (((ll->positions[i] + ll->positions[i + 1]) / 2) - subLineStart) || - IsEOLChar(ll->chars[i])) { + int i = ll->FindBefore(pt.x + subLineStart, lineStart, lineEnd); + while (i < lineEnd) { + if ((pt.x + subLineStart) < ((ll->positions[i] + ll->positions[i + 1]) / 2)) { return pdoc->MovePositionOutsideChar(i + posLineStart, 1); } + i++; } if (pt.x < (ll->positions[lineEnd] - subLineStart)) { return pdoc->MovePositionOutsideChar(lineEnd + posLineStart, 1); @@ -853,19 +592,20 @@ int Editor::PositionFromLineX(int lineDoc, int x) { retVal = ll->numCharsInLine + posLineStart; int subLine = 0; int lineStart = ll->LineStart(subLine); - int lineEnd = ll->LineStart(subLine + 1); + int lineEnd = ll->LineLastVisible(subLine); int subLineStart = ll->positions[lineStart]; if (actualWrapVisualStartIndent != 0) { if (lineStart != 0) // Wrapped x -= actualWrapVisualStartIndent * vs.aveCharWidth; } - for (int i = lineStart; i < lineEnd; i++) { - if (x < (((ll->positions[i] + ll->positions[i + 1]) / 2) - subLineStart) || - IsEOLChar(ll->chars[i])) { + int i = ll->FindBefore(x + subLineStart, lineStart, lineEnd); + while (i < lineEnd) { + if ((x + subLineStart) < ((ll->positions[i] + ll->positions[i + 1]) / 2)) { retVal = pdoc->MovePositionOutsideChar(i + posLineStart, 1); break; } + i++; } } return retVal; @@ -1929,10 +1669,6 @@ void DrawTabArrow(Surface *surface, PRectangle rcTab, int ymid) { surface->LineTo(xhead, ymid + ydiff); } -static bool IsSpaceOrTab(char ch) { - return ch == ' ' || ch == '\t'; -} - LineLayout *Editor::RetrieveLineLayout(int lineNumber) { int posLineStart = pdoc->LineStart(lineNumber); int posLineEnd = pdoc->LineStart(lineNumber + 1); @@ -1951,6 +1687,7 @@ LineLayout *Editor::RetrieveLineLayout(int lineNumber) { void Editor::LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayout *ll, int width) { if (!ll) return; + PLATFORM_ASSERT(line < pdoc->LinesTotal()); PLATFORM_ASSERT(ll->chars != NULL); int posLineStart = pdoc->LineStart(line); @@ -2086,7 +1823,7 @@ void Editor::LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayou ll->positions[charInLine + 1] = vstyle.styles[ll->styles[charInLine]].spaceWidth; } else { lastSegItalics = vstyle.styles[ll->styles[charInLine]].italic; - surface->MeasureWidths(vstyle.styles[ll->styles[charInLine]].font, ll->chars + startseg, + posCache.MeasureWidths(surface, vstyle, ll->styles[charInLine], ll->chars + startseg, lenSeg, ll->positions + startseg + 1); } } @@ -2126,8 +1863,6 @@ void Editor::LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayou } ll->lines = 0; // Calculate line start positions based upon width. - // For now this is simplistic - wraps on byte rather than character and - // in the middle of words. Should search for spaces or style changes. int lastGoodBreak = 0; int lastLineStart = 0; int startOffset = 0; @@ -2508,60 +2243,64 @@ void Editor::DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int lineVis } } - int i; + // Does not take margin into account but not significant + int xStartVisible = subLineStart - xStart; + + BreakFinder bfBack(ll, lineStart, lineEnd, posLineStart, xStartVisible); + int next = bfBack.First(); // Background drawing loop - for (i = lineStart; twoPhaseDraw && (i < lineEnd); i++) { + while (twoPhaseDraw && (next < lineEnd)) { + startseg = next; + next = bfBack.Next(); + int i = next - 1; int iDoc = i + posLineStart; - // If there is the end of a style run for any reason - if ((ll->styles[i] != ll->styles[i + 1]) || - i == (lineEnd - 1) || - IsControlCharacter(ll->chars[i]) || IsControlCharacter(ll->chars[i + 1]) || - ((ll->selStart != ll->selEnd) && ((iDoc + 1 == ll->selStart) || (iDoc + 1 == ll->selEnd))) || - (i == (ll->edgeColumn - 1))) { - rcSegment.left = ll->positions[startseg] + xStart - subLineStart; - rcSegment.right = ll->positions[i + 1] + xStart - subLineStart; - // Only try to draw if really visible - enhances performance by not calling environment to - // draw strings that are completely past the right side of the window. - if ((rcSegment.left <= rcLine.right) && (rcSegment.right >= rcLine.left)) { - int styleMain = ll->styles[i]; - bool inSelection = (iDoc >= ll->selStart) && (iDoc < ll->selEnd) && (ll->selStart != ll->selEnd); - bool inHotspot = (ll->hsStart != -1) && (iDoc >= ll->hsStart) && (iDoc < ll->hsEnd); - ColourAllocated textBack = TextBackground(vsDraw, overrideBackground, background, inSelection, inHotspot, styleMain, i, ll); - if (ll->chars[i] == '\t') { - // Tab display - if (drawWhitespaceBackground && - (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) - textBack = vsDraw.whitespaceBackground.allocated; - surface->FillRectangle(rcSegment, textBack); - } else if (IsControlCharacter(ll->chars[i])) { - // Control character display - inIndentation = false; - surface->FillRectangle(rcSegment, textBack); - } else { - // Normal text display - surface->FillRectangle(rcSegment, textBack); - if (vsDraw.viewWhitespace != wsInvisible || - (inIndentation && vsDraw.viewIndentationGuides)) { - for (int cpos = 0; cpos <= i - startseg; cpos++) { - if (ll->chars[cpos + startseg] == ' ') { - if (drawWhitespaceBackground && - (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) { - PRectangle rcSpace(ll->positions[cpos + startseg] + xStart, rcSegment.top, - ll->positions[cpos + startseg + 1] + xStart, rcSegment.bottom); - surface->FillRectangle(rcSpace, vsDraw.whitespaceBackground.allocated); - } - } else { - inIndentation = false; + + rcSegment.left = ll->positions[startseg] + xStart - subLineStart; + rcSegment.right = ll->positions[i + 1] + xStart - subLineStart; + // Only try to draw if really visible - enhances performance by not calling environment to + // draw strings that are completely past the right side of the window. + if ((rcSegment.left <= rcLine.right) && (rcSegment.right >= rcLine.left)) { + // Clip to line rectangle, since may have a huge position which will not work with some platforms + rcSegment.left = Platform::Maximum(rcSegment.left, rcLine.left); + rcSegment.right = Platform::Minimum(rcSegment.right, rcLine.right); + + int styleMain = ll->styles[i]; + bool inSelection = (iDoc >= ll->selStart) && (iDoc < ll->selEnd) && (ll->selStart != ll->selEnd); + bool inHotspot = (ll->hsStart != -1) && (iDoc >= ll->hsStart) && (iDoc < ll->hsEnd); + ColourAllocated textBack = TextBackground(vsDraw, overrideBackground, background, inSelection, inHotspot, styleMain, i, ll); + if (ll->chars[i] == '\t') { + // Tab display + if (drawWhitespaceBackground && + (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) + textBack = vsDraw.whitespaceBackground.allocated; + surface->FillRectangle(rcSegment, textBack); + } else if (IsControlCharacter(ll->chars[i])) { + // Control character display + inIndentation = false; + surface->FillRectangle(rcSegment, textBack); + } else { + // Normal text display + surface->FillRectangle(rcSegment, textBack); + if (vsDraw.viewWhitespace != wsInvisible || + (inIndentation && vsDraw.viewIndentationGuides)) { + for (int cpos = 0; cpos <= i - startseg; cpos++) { + if (ll->chars[cpos + startseg] == ' ') { + if (drawWhitespaceBackground && + (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) { + PRectangle rcSpace(ll->positions[cpos + startseg] + xStart, rcSegment.top, + ll->positions[cpos + startseg + 1] + xStart, rcSegment.bottom); + surface->FillRectangle(rcSpace, vsDraw.whitespaceBackground.allocated); } + } else { + inIndentation = false; } } } - } else if (rcSegment.left > rcLine.right) { - break; } - startseg = i + 1; + } else if (rcSegment.left > rcLine.right) { + break; } } @@ -2581,160 +2320,159 @@ void Editor::DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int lineVis } inIndentation = subLine == 0; // Do not handle indentation except on first subline. - startseg = ll->LineStart(subLine); // Foreground drawing loop - for (i = lineStart; i < lineEnd; i++) { + BreakFinder bfFore(ll, lineStart, lineEnd, posLineStart, xStartVisible); + next = bfFore.First(); + + while (next < lineEnd) { + + startseg = next; + next = bfFore.Next(); + int i = next - 1; int iDoc = i + posLineStart; - // If there is the end of a style run for any reason - if ((ll->styles[i] != ll->styles[i + 1]) || - i == (lineEnd - 1) || - IsControlCharacter(ll->chars[i]) || IsControlCharacter(ll->chars[i + 1]) || - ((ll->selStart != ll->selEnd) && ((iDoc + 1 == ll->selStart) || (iDoc + 1 == ll->selEnd))) || - (i == (ll->edgeColumn - 1))) { - rcSegment.left = ll->positions[startseg] + xStart - subLineStart; - rcSegment.right = ll->positions[i + 1] + xStart - subLineStart; - // Only try to draw if really visible - enhances performance by not calling environment to - // draw strings that are completely past the right side of the window. - if ((rcSegment.left <= rcLine.right) && (rcSegment.right >= rcLine.left)) { - int styleMain = ll->styles[i]; - ColourAllocated textFore = vsDraw.styles[styleMain].fore.allocated; - Font &textFont = vsDraw.styles[styleMain].font; - //hotspot foreground - if (ll->hsStart != -1 && iDoc >= ll->hsStart && iDoc < hsEnd) { - if (vsDraw.hotspotForegroundSet) - textFore = vsDraw.hotspotForeground.allocated; + + rcSegment.left = ll->positions[startseg] + xStart - subLineStart; + rcSegment.right = ll->positions[i + 1] + xStart - subLineStart; + // Only try to draw if really visible - enhances performance by not calling environment to + // draw strings that are completely past the right side of the window. + if ((rcSegment.left <= rcLine.right) && (rcSegment.right >= rcLine.left)) { + int styleMain = ll->styles[i]; + ColourAllocated textFore = vsDraw.styles[styleMain].fore.allocated; + Font &textFont = vsDraw.styles[styleMain].font; + //hotspot foreground + if (ll->hsStart != -1 && iDoc >= ll->hsStart && iDoc < hsEnd) { + if (vsDraw.hotspotForegroundSet) + textFore = vsDraw.hotspotForeground.allocated; + } + bool inSelection = (iDoc >= ll->selStart) && (iDoc < ll->selEnd) && (ll->selStart != ll->selEnd); + if (inSelection && (vsDraw.selforeset)) { + textFore = vsDraw.selforeground.allocated; + } + bool inHotspot = (ll->hsStart != -1) && (iDoc >= ll->hsStart) && (iDoc < ll->hsEnd); + ColourAllocated textBack = TextBackground(vsDraw, overrideBackground, background, inSelection, inHotspot, styleMain, i, ll); + if (ll->chars[i] == '\t') { + // Tab display + if (!twoPhaseDraw) { + if (drawWhitespaceBackground && + (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) + textBack = vsDraw.whitespaceBackground.allocated; + surface->FillRectangle(rcSegment, textBack); } - bool inSelection = (iDoc >= ll->selStart) && (iDoc < ll->selEnd) && (ll->selStart != ll->selEnd); - if (inSelection && (vsDraw.selforeset)) { - textFore = vsDraw.selforeground.allocated; + if ((vsDraw.viewWhitespace != wsInvisible) || ((inIndentation && vsDraw.viewIndentationGuides))) { + if (vsDraw.whitespaceForegroundSet) + textFore = vsDraw.whitespaceForeground.allocated; + surface->PenColour(textFore); } - bool inHotspot = (ll->hsStart != -1) && (iDoc >= ll->hsStart) && (iDoc < ll->hsEnd); - ColourAllocated textBack = TextBackground(vsDraw, overrideBackground, background, inSelection, inHotspot, styleMain, i, ll); - if (ll->chars[i] == '\t') { - // Tab display - if (!twoPhaseDraw) { - if (drawWhitespaceBackground && - (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) - textBack = vsDraw.whitespaceBackground.allocated; - surface->FillRectangle(rcSegment, textBack); - } - if ((vsDraw.viewWhitespace != wsInvisible) || ((inIndentation && vsDraw.viewIndentationGuides))) { - if (vsDraw.whitespaceForegroundSet) - textFore = vsDraw.whitespaceForeground.allocated; - surface->PenColour(textFore); - } - if (inIndentation && vsDraw.viewIndentationGuides) { - for (int xIG = ll->positions[i] / indentWidth * indentWidth; xIG < ll->positions[i + 1]; xIG += indentWidth) { - if (xIG >= ll->positions[i] && xIG > 0) { - DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIG + xStart, rcSegment, - (ll->xHighlightGuide == xIG)); - } + if (inIndentation && vsDraw.viewIndentationGuides) { + for (int xIG = ll->positions[i] / indentWidth * indentWidth; xIG < ll->positions[i + 1]; xIG += indentWidth) { + if (xIG >= ll->positions[i] && xIG > 0) { + DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIG + xStart, rcSegment, + (ll->xHighlightGuide == xIG)); } } - if (vsDraw.viewWhitespace != wsInvisible) { - if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) { - PRectangle rcTab(rcSegment.left + 1, rcSegment.top + 4, - rcSegment.right - 1, rcSegment.bottom - vsDraw.maxDescent); - DrawTabArrow(surface, rcTab, rcSegment.top + vsDraw.lineHeight / 2); - } + } + if (vsDraw.viewWhitespace != wsInvisible) { + if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) { + PRectangle rcTab(rcSegment.left + 1, rcSegment.top + 4, + rcSegment.right - 1, rcSegment.bottom - vsDraw.maxDescent); + DrawTabArrow(surface, rcTab, rcSegment.top + vsDraw.lineHeight / 2); } - } else if (IsControlCharacter(ll->chars[i])) { - // Control character display - inIndentation = false; - if (controlCharSymbol < 32) { - // Draw the character - const char *ctrlChar = ControlCharacterString(ll->chars[i]); - if (!twoPhaseDraw) { - surface->FillRectangle(rcSegment, textBack); - } - int normalCharHeight = surface->Ascent(ctrlCharsFont) - - surface->InternalLeading(ctrlCharsFont); - PRectangle rcCChar = rcSegment; - rcCChar.left = rcCChar.left + 1; - rcCChar.top = rcSegment.top + vsDraw.maxAscent - normalCharHeight; - rcCChar.bottom = rcSegment.top + vsDraw.maxAscent + 1; - PRectangle rcCentral = rcCChar; - rcCentral.top++; - rcCentral.bottom--; - surface->FillRectangle(rcCentral, textFore); - PRectangle rcChar = rcCChar; - rcChar.left++; - rcChar.right--; - surface->DrawTextClipped(rcChar, ctrlCharsFont, - rcSegment.top + vsDraw.maxAscent, ctrlChar, istrlen(ctrlChar), - textBack, textFore); - } else { - char cc[2] = { static_cast<char>(controlCharSymbol), '\0' }; - surface->DrawTextNoClip(rcSegment, ctrlCharsFont, - rcSegment.top + vsDraw.maxAscent, - cc, 1, textBack, textFore); + } + } else if (IsControlCharacter(ll->chars[i])) { + // Control character display + inIndentation = false; + if (controlCharSymbol < 32) { + // Draw the character + const char *ctrlChar = ControlCharacterString(ll->chars[i]); + if (!twoPhaseDraw) { + surface->FillRectangle(rcSegment, textBack); } + int normalCharHeight = surface->Ascent(ctrlCharsFont) - + surface->InternalLeading(ctrlCharsFont); + PRectangle rcCChar = rcSegment; + rcCChar.left = rcCChar.left + 1; + rcCChar.top = rcSegment.top + vsDraw.maxAscent - normalCharHeight; + rcCChar.bottom = rcSegment.top + vsDraw.maxAscent + 1; + PRectangle rcCentral = rcCChar; + rcCentral.top++; + rcCentral.bottom--; + surface->FillRectangle(rcCentral, textFore); + PRectangle rcChar = rcCChar; + rcChar.left++; + rcChar.right--; + surface->DrawTextClipped(rcChar, ctrlCharsFont, + rcSegment.top + vsDraw.maxAscent, ctrlChar, istrlen(ctrlChar), + textBack, textFore); } else { - // Normal text display - if (vsDraw.styles[styleMain].visible) { - if (twoPhaseDraw) { - surface->DrawTextTransparent(rcSegment, textFont, - rcSegment.top + vsDraw.maxAscent, ll->chars + startseg, - i - startseg + 1, textFore); - } else { - surface->DrawTextNoClip(rcSegment, textFont, - rcSegment.top + vsDraw.maxAscent, ll->chars + startseg, - i - startseg + 1, textFore, textBack); - } + char cc[2] = { static_cast<char>(controlCharSymbol), '\0' }; + surface->DrawTextNoClip(rcSegment, ctrlCharsFont, + rcSegment.top + vsDraw.maxAscent, + cc, 1, textBack, textFore); + } + } else { + // Normal text display + if (vsDraw.styles[styleMain].visible) { + if (twoPhaseDraw) { + surface->DrawTextTransparent(rcSegment, textFont, + rcSegment.top + vsDraw.maxAscent, ll->chars + startseg, + i - startseg + 1, textFore); + } else { + surface->DrawTextNoClip(rcSegment, textFont, + rcSegment.top + vsDraw.maxAscent, ll->chars + startseg, + i - startseg + 1, textFore, textBack); } - if (vsDraw.viewWhitespace != wsInvisible || - (inIndentation && vsDraw.viewIndentationGuides)) { - for (int cpos = 0; cpos <= i - startseg; cpos++) { - if (ll->chars[cpos + startseg] == ' ') { - if (vsDraw.viewWhitespace != wsInvisible) { - if (vsDraw.whitespaceForegroundSet) - textFore = vsDraw.whitespaceForeground.allocated; - if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) { - int xmid = (ll->positions[cpos + startseg] + ll->positions[cpos + startseg + 1]) / 2; - if (!twoPhaseDraw && drawWhitespaceBackground && - (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) { - textBack = vsDraw.whitespaceBackground.allocated; - PRectangle rcSpace(ll->positions[cpos + startseg] + xStart, rcSegment.top, ll->positions[cpos + startseg + 1] + xStart, rcSegment.bottom); - surface->FillRectangle(rcSpace, textBack); - } - PRectangle rcDot(xmid + xStart - subLineStart, rcSegment.top + vsDraw.lineHeight / 2, 0, 0); - rcDot.right = rcDot.left + 1; - rcDot.bottom = rcDot.top + 1; - surface->FillRectangle(rcDot, textFore); + } + if (vsDraw.viewWhitespace != wsInvisible || + (inIndentation && vsDraw.viewIndentationGuides)) { + for (int cpos = 0; cpos <= i - startseg; cpos++) { + if (ll->chars[cpos + startseg] == ' ') { + if (vsDraw.viewWhitespace != wsInvisible) { + if (vsDraw.whitespaceForegroundSet) + textFore = vsDraw.whitespaceForeground.allocated; + if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) { + int xmid = (ll->positions[cpos + startseg] + ll->positions[cpos + startseg + 1]) / 2; + if (!twoPhaseDraw && drawWhitespaceBackground && + (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) { + textBack = vsDraw.whitespaceBackground.allocated; + PRectangle rcSpace(ll->positions[cpos + startseg] + xStart, rcSegment.top, ll->positions[cpos + startseg + 1] + xStart, rcSegment.bottom); + surface->FillRectangle(rcSpace, textBack); } + PRectangle rcDot(xmid + xStart - subLineStart, rcSegment.top + vsDraw.lineHeight / 2, 0, 0); + rcDot.right = rcDot.left + 1; + rcDot.bottom = rcDot.top + 1; + surface->FillRectangle(rcDot, textFore); } - if (inIndentation && vsDraw.viewIndentationGuides) { - int startSpace = ll->positions[cpos + startseg]; - if (startSpace > 0 && (startSpace % indentWidth == 0)) { - DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, startSpace + xStart, rcSegment, - (ll->xHighlightGuide == ll->positions[cpos + startseg])); - } + } + if (inIndentation && vsDraw.viewIndentationGuides) { + int startSpace = ll->positions[cpos + startseg]; + if (startSpace > 0 && (startSpace % indentWidth == 0)) { + DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, startSpace + xStart, rcSegment, + (ll->xHighlightGuide == ll->positions[cpos + startseg])); } - } else { - inIndentation = false; } + } else { + inIndentation = false; } } } - if (ll->hsStart != -1 && vsDraw.hotspotUnderline && iDoc >= ll->hsStart && iDoc < ll->hsEnd ) { - PRectangle rcUL = rcSegment; - rcUL.top = rcUL.top + vsDraw.maxAscent + 1; - rcUL.bottom = rcUL.top + 1; - if (vsDraw.hotspotForegroundSet) - surface->FillRectangle(rcUL, vsDraw.hotspotForeground.allocated); - else - surface->FillRectangle(rcUL, textFore); - } else if (vsDraw.styles[styleMain].underline) { - PRectangle rcUL = rcSegment; - rcUL.top = rcUL.top + vsDraw.maxAscent + 1; - rcUL.bottom = rcUL.top + 1; + } + if (ll->hsStart != -1 && vsDraw.hotspotUnderline && iDoc >= ll->hsStart && iDoc < ll->hsEnd ) { + PRectangle rcUL = rcSegment; + rcUL.top = rcUL.top + vsDraw.maxAscent + 1; + rcUL.bottom = rcUL.top + 1; + if (vsDraw.hotspotForegroundSet) + surface->FillRectangle(rcUL, vsDraw.hotspotForeground.allocated); + else surface->FillRectangle(rcUL, textFore); - } - } else if (rcSegment.left > rcLine.right) { - break; + } else if (vsDraw.styles[styleMain].underline) { + PRectangle rcUL = rcSegment; + rcUL.top = rcUL.top + vsDraw.maxAscent + 1; + rcUL.bottom = rcUL.top + 1; + surface->FillRectangle(rcUL, textFore); } - startseg = i + 1; + } else if (rcSegment.left > rcLine.right) { + break; } } @@ -2752,6 +2490,8 @@ void Editor::DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int lineVis if (startPosSel < endPosSel) { rcSegment.left = xStart + ll->positions[startPosSel - posLineStart] - subLineStart; rcSegment.right = xStart + ll->positions[endPosSel - posLineStart] - subLineStart; + rcSegment.left = Platform::Maximum(rcSegment.left, rcLine.left); + rcSegment.right = Platform::Minimum(rcSegment.right, rcLine.right); SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw), vsDraw.selAlpha); } } @@ -3267,6 +3007,9 @@ long Editor::FormatRange(bool draw, RangeToFormat *pfr) { return 0; } + // Can't use measurements cached for screen + posCache.Clear(); + ViewStyle vsPrint(vs); // Modify the view style for printing as do not normally want any of the transient features to be printed @@ -3431,6 +3174,9 @@ long Editor::FormatRange(bool draw, RangeToFormat *pfr) { ++lineDoc; } + // Clear cache so measurements are not used for screen + posCache.Clear(); + return nPrintPos; } @@ -6632,6 +6378,13 @@ sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) { case SCI_GETLAYOUTCACHE: return llc.GetLevel(); + case SCI_SETPOSITIONCACHE: + posCache.SetSize(0x400); + break; + + case SCI_GETPOSITIONCACHE: + return posCache.GetSize(); + case SCI_SETSCROLLWIDTH: PLATFORM_ASSERT(wParam > 0); if ((wParam > 0) && (wParam != static_cast<unsigned int >(scrollWidth))) { diff --git a/src/Editor.h b/src/Editor.h index a31777611..213b0e361 100644 --- a/src/Editor.h +++ b/src/Editor.h @@ -46,92 +46,6 @@ public: }; /** - */ -class LineLayout { -private: - friend class LineLayoutCache; - int *lineStarts; - int lenLineStarts; - /// Drawing is only performed for @a maxLineLength characters on each line. - int lineNumber; - bool inCache; -public: - enum { wrapWidthInfinite = 0x7ffffff }; - int maxLineLength; - int numCharsInLine; - enum validLevel { llInvalid, llCheckTextAndStyle, llPositions, llLines } validity; - int xHighlightGuide; - bool highlightColumn; - int selStart; - int selEnd; - bool containsCaret; - int edgeColumn; - char *chars; - unsigned char *styles; - int styleBitsSet; - char *indicators; - int *positions; - char bracePreviousStyles[2]; - - // Hotspot support - int hsStart; - int hsEnd; - - // Wrapped line support - int widthLine; - int lines; - - LineLayout(int maxLineLength_); - virtual ~LineLayout(); - void Resize(int maxLineLength_); - void Free(); - void Invalidate(validLevel validity_); - int LineStart(int line) { - if (line <= 0) { - return 0; - } else if ((line >= lines) || !lineStarts) { - return numCharsInLine; - } else { - return lineStarts[line]; - } - } - void SetLineStart(int line, int start); - void SetBracesHighlight(Range rangeLine, Position braces[], - char bracesMatchStyle, int xHighlight); - void RestoreBracesHighlight(Range rangeLine, Position braces[]); -}; - -/** - */ -class LineLayoutCache { - int level; - int length; - int size; - LineLayout **cache; - bool allInvalidated; - int styleClock; - int useCount; - void Allocate(int length_); - void AllocateForLevel(int linesOnScreen, int linesInDoc); -public: - LineLayoutCache(); - virtual ~LineLayoutCache(); - void Deallocate(); - enum { - llcNone=SC_CACHE_NONE, - llcCaret=SC_CACHE_CARET, - llcPage=SC_CACHE_PAGE, - llcDocument=SC_CACHE_DOCUMENT - }; - void Invalidate(LineLayout::validLevel validity_); - void SetLevel(int level_); - int GetLevel() { return level; } - LineLayout *Retrieve(int lineNumber, int lineCaret, int maxChars, int styleClock_, - int linesOnScreen, int linesInDoc); - void Dispose(LineLayout *ll); -}; - -/** * Hold a piece of text selected for copying or dragging. * The text is expected to hold a terminating '\0' and this is counted in len. */ @@ -233,6 +147,7 @@ protected: // ScintillaBase subclass needs access to much of Editor Surface *pixmapIndentGuideHighlight; LineLayoutCache llc; + PositionCache posCache; KeyMap kmap; diff --git a/src/PositionCache.cxx b/src/PositionCache.cxx new file mode 100644 index 000000000..01b8f68f1 --- /dev/null +++ b/src/PositionCache.cxx @@ -0,0 +1,586 @@ +// Scintilla source code edit control +/** @file PositionCache.cxx + ** Classes for caching layout information. + **/ +// Copyright 1998-2007 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <ctype.h> + +#include "Platform.h" + +#include "Scintilla.h" + +#include "ContractionState.h" +#include "SVector.h" +#include "SplitVector.h" +#include "Partitioning.h" +#include "CellBuffer.h" +#include "KeyMap.h" +#include "RunStyles.h" +#include "Indicator.h" +#include "XPM.h" +#include "LineMarker.h" +#include "Style.h" +#include "ViewStyle.h" +#include "CharClassify.h" +#include "Decoration.h" +#include "Document.h" +#include "PositionCache.h" + +static inline bool IsControlCharacter(int ch) { + // iscntrl returns true for lots of chars > 127 which are displayable + return ch >= 0 && ch < ' '; +} + +LineLayout::LineLayout(int maxLineLength_) : + lineStarts(0), + lenLineStarts(0), + lineNumber(-1), + inCache(false), + maxLineLength(-1), + numCharsInLine(0), + validity(llInvalid), + xHighlightGuide(0), + highlightColumn(0), + selStart(0), + selEnd(0), + containsCaret(false), + edgeColumn(0), + chars(0), + styles(0), + styleBitsSet(0), + indicators(0), + positions(0), + hsStart(0), + hsEnd(0), + widthLine(wrapWidthInfinite), + lines(1) { + Resize(maxLineLength_); +} + +LineLayout::~LineLayout() { + Free(); +} + +void LineLayout::Resize(int maxLineLength_) { + if (maxLineLength_ > maxLineLength) { + Free(); + chars = new char[maxLineLength_ + 1]; + styles = new unsigned char[maxLineLength_ + 1]; + indicators = new char[maxLineLength_ + 1]; + // Extra position allocated as sometimes the Windows + // GetTextExtentExPoint API writes an extra element. + positions = new int[maxLineLength_ + 1 + 1]; + maxLineLength = maxLineLength_; + } +} + +void LineLayout::Free() { + delete []chars; + chars = 0; + delete []styles; + styles = 0; + delete []indicators; + indicators = 0; + delete []positions; + positions = 0; + delete []lineStarts; + lineStarts = 0; +} + +void LineLayout::Invalidate(validLevel validity_) { + if (validity > validity_) + validity = validity_; +} + +int LineLayout::LineStart(int line) const { + if (line <= 0) { + return 0; + } else if ((line >= lines) || !lineStarts) { + return numCharsInLine; + } else { + return lineStarts[line]; + } +} + +int LineLayout::LineLastVisible(int line) const { + if (line < 0) { + return 0; + } else if ((line >= lines-1) || !lineStarts) { + int startLine = LineStart(line); + int endLine = numCharsInLine; + while ((endLine > startLine) && IsEOLChar(chars[endLine-1])) { + endLine--; + } + return endLine; + } else { + return lineStarts[line+1]; + } +} + +void LineLayout::SetLineStart(int line, int start) { + if ((line >= lenLineStarts) && (line != 0)) { + int newMaxLines = line + 20; + int *newLineStarts = new int[newMaxLines]; + if (!newLineStarts) + return; + for (int i = 0; i < newMaxLines; i++) { + if (i < lenLineStarts) + newLineStarts[i] = lineStarts[i]; + else + newLineStarts[i] = 0; + } + delete []lineStarts; + lineStarts = newLineStarts; + lenLineStarts = newMaxLines; + } + lineStarts[line] = start; +} + +void LineLayout::SetBracesHighlight(Range rangeLine, Position braces[], + char bracesMatchStyle, int xHighlight) { + if (rangeLine.ContainsCharacter(braces[0])) { + int braceOffset = braces[0] - rangeLine.start; + if (braceOffset < numCharsInLine) { + bracePreviousStyles[0] = styles[braceOffset]; + styles[braceOffset] = bracesMatchStyle; + } + } + if (rangeLine.ContainsCharacter(braces[1])) { + int braceOffset = braces[1] - rangeLine.start; + if (braceOffset < numCharsInLine) { + bracePreviousStyles[1] = styles[braceOffset]; + styles[braceOffset] = bracesMatchStyle; + } + } + if ((braces[0] >= rangeLine.start && braces[1] <= rangeLine.end) || + (braces[1] >= rangeLine.start && braces[0] <= rangeLine.end)) { + xHighlightGuide = xHighlight; + } +} + +void LineLayout::RestoreBracesHighlight(Range rangeLine, Position braces[]) { + if (rangeLine.ContainsCharacter(braces[0])) { + int braceOffset = braces[0] - rangeLine.start; + if (braceOffset < numCharsInLine) { + styles[braceOffset] = bracePreviousStyles[0]; + } + } + if (rangeLine.ContainsCharacter(braces[1])) { + int braceOffset = braces[1] - rangeLine.start; + if (braceOffset < numCharsInLine) { + styles[braceOffset] = bracePreviousStyles[1]; + } + } + xHighlightGuide = 0; +} + +int LineLayout::FindBefore(int x, int lower, int upper) const { + do { + int middle = (upper + lower + 1) / 2; // Round high + int posMiddle = positions[middle]; + if (x < posMiddle) { + upper = middle - 1; + } else { + lower = middle; + } + } while (lower < upper); + return lower; +} + +LineLayoutCache::LineLayoutCache() : + level(0), length(0), size(0), cache(0), + allInvalidated(false), styleClock(-1), useCount(0) { + Allocate(0); +} + +LineLayoutCache::~LineLayoutCache() { + Deallocate(); +} + +void LineLayoutCache::Allocate(int length_) { + PLATFORM_ASSERT(cache == NULL); + allInvalidated = false; + length = length_; + size = length; + if (size > 1) { + size = (size / 16 + 1) * 16; + } + if (size > 0) { + cache = new LineLayout * [size]; + } + for (int i = 0; i < size; i++) + cache[i] = 0; +} + +void LineLayoutCache::AllocateForLevel(int linesOnScreen, int linesInDoc) { + PLATFORM_ASSERT(useCount == 0); + int lengthForLevel = 0; + if (level == llcCaret) { + lengthForLevel = 1; + } else if (level == llcPage) { + lengthForLevel = linesOnScreen + 1; + } else if (level == llcDocument) { + lengthForLevel = linesInDoc; + } + if (lengthForLevel > size) { + Deallocate(); + Allocate(lengthForLevel); + } else { + if (lengthForLevel < length) { + for (int i = lengthForLevel; i < length; i++) { + delete cache[i]; + cache[i] = 0; + } + } + length = lengthForLevel; + } + PLATFORM_ASSERT(length == lengthForLevel); + PLATFORM_ASSERT(cache != NULL || length == 0); +} + +void LineLayoutCache::Deallocate() { + PLATFORM_ASSERT(useCount == 0); + for (int i = 0; i < length; i++) + delete cache[i]; + delete []cache; + cache = 0; + length = 0; + size = 0; +} + +void LineLayoutCache::Invalidate(LineLayout::validLevel validity_) { + if (cache && !allInvalidated) { + for (int i = 0; i < length; i++) { + if (cache[i]) { + cache[i]->Invalidate(validity_); + } + } + if (validity_ == LineLayout::llInvalid) { + allInvalidated = true; + } + } +} + +void LineLayoutCache::SetLevel(int level_) { + allInvalidated = false; + if ((level_ != -1) && (level != level_)) { + level = level_; + Deallocate(); + } +} + +LineLayout *LineLayoutCache::Retrieve(int lineNumber, int lineCaret, int maxChars, int styleClock_, + int linesOnScreen, int linesInDoc) { + AllocateForLevel(linesOnScreen, linesInDoc); + if (styleClock != styleClock_) { + Invalidate(LineLayout::llCheckTextAndStyle); + styleClock = styleClock_; + } + allInvalidated = false; + int pos = -1; + LineLayout *ret = 0; + if (level == llcCaret) { + pos = 0; + } else if (level == llcPage) { + if (lineNumber == lineCaret) { + pos = 0; + } else if (length > 1) { + pos = 1 + (lineNumber % (length - 1)); + } + } else if (level == llcDocument) { + pos = lineNumber; + } + if (pos >= 0) { + PLATFORM_ASSERT(useCount == 0); + if (cache && (pos < length)) { + if (cache[pos]) { + if ((cache[pos]->lineNumber != lineNumber) || + (cache[pos]->maxLineLength < maxChars)) { + delete cache[pos]; + cache[pos] = 0; + } + } + if (!cache[pos]) { + cache[pos] = new LineLayout(maxChars); + } + if (cache[pos]) { + cache[pos]->lineNumber = lineNumber; + cache[pos]->inCache = true; + ret = cache[pos]; + useCount++; + } + } + } + + if (!ret) { + ret = new LineLayout(maxChars); + ret->lineNumber = lineNumber; + } + + return ret; +} + +void LineLayoutCache::Dispose(LineLayout *ll) { + allInvalidated = false; + if (ll) { + if (!ll->inCache) { + delete ll; + } else { + useCount--; + } + } +} + +void BreakFinder::Insert(int val) { + if (val > nextBreak) { + for (unsigned int j = 0; j<saeLen; j++) { + if (val == selAndEdge[j]) { + return; + } if (val < selAndEdge[j]) { + for (unsigned int k = saeLen; j>k; k--) { + selAndEdge[k] = selAndEdge[k-1]; + } + saeLen++; + selAndEdge[j] = val; + return; + } + } + // Not less than any so append + selAndEdge[saeLen++] = val; + } +} + +BreakFinder::BreakFinder(LineLayout *ll_, int lineStart_, int lineEnd_, int posLineStart_, int xStart) : + ll(ll_), + lineStart(lineStart_), + lineEnd(lineEnd_), + posLineStart(posLineStart_), + nextBreak(lineStart_), + saeLen(0), + saeCurrentPos(0), + saeNext(0), + subBreak(-1) { + for (unsigned int j=0; j < sizeof(selAndEdge) / sizeof(selAndEdge[0]); j++) { + selAndEdge[j] = 0; + } + + // Search for first visible break + // First find the first visible character + nextBreak = ll->FindBefore(xStart, lineStart, lineEnd); + // Now back to a style break + while ((nextBreak > lineStart) && (ll->styles[nextBreak] == ll->styles[nextBreak - 1])) { + nextBreak--; + } + + if (ll->selStart != ll->selEnd) { + Insert(ll->selStart - posLineStart - 1); + Insert(ll->selEnd - posLineStart - 1); + } + + Insert(ll->edgeColumn - 1); + Insert(lineEnd - 1); + saeNext = (saeLen > 0) ? selAndEdge[0] : -1; +} + +int BreakFinder::First() { + return nextBreak; +} + +int BreakFinder::Next() { + if (subBreak == -1) { + int prev = nextBreak; + while (nextBreak < lineEnd) { + if ((ll->styles[nextBreak] != ll->styles[nextBreak + 1]) || + (nextBreak == saeNext) || + IsControlCharacter(ll->chars[nextBreak]) || IsControlCharacter(ll->chars[nextBreak + 1])) { + if (nextBreak == saeNext) { + saeCurrentPos++; + saeNext = (saeLen > saeCurrentPos) ? selAndEdge[saeCurrentPos] : -1; + } + nextBreak++; + if ((nextBreak - prev) < lengthStartSubdivision) { + return nextBreak; + } + break; + } + nextBreak++; + } + if ((nextBreak - prev) < lengthStartSubdivision) { + return nextBreak; + } + subBreak = prev; + } + // Splitting up a long run from prev to nextBreak in lots of approximately lengthEachSubdivision. + // For very long runs add extra breaks after spaces or if no spaces before low punctuation. + if ((nextBreak - subBreak) <= lengthEachSubdivision) { + subBreak = -1; + return nextBreak; + } else { + int lastGoodBreak = -1; + int lastOKBreak = -1; + int j; + for (j = subBreak + 1; j <= nextBreak; j++) { + if (IsSpaceOrTab(ll->chars[j - 1]) && !IsSpaceOrTab(ll->chars[j])) { + lastGoodBreak = j; + } + if (ll->chars[j] < 'A') { + lastOKBreak = j; + } + if (((j - subBreak) >= lengthEachSubdivision) && ((lastGoodBreak >= 0) || (lastOKBreak >= 0))) { + break; + } + } + if (lastGoodBreak >= 0) { + subBreak = lastGoodBreak; + } else if (lastOKBreak >= 0) { + subBreak = lastOKBreak; + } else { + subBreak = nextBreak; + } + if (subBreak >= nextBreak) { + subBreak = -1; + return nextBreak; + } else { + return subBreak; + } + } +} + +PositionCacheEntry::PositionCacheEntry() : + styleNumber(0), len(0), clock(0), positions(0) { +} + +void PositionCacheEntry::Set(unsigned int styleNumber_, const char *s_, + unsigned int len_, int *positions_, unsigned int clock_) { + Clear(); + styleNumber = styleNumber_; + len = len_; + clock = clock_; + if (s_ && positions_) { + positions = new short[len + (len + 1) / 2]; + for (unsigned int i=0;i<len;i++) { + positions[i] = static_cast<short>(positions_[i]); + } + memcpy(reinterpret_cast<char *>(positions + len), s_, len); + } +} + +PositionCacheEntry::~PositionCacheEntry() { + Clear(); +} + +void PositionCacheEntry::Clear() { + delete []positions; + positions = 0; + styleNumber = 0; + len = 0; + clock = 0; +} + +bool PositionCacheEntry::Retrieve(unsigned int styleNumber_, const char *s_, + unsigned int len_, int *positions_) const { + if ((styleNumber == styleNumber_) && (len == len_) && + (memcmp(reinterpret_cast<char *>(positions + len), s_, len)== 0)) { + for (unsigned int i=0;i<len;i++) { + positions_[i] = positions[i]; + } + return true; + } else { + return false; + } +} + +int PositionCacheEntry::Hash(unsigned int styleNumber, const char *s, unsigned int len) { + unsigned int ret = s[0] << 7; + for (unsigned int i=0; i<len; i++) { + ret *= 1000003; + ret ^= s[i]; + } + ret *= 1000003; + ret ^= len; + ret *= 1000003; + ret ^= styleNumber; + return ret; +} + +bool PositionCacheEntry::NewerThan(const PositionCacheEntry &other) { + return clock > other.clock; +} + +void PositionCacheEntry::ResetClock() { + if (clock > 0) { + clock = 1; + } +} + +PositionCache::PositionCache() { + size = 0x400; + clock = 1; + pces = new PositionCacheEntry[size]; + allClear = true; +} + +PositionCache::~PositionCache() { + Clear(); + delete []pces; +} + +void PositionCache::Clear() { + if (!allClear) { + for (size_t i=0;i<size;i++) { + pces[i].Clear(); + } + } + clock = 1; + allClear = true; +} + +void PositionCache::SetSize(size_t size_) { + Clear(); + delete []pces; + size = size_; + pces = new PositionCacheEntry[size]; +} + +void PositionCache::MeasureWidths(Surface *surface, ViewStyle &vstyle, unsigned int styleNumber, + const char *s, unsigned int len, int *positions) { + allClear = false; + int probe = -1; + if ((size > 0) && (len < 30)) { + // Only store short strings in the cache so it doesn't churn with + // long comments with only a single comment. + + // Two way associative: try two probe positions. + int hashValue = PositionCacheEntry::Hash(styleNumber, s, len); + probe = hashValue % size; + if (pces[probe].Retrieve(styleNumber, s, len, positions)) { + return; + } + int probe2 = (hashValue * 37) % size; + if (pces[probe2].Retrieve(styleNumber, s, len, positions)) { + return; + } + // Not found. Choose the oldest of the two slots to replace + if (pces[probe].NewerThan(pces[probe2])) { + probe = probe2; + } + } + surface->MeasureWidths(vstyle.styles[styleNumber].font, s, len, positions); + if (probe >= 0) { + clock++; + if (clock > 60000) { + // Since there are only 16 bits for the clock, wrap it round and + // reset all cache entries so none get stuck with a high clock. + for (size_t i=0;i<size;i++) { + pces[i].ResetClock(); + } + clock = 2; + } + pces[probe].Set(styleNumber, s, len, positions, clock); + } +} diff --git a/src/PositionCache.h b/src/PositionCache.h new file mode 100644 index 000000000..fc261fbbc --- /dev/null +++ b/src/PositionCache.h @@ -0,0 +1,154 @@ +// Scintilla source code edit control +/** @file PositionCache.h + ** Classes for caching layout information. + **/ +// Copyright 1998-2007 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef POSITIONCACHE_H +#define POSITIONCACHE_H + +static inline bool IsEOLChar(char ch) { + return (ch == '\r') || (ch == '\n'); +} + +/** + */ +class LineLayout { +private: + friend class LineLayoutCache; + int *lineStarts; + int lenLineStarts; + /// Drawing is only performed for @a maxLineLength characters on each line. + int lineNumber; + bool inCache; +public: + enum { wrapWidthInfinite = 0x7ffffff }; + int maxLineLength; + int numCharsInLine; + enum validLevel { llInvalid, llCheckTextAndStyle, llPositions, llLines } validity; + int xHighlightGuide; + bool highlightColumn; + int selStart; + int selEnd; + bool containsCaret; + int edgeColumn; + char *chars; + unsigned char *styles; + int styleBitsSet; + char *indicators; + int *positions; + char bracePreviousStyles[2]; + + // Hotspot support + int hsStart; + int hsEnd; + + // Wrapped line support + int widthLine; + int lines; + + LineLayout(int maxLineLength_); + virtual ~LineLayout(); + void Resize(int maxLineLength_); + void Free(); + void Invalidate(validLevel validity_); + int LineStart(int line) const; + int LineLastVisible(int line) const; + void SetLineStart(int line, int start); + void SetBracesHighlight(Range rangeLine, Position braces[], + char bracesMatchStyle, int xHighlight); + void RestoreBracesHighlight(Range rangeLine, Position braces[]); + int FindBefore(int x, int lower, int upper) const; +}; + +/** + */ +class LineLayoutCache { + int level; + int length; + int size; + LineLayout **cache; + bool allInvalidated; + int styleClock; + int useCount; + void Allocate(int length_); + void AllocateForLevel(int linesOnScreen, int linesInDoc); +public: + LineLayoutCache(); + virtual ~LineLayoutCache(); + void Deallocate(); + enum { + llcNone=SC_CACHE_NONE, + llcCaret=SC_CACHE_CARET, + llcPage=SC_CACHE_PAGE, + llcDocument=SC_CACHE_DOCUMENT + }; + void Invalidate(LineLayout::validLevel validity_); + void SetLevel(int level_); + int GetLevel() { return level; } + LineLayout *Retrieve(int lineNumber, int lineCaret, int maxChars, int styleClock_, + int linesOnScreen, int linesInDoc); + void Dispose(LineLayout *ll); +}; + +class PositionCacheEntry { + unsigned int styleNumber:8; + unsigned int len:8; + unsigned int clock:16; + short *positions; +public: + PositionCacheEntry(); + ~PositionCacheEntry(); + void Set(unsigned int styleNumber_, const char *s_, unsigned int len_, int *positions_, unsigned int clock); + void Clear(); + bool Retrieve(unsigned int styleNumber_, const char *s_, unsigned int len_, int *positions_) const; + static int Hash(unsigned int styleNumber, const char *s, unsigned int len); + bool NewerThan(const PositionCacheEntry &other); + void ResetClock(); +}; + +// Class to break a line of text into shorter runs at sensible places. +class BreakFinder { + // If a whole run is longer than lengthStartSubdivision then subdivide + // into smaller runs at spaces or punctuation. + enum { lengthStartSubdivision = 10 }; + // Try to make each subdivided run lengthEachSubdivision or shorter. + enum { lengthEachSubdivision = 5 }; + LineLayout *ll; + int lineStart; + int lineEnd; + int posLineStart; + int nextBreak; + int selAndEdge[5]; + unsigned int saeLen; + unsigned int saeCurrentPos; + int saeNext; + int subBreak; + void Insert(int val); +public: + BreakFinder(LineLayout *ll_, int lineStart_, int lineEnd_, int posLineStart_, int xStart); + int First(); + int Next(); +}; + +class PositionCache { + PositionCacheEntry *pces; + size_t size; + unsigned int clock; + bool allClear; +public: + PositionCache(); + ~PositionCache(); + void Clear(); + void SetSize(size_t size_); + int GetSize() { return size; } + void MeasureWidths(Surface *surface, ViewStyle &vstyle, unsigned int styleNumber, + const char *s, unsigned int len, int *positions); +}; + +inline bool IsSpaceOrTab(int ch) { + return ch == ' ' || ch == '\t'; +} + +#endif diff --git a/src/ScintillaBase.cxx b/src/ScintillaBase.cxx index e0ac652fb..e2dd9473e 100644 --- a/src/ScintillaBase.cxx +++ b/src/ScintillaBase.cxx @@ -37,6 +37,7 @@ #include "CharClassify.h" #include "Decoration.h" #include "Document.h" +#include "PositionCache.h" #include "Editor.h" #include "ScintillaBase.h" |