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 | |
| 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.
| -rw-r--r-- | gtk/ScintillaGTK.cxx | 1 | ||||
| -rw-r--r-- | gtk/deps.mak | 29 | ||||
| -rw-r--r-- | gtk/makefile | 4 | ||||
| -rw-r--r-- | gtk/scintilla.mak | 11 | ||||
| -rw-r--r-- | include/Scintilla.h | 2 | ||||
| -rw-r--r-- | include/Scintilla.iface | 6 | ||||
| -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 | ||||
| -rw-r--r-- | vcbuild/SciLexer.dsp | 4 | ||||
| -rw-r--r-- | win32/ScintillaWin.cxx | 1 | ||||
| -rw-r--r-- | win32/makefile | 4 | ||||
| -rw-r--r-- | win32/scintilla.mak | 10 | ||||
| -rw-r--r-- | win32/scintilla_vc6.mak | 2 | 
16 files changed, 1021 insertions, 568 deletions
diff --git a/gtk/ScintillaGTK.cxx b/gtk/ScintillaGTK.cxx index 71c7b342f..7a1aab337 100644 --- a/gtk/ScintillaGTK.cxx +++ b/gtk/ScintillaGTK.cxx @@ -43,6 +43,7 @@  #include "Decoration.h"  #include "CharClassify.h"  #include "Document.h" +#include "PositionCache.h"  #include "Editor.h"  #include "SString.h"  #include "ScintillaBase.h" diff --git a/gtk/deps.mak b/gtk/deps.mak index f8fa285fd..8fe88e266 100644 --- a/gtk/deps.mak +++ b/gtk/deps.mak @@ -1,9 +1,7 @@ -PlatGTK.o: PlatGTK.cxx  \ -   ../include/Platform.h \ +PlatGTK.o: PlatGTK.cxx \    ../include/Scintilla.h ../include/ScintillaWidget.h \    ../src/UniConversion.h ../src/XPM.h Converter.h -ScintillaGTK.o: ScintillaGTK.cxx  \ -   ../include/Platform.h \ +ScintillaGTK.o: ScintillaGTK.cxx \    ../include/Scintilla.h ../include/ScintillaWidget.h \    ../include/SciLexer.h ../include/PropSet.h ../include/SString.h \    ../include/Accessor.h ../include/KeyWords.h ../src/ContractionState.h \ @@ -11,9 +9,10 @@ ScintillaGTK.o: ScintillaGTK.cxx  \    ../src/CellBuffer.h ../src/CallTip.h ../src/KeyMap.h ../src/Indicator.h \    ../src/XPM.h ../src/LineMarker.h ../src/Style.h ../src/AutoComplete.h \    ../src/ViewStyle.h ../src/RunStyles.h ../src/Decoration.h \ -  ../src/CharClassify.h ../src/Document.h ../src/Editor.h \ -  ../include/SString.h ../src/ScintillaBase.h ../src/UniConversion.h \ -  scintilla-marshal.h ../src/ExternalLexer.h Converter.h +  ../src/CharClassify.h ../src/Document.h ../src/PositionCache.h \ +  ../src/Editor.h ../include/SString.h ../src/ScintillaBase.h \ +  ../src/UniConversion.h scintilla-marshal.h ../src/ExternalLexer.h \ +  Converter.h  AutoComplete.o: ../src/AutoComplete.cxx ../include/Platform.h \    ../include/PropSet.h ../include/SString.h ../src/AutoComplete.h  CallTip.o: ../src/CallTip.cxx ../include/Platform.h \ @@ -43,7 +42,8 @@ Editor.o: ../src/Editor.cxx ../include/Platform.h ../include/Scintilla.h \    ../src/Partitioning.h ../src/CellBuffer.h ../src/KeyMap.h \    ../src/RunStyles.h ../src/Indicator.h ../src/XPM.h ../src/LineMarker.h \    ../src/Style.h ../src/ViewStyle.h ../src/CharClassify.h \ -  ../src/Decoration.h ../src/Document.h ../src/Editor.h +  ../src/Decoration.h ../src/Document.h ../src/PositionCache.h \ +  ../src/Editor.h  ExternalLexer.o: ../src/ExternalLexer.cxx ../include/Platform.h \    ../include/Scintilla.h ../include/SciLexer.h ../include/PropSet.h \    ../include/SString.h ../include/Accessor.h ../src/DocumentAccessor.h \ @@ -137,6 +137,9 @@ LexFortran.o: ../src/LexFortran.cxx ../include/Platform.h \    ../include/PropSet.h ../include/SString.h ../include/Accessor.h \    ../src/StyleContext.h ../include/KeyWords.h ../include/Scintilla.h \    ../include/SciLexer.h +LexGAP.o: ../src/LexGAP.cxx ../include/Platform.h ../include/PropSet.h \ +  ../include/SString.h ../include/Accessor.h ../src/StyleContext.h \ +  ../include/KeyWords.h ../include/Scintilla.h ../include/SciLexer.h  LexGui4Cli.o: ../src/LexGui4Cli.cxx ../include/Platform.h \    ../include/PropSet.h ../include/SString.h ../include/Accessor.h \    ../src/StyleContext.h ../include/KeyWords.h ../include/Scintilla.h \ @@ -260,6 +263,13 @@ LexYAML.o: ../src/LexYAML.cxx ../include/Platform.h ../include/PropSet.h \    ../include/KeyWords.h ../include/Scintilla.h ../include/SciLexer.h  LineMarker.o: ../src/LineMarker.cxx ../include/Platform.h \    ../include/Scintilla.h ../src/XPM.h ../src/LineMarker.h +PositionCache.o: ../src/PositionCache.cxx ../include/Platform.h \ +  ../include/Scintilla.h ../src/ContractionState.h ../src/SVector.h \ +  ../src/SplitVector.h ../src/Partitioning.h ../src/CellBuffer.h \ +  ../src/KeyMap.h ../src/RunStyles.h ../src/Indicator.h ../src/XPM.h \ +  ../src/LineMarker.h ../src/Style.h ../src/ViewStyle.h \ +  ../src/CharClassify.h ../src/Decoration.h ../src/Document.h \ +  ../src/PositionCache.h  PropSet.o: ../src/PropSet.cxx ../include/Platform.h ../include/PropSet.h \    ../include/SString.h  RESearch.o: ../src/RESearch.cxx ../src/CharClassify.h ../src/RESearch.h @@ -274,7 +284,8 @@ ScintillaBase.o: ../src/ScintillaBase.cxx ../include/Platform.h \    ../src/CellBuffer.h ../src/CallTip.h ../src/KeyMap.h ../src/Indicator.h \    ../src/XPM.h ../src/LineMarker.h ../src/Style.h ../src/ViewStyle.h \    ../src/AutoComplete.h ../src/CharClassify.h ../src/Decoration.h \ -  ../src/Document.h ../src/Editor.h ../src/ScintillaBase.h +  ../src/Document.h ../src/PositionCache.h ../src/Editor.h \ +  ../src/ScintillaBase.h  StyleContext.o: ../src/StyleContext.cxx ../include/Platform.h \    ../include/PropSet.h ../include/SString.h ../include/Accessor.h \    ../src/StyleContext.h diff --git a/gtk/makefile b/gtk/makefile index eb7dec885..52a88f4e7 100644 --- a/gtk/makefile +++ b/gtk/makefile @@ -80,12 +80,12 @@ clean:  	rm -f *.o $(COMPLIB)  deps: -	$(CC) -MM $(CONFIGFLAGS) $(CXXFLAGS) *.cxx ../src/*.cxx >deps.mak +	$(CC) -MM $(CONFIGFLAGS) $(CXXFLAGS) *.cxx ../src/*.cxx | sed -e 's/\/usr.* //' | grep [a-zA-Z] >deps.mak  $(COMPLIB): DocumentAccessor.o WindowAccessor.o KeyWords.o StyleContext.o \  	CharClassify.o Decoration.o Document.o CallTip.o \  	ScintillaBase.o ContractionState.o Editor.o ExternalLexer.o PropSet.o PlatGTK.o \ -	KeyMap.o LineMarker.o ScintillaGTK.o CellBuffer.o ViewStyle.o \ +	KeyMap.o LineMarker.o PositionCache.o ScintillaGTK.o CellBuffer.o ViewStyle.o \  	RESearch.o RunStyles.o Style.o Indicator.o AutoComplete.o UniConversion.o XPM.o \  	$(MARSHALLER) $(LEXOBJS)  	$(AR) rc $@ $^ diff --git a/gtk/scintilla.mak b/gtk/scintilla.mak index deb3df2ea..1f2dcc798 100644 --- a/gtk/scintilla.mak +++ b/gtk/scintilla.mak @@ -128,6 +128,7 @@ SOBJS=\  	$(DIR_O)\KeyMap.obj \  	$(DIR_O)\LineMarker.obj \  	$(DIR_O)\PlatGTK.obj \ +	$(DIR_O)\PositionCache.obj \  	$(MARSHALLER) \  	$(DIR_O)\RESearch.obj \  	$(DIR_O)\RunStyles.obj \ @@ -222,6 +223,7 @@ LOBJS=\  	$(DIR_O)\KeyWords.obj \  	$(DIR_O)\LineMarker.obj \  	$(DIR_O)\PlatGTK.obj \ +	$(DIR_O)\PositionCache.obj \  	$(MARSHALLER) \  	$(DIR_O)\RESearch.obj \  	$(DIR_O)\RunStyles.obj \ @@ -324,7 +326,7 @@ $(DIR_O)\Editor.obj: ../src/Editor.cxx ../include/Platform.h ../include/Scintill    ../src/Partitioning.h ../src/CellBuffer.h ../src/KeyMap.h \    ../src/RunStyles.h ../src/Indicator.h ../src/XPM.h ../src/LineMarker.h \    ../src/Style.h ../src/ViewStyle.h ../src/CharClassify.h \ -  ../src/Document.h ../src/Editor.h +  ../src/Document.h ../src/Editor.h ../src/PositionCache.h  $(DIR_O)\ExternalLexer.obj: ../src/ExternalLexer.cxx ../include/Platform.h \    ../include/Scintilla.h ../include/SciLexer.h ../include/PropSet.h \ @@ -478,6 +480,13 @@ $(DIR_O)\LineMarker.obj: ../src/LineMarker.cxx ../include/Platform.h \  $(DIR_O)\PlatGTK.obj: PlatGTK.cxx ../include/Platform.h \    ../src/UniConversion.h ../src/XPM.h +$(DIR_O)\PositionCache.obj: ../src/Editor.cxx ../include/Platform.h ../include/Scintilla.h \ +  ../src/ContractionState.h ../src/SVector.h ../src/SplitVector.h \ +  ../src/Partitioning.h ../src/CellBuffer.h ../src/KeyMap.h \ +  ../src/RunStyles.h ../src/Indicator.h ../src/XPM.h ../src/LineMarker.h \ +  ../src/Style.h ../src/ViewStyle.h ../src/CharClassify.h \ +  ../src/Decoration.h ../src/Document.h ../src/Editor.h ../src/PositionCache.h +  $(DIR_O)\PropSet.obj: ../src/PropSet.cxx ../include/Platform.h ../include/PropSet.h \    ../include/SString.h diff --git a/include/Scintilla.h b/include/Scintilla.h index 81a7b814d..c50528b72 100644 --- a/include/Scintilla.h +++ b/include/Scintilla.h @@ -657,6 +657,8 @@ typedef sptr_t (*SciFnDirect)(sptr_t ptr, unsigned int iMessage, uptr_t wParam,  #define SCI_INDICATORVALUEAT 2507  #define SCI_INDICATORSTART 2508  #define SCI_INDICATOREND 2509 +#define SCI_SETPOSITIONCACHE 2514 +#define SCI_GETPOSITIONCACHE 2515  #define SCI_STARTRECORD 3001  #define SCI_STOPRECORD 3002  #define SCI_SETLEXER 4001 diff --git a/include/Scintilla.iface b/include/Scintilla.iface index 7d21f41b3..b932a33f6 100644 --- a/include/Scintilla.iface +++ b/include/Scintilla.iface @@ -1775,6 +1775,12 @@ fun int IndicatorStart=2508(int indicator, int position)  # Where does a particular indicator end?  fun int IndicatorEnd=2509(int indicator, int position) +# Set number of entries in position cache +set void SetPositionCache=2514(int size,) + +# How many entries are allocated to the position cache? +get int GetPositionCache=2515(,) +  # Start notifying the container of all key presses and commands.  fun void StartRecord=3001(,) 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" diff --git a/vcbuild/SciLexer.dsp b/vcbuild/SciLexer.dsp index 4a3b01c8a..015835908 100644 --- a/vcbuild/SciLexer.dsp +++ b/vcbuild/SciLexer.dsp @@ -402,6 +402,10 @@ SOURCE=..\win32\PlatWin.cxx  # End Source File  # Begin Source File +SOURCE=..\src\PositionCache.cxx +# End Source File +# Begin Source File +  SOURCE=..\src\PropSet.cxx  # End Source File  # Begin Source File diff --git a/win32/ScintillaWin.cxx b/win32/ScintillaWin.cxx index 6d7f1268d..248513e18 100644 --- a/win32/ScintillaWin.cxx +++ b/win32/ScintillaWin.cxx @@ -45,6 +45,7 @@  #include "CharClassify.h"  #include "Decoration.h"  #include "Document.h" +#include "PositionCache.h"  #include "Editor.h"  #include "ScintillaBase.h"  #include "UniConversion.h" diff --git a/win32/makefile b/win32/makefile index c9da20826..63b30dd56 100644 --- a/win32/makefile +++ b/win32/makefile @@ -64,14 +64,14 @@ LexTADS3.o LexTCL.o LexTeX.o LexVB.o LexVerilog.o LexVHDL.o LexYAML.o  SOBJS	= ScintillaWin.o ScintillaBase.o Editor.o CharClassify.o Decoration.o \  	Document.o ContractionState.o CellBuffer.o CallTip.o \ -	ScintRes.o PlatWin.o KeyMap.o Indicator.o LineMarker.o RESearch.o RunStyles.o \ +	ScintRes.o PlatWin.o PositionCache.o KeyMap.o Indicator.o LineMarker.o RESearch.o RunStyles.o \  	Style.o ViewStyle.o AutoComplete.o UniConversion.o PropSet.o XPM.o  $(COMPONENT): $(SOBJS) Scintilla.def  	$(DLLWRAP) --add-stdcall-alias --target i386-mingw32 -o $@ $(SOBJS) $(LDFLAGS) -s --relocatable  LOBJS	= ScintillaWinL.o ScintillaBaseL.o Editor.o CharClassify.o Decoration.o \  	Document.o ContractionState.o CellBuffer.o CallTip.o \ -	ScintRes.o PlatWin.o KeyMap.o Indicator.o LineMarker.o RESearch.o RunStyles.o \ +	ScintRes.o PlatWin.o PositionCache.o KeyMap.o Indicator.o LineMarker.o RESearch.o RunStyles.o \  	Style.o ViewStyle.o AutoComplete.o UniConversion.o KeyWords.o \  	DocumentAccessor.o PropSet.o ExternalLexer.o StyleContext.o XPM.o $(LEXOBJS)  $(LEXCOMPONENT): $(LOBJS) Scintilla.def diff --git a/win32/scintilla.mak b/win32/scintilla.mak index da3c506bd..b6b4749ef 100644 --- a/win32/scintilla.mak +++ b/win32/scintilla.mak @@ -99,6 +99,7 @@ SOBJS=\  	$(DIR_O)\KeyMap.obj \  	$(DIR_O)\LineMarker.obj \  	$(DIR_O)\PlatWin.obj \ +	$(DIR_O)\PositionCache.obj \  	$(DIR_O)\PropSet.obj \  	$(DIR_O)\RESearch.obj \  	$(DIR_O)\RunStyles.obj \ @@ -193,6 +194,7 @@ LOBJS=\  	$(DIR_O)\KeyWords.obj \  	$(DIR_O)\LineMarker.obj \  	$(DIR_O)\PlatWin.obj \ +	$(DIR_O)\PositionCache.obj \  	$(DIR_O)\PropSet.obj \  	$(DIR_O)\RESearch.obj \  	$(DIR_O)\RunStyles.obj \ @@ -280,7 +282,7 @@ $(DIR_O)\Editor.obj: ../src/Editor.cxx ../include/Platform.h ../include/Scintill    ../src/Partitioning.h ../src/CellBuffer.h ../src/KeyMap.h \    ../src/RunStyles.h ../src/Indicator.h ../src/XPM.h ../src/LineMarker.h \    ../src/Style.h ../src/ViewStyle.h ../src/CharClassify.h \ -  ../src/Decoration.h ../src/Document.h ../src/Editor.h +  ../src/Decoration.h ../src/Document.h ../src/Editor.h ../src/PositionCache.h  $(DIR_O)\ExternalLexer.obj: ../src/ExternalLexer.cxx ../include/Platform.h \    ../include/Scintilla.h ../include/SciLexer.h ../include/PropSet.h \    ../include/SString.h ../include/Accessor.h ../src/DocumentAccessor.h \ @@ -427,6 +429,12 @@ $(DIR_O)\LineMarker.obj: ../src/LineMarker.cxx ../include/Platform.h \    ../include/Scintilla.h ../src/XPM.h ../src/LineMarker.h  $(DIR_O)\PlatWin.obj: PlatWin.cxx ../include/Platform.h PlatformRes.h \    ../src/UniConversion.h ../src/XPM.h +$(DIR_O)\PositionCache.obj: ../src/Editor.cxx ../include/Platform.h ../include/Scintilla.h \ +  ../src/ContractionState.h ../src/SVector.h ../src/SplitVector.h \ +  ../src/Partitioning.h ../src/CellBuffer.h ../src/KeyMap.h \ +  ../src/RunStyles.h ../src/Indicator.h ../src/XPM.h ../src/LineMarker.h \ +  ../src/Style.h ../src/ViewStyle.h ../src/CharClassify.h \ +  ../src/Decoration.h ../src/Document.h ../src/Editor.h ../src/PositionCache.h  $(DIR_O)\PropSet.obj: ../src/PropSet.cxx ../include/Platform.h ../include/PropSet.h \    ../include/SString.h  $(DIR_O)\RESearch.obj: ../src/RESearch.cxx ../src/CharClassify.h ../src/RESearch.h diff --git a/win32/scintilla_vc6.mak b/win32/scintilla_vc6.mak index 2a35235e3..9b9c9b356 100644 --- a/win32/scintilla_vc6.mak +++ b/win32/scintilla_vc6.mak @@ -101,6 +101,7 @@ SOBJS=\  	$(DIR_O)\KeyMap.obj \  	$(DIR_O)\LineMarker.obj \  	$(DIR_O)\PlatWin.obj \ +	$(DIR_O)\PositionCache.obj \  	$(DIR_O)\PropSet.obj \  	$(DIR_O)\RESearch.obj \  	$(DIR_O)\RunStyles.obj \ @@ -195,6 +196,7 @@ LOBJS=\  	$(DIR_O)\KeyWords.obj \  	$(DIR_O)\LineMarker.obj \  	$(DIR_O)\PlatWin.obj \ +	$(DIR_O)\PositionCache.obj \  	$(DIR_O)\RunStyles.obj \  	$(DIR_O)\RESearch.obj \  	$(DIR_O)\PropSet.obj \  | 
