aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authornyamatongwe <unknown>2007-06-02 05:18:13 +0000
committernyamatongwe <unknown>2007-06-02 05:18:13 +0000
commit284a7cde23d26ccc5c5b75aaa9c1e69e659a4adc (patch)
tree22d32a8aeccfda9902240bfca1ffad692136f45c /src
parentb78f4e0f1eeb340c23b3c9df896da7405c2e0217 (diff)
downloadscintilla-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.cxx687
-rw-r--r--src/Editor.h87
-rw-r--r--src/PositionCache.cxx586
-rw-r--r--src/PositionCache.h154
-rw-r--r--src/ScintillaBase.cxx1
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"