diff options
| author | nyamatongwe <devnull@localhost> | 2008-11-27 22:42:51 +0000 | 
|---|---|---|
| committer | nyamatongwe <devnull@localhost> | 2008-11-27 22:42:51 +0000 | 
| commit | d126e53e25d55c60b72f6ee213548c7ec28f380b (patch) | |
| tree | 127643fa8ce62011f083aa31d7bc6faf68455c3f /src/LexPascal.cxx | |
| parent | da07ed4754fa7b1e4f40c768e6653a066d1787f3 (diff) | |
| download | scintilla-mirror-d126e53e25d55c60b72f6ee213548c7ec28f380b.tar.gz | |
Better Pascal lexer from Marko Njezic FR #2190650.
Diffstat (limited to 'src/LexPascal.cxx')
| -rw-r--r-- | src/LexPascal.cxx | 699 | 
1 files changed, 459 insertions, 240 deletions
| diff --git a/src/LexPascal.cxx b/src/LexPascal.cxx index 0e4576064..2cf1b40f2 100644 --- a/src/LexPascal.cxx +++ b/src/LexPascal.cxx @@ -4,8 +4,109 @@   ** Written by Laurent le Tynevez   ** Updated by Simon Steele <s.steele@pnotepad.org> September 2002   ** Updated by Mathias Rauen <scite@madshi.net> May 2003 (Delphi adjustments) + ** Completely rewritten by Marko Njezic <sf@maxempire.com> October 2008   **/ +/* + +A few words about features of the new completely rewritten LexPascal... + +Generally speaking LexPascal tries to support all available Delphi features (up  +to Delphi 2009 at this time), including .NET specific features. + +~ HIGHLIGHTING: + +If you enable "lexer.pascal.smart.highlighting" property, some keywords will  +only be highlighted in appropriate context. As implemented those are keywords  +related to property and DLL exports declarations (similar to how Delphi IDE  +works).  + +For example, keywords "read" and "write" will only be highlighted if they are in  +property declaration: + +property MyProperty: boolean read FMyProperty write FMyProperty;  + +~ FOLDING: + +Folding is supported in the following cases: + +- Folding of stream-like comments +- Folding of groups of consecutive line comments +- Folding of preprocessor blocks (the following preprocessor blocks are  +supported: IF / IFEND; IFDEF, IFNDEF, IFOPT / ENDIF and REGION / ENDREGION  +blocks), including nesting of preprocessor blocks up to 255 levels +- Folding of code blocks on appropriate keywords (the following code blocks are  +supported: "begin, asm, record, try, case / end" blocks, class & object  +declarations and interface declarations) + +Remarks: + +- Folding of code blocks tries to handle all special cases in which folding  +should not occur. As implemented those are: + +1. Structure "record case / end" (there's only one "end" statement and "case" is  +ignored as fold point) +2. Forward class declarations ("type TMyClass = class;") and object method  +declarations ("TNotifyEvent = procedure(Sender: TObject) of object;") are  +ignored as fold points +3. Simplified complete class declarations ("type TMyClass = class(TObject);")  +are ignored as fold points +4. Every other situation when class keyword doesn't actually start class  +declaration ("class procedure", "class function", "class of", "class var",  +"class property" and "class operator") + +- Folding of code blocks inside preprocessor blocks is disabled (any comments  +inside them will be folded fine) because there is no guarantee that complete  +code block will be contained inside folded preprocessor block in which case  +folded code block could end prematurely at the end of preprocessor block if  +there is no closing statement inside. This was done in order to properly process  +document that may contain something like this: + +type +{$IFDEF UNICODE} +  TMyClass = class(UnicodeAncestor) +{$ELSE} +  TMyClass = class(AnsiAncestor) +{$ENDIF} +  private +  ... +  public +  ... +  published +  ... +end; + +If class declarations were folded, then the second class declaration would end  +at "$ENDIF" statement, first class statement would end at "end;" statement and  +preprocessor "$IFDEF" block would go all the way to the end of document.  +However, having in mind all this, if you want to enable folding of code blocks  +inside preprocessor blocks, you can disable folding of preprocessor blocks by  +changing "fold.preprocessor" property, in which case everything inside them  +would be folded. + +~ KEYWORDS: + +The list of keywords that can be used in pascal.properties file (up to Delphi  +2009): + +- Keywords: absolute abstract and array as asm assembler automated begin case  +cdecl class const constructor deprecated destructor dispid dispinterface div do  +downto dynamic else end except export exports external far file final  +finalization finally for forward function goto if implementation in inherited  +initialization inline interface is label library message mod near nil not object  +of on or out overload override packed pascal platform private procedure program  +property protected public published raise record register reintroduce repeat  +resourcestring safecall sealed set shl shr static stdcall strict string then  +threadvar to try type unit unsafe until uses var varargs virtual while with xor + +- Keywords related to the "smart highlithing" feature: add default implements  +index name nodefault read readonly remove stored write writeonly + +- Keywords related to Delphi packages (in addition to all above): package  +contains requires + +*/ +  #include <stdlib.h>  #include <string.h>  #include <ctype.h> @@ -20,12 +121,13 @@  #include "Scintilla.h"  #include "SciLexer.h"  #include "StyleContext.h" +#include "CharacterSet.h"  #ifdef SCI_NAMESPACE  using namespace Scintilla;  #endif -static void getRange(unsigned int start, +static void GetRangeLowered(unsigned int start,  		unsigned int end,  		Accessor &styler,  		char *s, @@ -38,239 +140,361 @@ static void getRange(unsigned int start,  	s[i] = '\0';  } -static bool IsStreamCommentStyle(int style) { -	return style == SCE_C_COMMENT || -		style == SCE_C_COMMENTDOC || -		style == SCE_C_COMMENTDOCKEYWORD || -		style == SCE_C_COMMENTDOCKEYWORDERROR; -} +static void GetForwardRangeLowered(unsigned int start, +		CharacterSet &charSet, +		Accessor &styler, +		char *s, +		unsigned int len) { +	unsigned int i = 0; +	while ((i < len-1) && charSet.Contains(styler.SafeGetCharAt(start + i))) { +		s[i] = static_cast<char>(tolower(styler.SafeGetCharAt(start + i))); +		i++; +	} +	s[i] = '\0'; -static void ColourTo(Accessor &styler, unsigned int end, unsigned int attr, bool bInAsm) { -	if ((bInAsm) && (attr == SCE_C_OPERATOR || attr == SCE_C_NUMBER || attr == SCE_C_DEFAULT || attr == SCE_C_WORD || attr == SCE_C_IDENTIFIER)) { -		styler.ColourTo(end, SCE_C_REGEX); -	} else -		styler.ColourTo(end, attr);  } -// returns 1 if the item starts a class definition, and -1 if the word is "end", and 2 if the word is "asm" -static int classifyWordPascal(unsigned int start, unsigned int end, /*WordList &keywords*/WordList *keywordlists[], Accessor &styler, bool bInClass, bool bInAsm) { -	int ret = 0; +enum { +	stateInAsm = 0x1000,  +	stateInProperty = 0x2000,  +	stateInExport = 0x4000,  +	stateFoldInPreprocessor = 0x0100,  +	stateFoldInRecord = 0x0200,  +	stateFoldInPreprocessorLevelMask = 0x00FF,  +	stateFoldMaskAll = 0x0FFF +}; +static void ClassifyPascalWord(WordList *keywordlists[], StyleContext &sc, int &curLineState, bool bSmartHighlighting) {  	WordList& keywords = *keywordlists[0]; -	WordList& classwords = *keywordlists[1];  	char s[100]; -	getRange(start, end, styler, s, sizeof(s)); - -	char chAttr = SCE_C_IDENTIFIER; -	if (isdigit(s[0]) || (s[0] == '.') ||(s[0] == '$')) { -		chAttr = SCE_C_NUMBER; +	sc.GetCurrentLowered(s, sizeof(s)); +	if (keywords.InList(s)) { +		if (curLineState & stateInAsm) { +			if (strcmp(s, "end") == 0 && sc.GetRelative(-4) != '@') { +				curLineState &= ~stateInAsm; +				sc.ChangeState(SCE_PAS_WORD); +			} else { +				sc.ChangeState(SCE_PAS_ASM); +			} +		} else { +			bool ignoreKeyword = false; +			if (strcmp(s, "asm") == 0) { +				curLineState |= stateInAsm; +			} else if (bSmartHighlighting) { +				if (strcmp(s, "property") == 0) { +					curLineState |= stateInProperty; +				} else if (strcmp(s, "exports") == 0) { +					curLineState |= stateInExport; +				} else if (!(curLineState & (stateInProperty | stateInExport)) && strcmp(s, "index") == 0) { +					ignoreKeyword = true; +				} else if (!(curLineState & stateInExport) && strcmp(s, "name") == 0) { +					ignoreKeyword = true; +				} else if (!(curLineState & stateInProperty) &&  +					(strcmp(s, "read") == 0 || strcmp(s, "write") == 0 ||  +					 strcmp(s, "default") == 0 || strcmp(s, "nodefault") == 0 ||  +					 strcmp(s, "stored") == 0 || strcmp(s, "implements") == 0 ||  +					 strcmp(s, "readonly") == 0 || strcmp(s, "writeonly") == 0 ||  +					 strcmp(s, "add") == 0 || strcmp(s, "remove") == 0)) { +					ignoreKeyword = true; +				} +			} +			if (!ignoreKeyword) { +				sc.ChangeState(SCE_PAS_WORD); +			} +		} +	} else if (curLineState & stateInAsm) { +		sc.ChangeState(SCE_PAS_ASM);  	} -	else { -		if (s[0] == '#') { -			chAttr = SCE_C_CHARACTER; +	sc.SetState(SCE_PAS_DEFAULT); +} + +static void ColourisePascalDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[], +		Accessor &styler) { +	bool bSmartHighlighting = styler.GetPropertyInt("lexer.pascal.smart.highlighting", 1) != 0; + +	CharacterSet setWordStart(CharacterSet::setAlpha, "_", 0x80, true); +	CharacterSet setWord(CharacterSet::setAlphaNum, "_", 0x80, true); +	CharacterSet setNumber(CharacterSet::setDigits, ".-+eE"); +	CharacterSet setHexNumber(CharacterSet::setDigits, "abcdefABCDEF"); +	CharacterSet setOperator(CharacterSet::setNone, "#$&'()*+,-./:;<=>@[]^{}"); + +	int curLine = styler.GetLine(startPos); +	int curLineState = curLine > 0 ? styler.GetLineState(curLine - 1) : 0; + +	StyleContext sc(startPos, length, initStyle, styler); + +	for (; sc.More(); sc.Forward()) { +		if (sc.atLineEnd) { +			// Update the line state, so it can be seen by next line +			curLine = styler.GetLine(sc.currentPos); +			styler.SetLineState(curLine, curLineState);  		} -		else { -			if (keywords.InList(s)) { -				chAttr = SCE_C_WORD; -				if(strcmp(s, "class") == 0) { -					ret = 1; +		// Determine if the current state should terminate. +		switch (sc.state) { +			case SCE_PAS_NUMBER: +				if (!setNumber.Contains(sc.ch) || (sc.ch == '.' && sc.chNext == '.')) { +					sc.SetState(SCE_PAS_DEFAULT); +				} else if (sc.ch == '-' || sc.ch == '+') { +					if (sc.chPrev != 'E' && sc.chPrev != 'e') { +						sc.SetState(SCE_PAS_DEFAULT); +					}  				} -				else if (strcmp(s, "asm") == 0) { -					ret = 2; +				break; +			case SCE_PAS_IDENTIFIER: +				if (!setWord.Contains(sc.ch)) { +					ClassifyPascalWord(keywordlists, sc, curLineState, bSmartHighlighting);  				} -				else if (strcmp(s, "end") == 0) { -					ret = -1; +				break; +			case SCE_PAS_HEXNUMBER: +				if (!setHexNumber.Contains(sc.ch)) { +					sc.SetState(SCE_PAS_DEFAULT);  				} -			} else if (bInClass) { -				if (classwords.InList(s)) { -					chAttr = SCE_C_WORD; +				break; +			case SCE_PAS_COMMENT: +			case SCE_PAS_PREPROCESSOR: +				if (sc.ch == '}') { +					sc.ForwardSetState(SCE_PAS_DEFAULT);  				} -			} +				break; +			case SCE_PAS_COMMENT2: +			case SCE_PAS_PREPROCESSOR2: +				if (sc.Match('*', ')')) { +					sc.Forward(); +					sc.ForwardSetState(SCE_PAS_DEFAULT); +				} +				break; +			case SCE_PAS_COMMENTLINE: +				if (sc.atLineStart) { +					sc.SetState(SCE_PAS_DEFAULT); +				} +				break; +			case SCE_PAS_STRING: +				if (sc.atLineEnd) { +					sc.ChangeState(SCE_PAS_STRINGEOL); +				} else if (sc.ch == '\'' && sc.chNext == '\'') { +					sc.Forward(); +				} else if (sc.ch == '\'') { +					sc.ForwardSetState(SCE_PAS_DEFAULT); +				} +				break; +			case SCE_PAS_STRINGEOL: +				if (sc.atLineStart) { +					sc.SetState(SCE_PAS_DEFAULT); +				} +				break; +			case SCE_PAS_CHARACTER: +				if (!setHexNumber.Contains(sc.ch) && sc.ch != '$') { +					sc.SetState(SCE_PAS_DEFAULT); +				} +				break; +			case SCE_PAS_OPERATOR: +				if (bSmartHighlighting && sc.chPrev == ';') { +					curLineState &= ~(stateInProperty | stateInExport); +				} +				sc.SetState(SCE_PAS_DEFAULT); +				break; +			case SCE_PAS_ASM: +				sc.SetState(SCE_PAS_DEFAULT); +				break;  		} -	} -	ColourTo(styler, end, chAttr, (bInAsm && ret != -1)); -	return ret; -} -static int classifyFoldPointPascal(const char* s) { -	int lev = 0; -	if (!(isdigit(s[0]) || (s[0] == '.'))) { -		if (strcmp(s, "begin") == 0 || -			strcmp(s, "object") == 0 || -			strcmp(s, "case") == 0 || -			strcmp(s, "class") == 0 || -			strcmp(s, "record") == 0 || -			strcmp(s, "try") == 0) { -			lev=1; -		} else if (strcmp(s, "end") == 0) { -			lev=-1; +		// Determine if a new state should be entered. +		if (sc.state == SCE_PAS_DEFAULT) { +			if (IsADigit(sc.ch) && !(curLineState & stateInAsm)) { +				sc.SetState(SCE_PAS_NUMBER); +			} else if (setWordStart.Contains(sc.ch)) { +				sc.SetState(SCE_PAS_IDENTIFIER); +			} else if (sc.ch == '$' && !(curLineState & stateInAsm)) { +				sc.SetState(SCE_PAS_HEXNUMBER); +			} else if (sc.Match('{', '$')) { +				sc.SetState(SCE_PAS_PREPROCESSOR); +			} else if (sc.ch == '{') { +				sc.SetState(SCE_PAS_COMMENT); +			} else if (sc.Match("(*$")) { +				sc.SetState(SCE_PAS_PREPROCESSOR2); +			} else if (sc.Match('(', '*')) { +				sc.SetState(SCE_PAS_COMMENT2); +				sc.Forward();	// Eat the * so it isn't used for the end of the comment +			} else if (sc.Match('/', '/')) { +				sc.SetState(SCE_PAS_COMMENTLINE); +			} else if (sc.ch == '\'') { +				sc.SetState(SCE_PAS_STRING); +			} else if (sc.ch == '#') { +				sc.SetState(SCE_PAS_CHARACTER); +			} else if (setOperator.Contains(sc.ch) && !(curLineState & stateInAsm)) { +				sc.SetState(SCE_PAS_OPERATOR); +			} else if (curLineState & stateInAsm) { +				sc.SetState(SCE_PAS_ASM); +			}  		}  	} -	return lev; -} -static void ColourisePascalDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[], -	Accessor &styler) { - -	styler.StartAt(startPos); +	if (sc.state == SCE_PAS_IDENTIFIER && setWord.Contains(sc.chPrev)) { +		ClassifyPascalWord(keywordlists, sc, curLineState, bSmartHighlighting); +	} -	int state = initStyle; -	if (state == SCE_C_CHARACTER)	// Does not leak onto next line -		state = SCE_C_DEFAULT; -	char chPrev = ' '; -	char chNext = styler[startPos]; -	unsigned int lengthDoc = startPos + length; +	sc.Complete(); +} -	bool bInClassDefinition; +static bool IsStreamCommentStyle(int style) { +	return style == SCE_PAS_COMMENT || style == SCE_PAS_COMMENT2; +} -	int currentLine = styler.GetLine(startPos); -	if (currentLine > 0) { -		styler.SetLineState(currentLine, styler.GetLineState(currentLine-1)); -		bInClassDefinition = (styler.GetLineState(currentLine) == 1); -	} else { -		styler.SetLineState(currentLine, 0); -		bInClassDefinition = false; +static bool IsCommentLine(int line, Accessor &styler) { +	int pos = styler.LineStart(line); +	int eolPos = styler.LineStart(line + 1) - 1; +	for (int i = pos; i < eolPos; i++) { +		char ch = styler[i]; +		char chNext = styler.SafeGetCharAt(i + 1); +		int style = styler.StyleAt(i); +		if (ch == '/' && chNext == '/' && style == SCE_PAS_COMMENTLINE) { +			return true; +		} else if (!IsASpaceOrTab(ch)) { +			return false; +		}  	} +	return false; +} -	bool bInAsm = (state == SCE_C_REGEX); -	if (bInAsm) -		state = SCE_C_DEFAULT; - -	styler.StartSegment(startPos); -	for (unsigned int i = startPos; i < lengthDoc; i++) { -		char ch = chNext; +static unsigned int GetFoldInPreprocessorLevelFlag(int lineFoldStateCurrent) { +	return lineFoldStateCurrent & stateFoldInPreprocessorLevelMask; +} -		chNext = styler.SafeGetCharAt(i + 1); +static void SetFoldInPreprocessorLevelFlag(int &lineFoldStateCurrent, unsigned int nestLevel) { +	lineFoldStateCurrent &= ~stateFoldInPreprocessorLevelMask; +	lineFoldStateCurrent |= nestLevel & stateFoldInPreprocessorLevelMask; +} -		if ((ch == '\r' && chNext != '\n') || (ch == '\n')) { -			// Trigger on CR only (Mac style) or either on LF from CR+LF (Dos/Win) or on LF alone (Unix) -			// Avoid triggering two times on Dos/Win -			// End of line -			if (state == SCE_C_CHARACTER) { -				ColourTo(styler, i, state, bInAsm); -				state = SCE_C_DEFAULT; -			} -			currentLine++; -			styler.SetLineState(currentLine, (bInClassDefinition ? 1 : 0)); +static void ClassifyPascalPreprocessorFoldPoint(int &levelCurrent, int &lineFoldStateCurrent,  +		unsigned int startPos, Accessor &styler) { +	CharacterSet setWord(CharacterSet::setAlpha); + +	char s[11];	// Size of the longest possible keyword + one additional character + null +	GetForwardRangeLowered(startPos, setWord, styler, s, sizeof(s)); + +	unsigned int nestLevel = GetFoldInPreprocessorLevelFlag(lineFoldStateCurrent); + +	if (strcmp(s, "if") == 0 ||  +		strcmp(s, "ifdef") == 0 ||  +		strcmp(s, "ifndef") == 0 ||  +		strcmp(s, "ifopt") == 0 ||  +		strcmp(s, "region") == 0) { +		nestLevel++; +		SetFoldInPreprocessorLevelFlag(lineFoldStateCurrent, nestLevel); +		lineFoldStateCurrent |= stateFoldInPreprocessor; +		levelCurrent++; +	} else if (strcmp(s, "endif") == 0 ||  +		strcmp(s, "ifend") == 0 ||  +		strcmp(s, "endregion") == 0) { +		nestLevel--; +		SetFoldInPreprocessorLevelFlag(lineFoldStateCurrent, nestLevel); +		if (nestLevel == 0) { +			lineFoldStateCurrent &= ~stateFoldInPreprocessor;  		} - -		if (styler.IsLeadByte(ch)) { -			chNext = styler.SafeGetCharAt(i + 2); -			chPrev = ' '; -			i += 1; -			continue; +		levelCurrent--; +		if (levelCurrent < SC_FOLDLEVELBASE) { +			levelCurrent = SC_FOLDLEVELBASE;  		} +	} +} -		if (state == SCE_C_DEFAULT) { -			if (iswordstart(ch) || ch == '#' || ch == '$' || (ch == '@' && bInAsm)) { -				ColourTo(styler, i-1, state, bInAsm); -				state = SCE_C_IDENTIFIER; -			} else if (ch == '{' && chNext != '$' && chNext != '&') { -				ColourTo(styler, i-1, state, bInAsm); -				state = SCE_C_COMMENT; -			} else if (ch == '(' && chNext == '*' -						&& styler.SafeGetCharAt(i + 2) != '$' -						&& styler.SafeGetCharAt(i + 2) != '&') { -				ColourTo(styler, i-1, state, bInAsm); -				state = SCE_C_COMMENTDOC; -			} else if (ch == '/' && chNext == '/') { -				ColourTo(styler, i-1, state, bInAsm); -				state = SCE_C_COMMENTLINE; -			} else if (ch == '\'') { -				ColourTo(styler, i-1, state, bInAsm); -				state = SCE_C_CHARACTER; -			} else if (ch == '{' && (chNext == '$' || chNext=='&')) { -				ColourTo(styler, i-1, state, bInAsm); -				state = SCE_C_PREPROCESSOR; -			} else if (isoperator(ch)) { -				ColourTo(styler, i-1, state, bInAsm); -				ColourTo(styler, i, SCE_C_OPERATOR, bInAsm); - -			} -		} else if (state == SCE_C_IDENTIFIER) { -			bool bDoublePoint = ((ch == '.') && (chPrev == '.')); -			if ((!iswordchar(ch) && ch != '$' && ch != '#' && (ch != '@' || !bInAsm)) || bDoublePoint) { -				if (bDoublePoint) i--; -				int lStateChange = classifyWordPascal(styler.GetStartSegment(), i - 1, keywordlists, styler, bInClassDefinition, bInAsm); - -				if(lStateChange == 1) { -					styler.SetLineState(currentLine, 1); -					bInClassDefinition = true; -				} else if(lStateChange == 2) { -					bInAsm = true; -				} else if(lStateChange == -1) { -					styler.SetLineState(currentLine, 0); -					bInClassDefinition = false; -					bInAsm = false; -				} -				if (bDoublePoint) { -					i++; -					ColourTo(styler, i-1, SCE_C_DEFAULT, bInAsm); -				} +static unsigned int SkipWhiteSpace(unsigned int currentPos, unsigned int endPos,  +		Accessor &styler, bool includeChars = false) { +	CharacterSet setWord(CharacterSet::setAlphaNum, "_"); +	unsigned int j = currentPos + 1; +	char ch = styler.SafeGetCharAt(j); +	while ((j < endPos) && (IsASpaceOrTab(ch) || ch == '\r' || ch == '\n') ||  +		IsStreamCommentStyle(styler.StyleAt(j)) || (includeChars && setWord.Contains(ch))) { +		j++; +		ch = styler.SafeGetCharAt(j); +	} +	return j; +} -				state = SCE_C_DEFAULT; -				chNext = styler.SafeGetCharAt(i + 1); -				if (ch == '{' && chNext != '$' && chNext != '&') { -					state = SCE_C_COMMENT; -				} else if (ch == '(' && chNext == '*' -						&& styler.SafeGetCharAt(i + 2) != '$' -						&& styler.SafeGetCharAt(i + 2) != '&') { -					ColourTo(styler, i-1, state, bInAsm); -					state = SCE_C_COMMENTDOC; -				} else if (ch == '/' && chNext == '/') { -					state = SCE_C_COMMENTLINE; -				} else if (ch == '\'') { -					state = SCE_C_CHARACTER; -				} else if (isoperator(ch)) { -					ColourTo(styler, i, SCE_C_OPERATOR, bInAsm); -				} -			} -		} else { -			if (state == SCE_C_PREPROCESSOR) { -				if (ch=='}'){ -					ColourTo(styler, i, state, bInAsm); -					state = SCE_C_DEFAULT; -				} else { -					if ((ch == '\r' || ch == '\n') && !(chPrev == '\\' || chPrev == '\r')) { -						ColourTo(styler, i-1, state, bInAsm); -						state = SCE_C_DEFAULT; +static void ClassifyPascalWordFoldPoint(int &levelCurrent, int &lineFoldStateCurrent,  +		unsigned int startPos, unsigned int endPos,  +		unsigned int lastStart, unsigned int currentPos, Accessor &styler) { +	char s[100]; +	GetRangeLowered(lastStart, currentPos, styler, s, sizeof(s)); + +	if (strcmp(s, "record") == 0) { +		lineFoldStateCurrent |= stateFoldInRecord; +		levelCurrent++; +	} else if (strcmp(s, "begin") == 0 ||  +		strcmp(s, "asm") == 0 ||  +		strcmp(s, "try") == 0 ||  +		(strcmp(s, "case") == 0 && !(lineFoldStateCurrent & stateFoldInRecord))) { +		levelCurrent++; +	} else if (strcmp(s, "class") == 0 || strcmp(s, "object") == 0) { +		// "class" & "object" keywords require special handling... +		bool ignoreKeyword = false; +		unsigned int j = SkipWhiteSpace(currentPos, endPos, styler); +		if (j < endPos) { +			CharacterSet setWordStart(CharacterSet::setAlpha, "_"); +			CharacterSet setWord(CharacterSet::setAlphaNum, "_"); + +			if (styler.SafeGetCharAt(j) == ';') { +				// Handle forward class declarations ("type TMyClass = class;")  +				// and object method declarations ("TNotifyEvent = procedure(Sender: TObject) of object;") +				ignoreKeyword = true; +			} else if (strcmp(s, "class") == 0) { +				// "class" keyword has a few more special cases... +				if (styler.SafeGetCharAt(j) == '(') { +					// Handle simplified complete class declarations ("type TMyClass = class(TObject);")  +					j = SkipWhiteSpace(j, endPos, styler, true); +					if (j < endPos && styler.SafeGetCharAt(j) == ')') { +						j = SkipWhiteSpace(j, endPos, styler); +						if (j < endPos && styler.SafeGetCharAt(j) == ';') { +							ignoreKeyword = true; +						}  					} -				} -			} else if (state == SCE_C_COMMENT) { -				if (ch == '}' ) { -					ColourTo(styler, i, state, bInAsm); -					state = SCE_C_DEFAULT; -				} -			} else if (state == SCE_C_COMMENTDOC) { -				if (ch == ')' && chPrev == '*') { -					if (((i > styler.GetStartSegment() + 2) || ( -						(initStyle == SCE_C_COMMENTDOC) && -						(styler.GetStartSegment() == static_cast<unsigned int>(startPos))))) { -							ColourTo(styler, i, state, bInAsm); -							state = SCE_C_DEFAULT; +				} else if (setWordStart.Contains(styler.SafeGetCharAt(j))) { +					char s2[11];	// Size of the longest possible keyword + one additional character + null +					GetForwardRangeLowered(j, setWord, styler, s2, sizeof(s2)); + +					if (strcmp(s2, "procedure") == 0 ||  +						strcmp(s2, "function") == 0 ||  +						strcmp(s2, "of") == 0 ||  +						strcmp(s2, "var") == 0 ||  +						strcmp(s2, "property") == 0 ||  +						strcmp(s2, "operator") == 0) { +						ignoreKeyword = true;  					}  				} -			} else if (state == SCE_C_COMMENTLINE) { -				if (ch == '\r' || ch == '\n') { -					ColourTo(styler, i-1, state, bInAsm); -					state = SCE_C_DEFAULT; -				} -			} else if (state == SCE_C_CHARACTER) { -				if (ch == '\'') { -					ColourTo(styler, i, state, bInAsm); -					state = SCE_C_DEFAULT; -				}  			}  		} -		chPrev = ch; +		if (!ignoreKeyword) { +			levelCurrent++; +		} +	} else if (strcmp(s, "interface") == 0) { +		// "interface" keyword requires special handling... +		bool ignoreKeyword = true; +		unsigned int j = lastStart - 1; +		char ch = styler.SafeGetCharAt(j); +		while ((j >= startPos) && (IsASpaceOrTab(ch) || ch == '\r' || ch == '\n') ||  +			IsStreamCommentStyle(styler.StyleAt(j))) { +			j--; +			ch = styler.SafeGetCharAt(j); +		} +		if (j >= startPos && styler.SafeGetCharAt(j) == '=') { +			ignoreKeyword = false; +		} +		if (!ignoreKeyword) { +			levelCurrent++; +		} +	} else if (strcmp(s, "end") == 0) { +		lineFoldStateCurrent &= ~stateFoldInRecord; +		levelCurrent--; +		if (levelCurrent < SC_FOLDLEVELBASE) { +			levelCurrent = SC_FOLDLEVELBASE; +		}  	} -	ColourTo(styler, lengthDoc - 1, state, bInAsm);  }  static void FoldPascalDoc(unsigned int startPos, int length, int initStyle, WordList *[], -                            Accessor &styler) { +		Accessor &styler) {  	bool foldComment = styler.GetPropertyInt("fold.comment") != 0;  	bool foldPreprocessor = styler.GetPropertyInt("fold.preprocessor") != 0;  	bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0; @@ -279,11 +503,13 @@ static void FoldPascalDoc(unsigned int startPos, int length, int initStyle, Word  	int lineCurrent = styler.GetLine(startPos);  	int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;  	int levelCurrent = levelPrev; +	int lineFoldStateCurrent = lineCurrent > 0 ? styler.GetLineState(lineCurrent - 1) & stateFoldMaskAll : 0;  	char chNext = styler[startPos];  	int styleNext = styler.StyleAt(startPos);  	int style = initStyle;  	int lastStart = 0; +	CharacterSet setWord(CharacterSet::setAlphaNum, "_", 0x80, true);  	for (unsigned int i = startPos; i < endPos; i++) {  		char ch = chNext; @@ -293,53 +519,45 @@ static void FoldPascalDoc(unsigned int startPos, int length, int initStyle, Word  		styleNext = styler.StyleAt(i + 1);  		bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n'); -		if (stylePrev != SCE_C_WORD && style == SCE_C_WORD) +		if (foldComment && IsStreamCommentStyle(style)) { +			if (!IsStreamCommentStyle(stylePrev)) { +				levelCurrent++; +			} else if (!IsStreamCommentStyle(styleNext) && !atEOL) { +				// Comments don't end at end of line and the next character may be unstyled. +				levelCurrent--; +			} +		} +		if (foldComment && atEOL && IsCommentLine(lineCurrent, styler))  		{ -			// Store last word start point. -			lastStart = i; +			if (!IsCommentLine(lineCurrent - 1, styler) +			    && IsCommentLine(lineCurrent + 1, styler)) +				levelCurrent++; +			else if (IsCommentLine(lineCurrent - 1, styler) +			         && !IsCommentLine(lineCurrent+1, styler)) +				levelCurrent--;  		} - -		if (stylePrev == SCE_C_WORD) { -			if(iswordchar(ch) && !iswordchar(chNext)) { -				char s[100]; -				getRange(lastStart, i, styler, s, sizeof(s)); -				levelCurrent += classifyFoldPointPascal(s); +		if (foldPreprocessor) { +			if (style == SCE_PAS_PREPROCESSOR && ch == '{' && chNext == '$') { +				ClassifyPascalPreprocessorFoldPoint(levelCurrent, lineFoldStateCurrent, i + 2, styler); +			} else if (style == SCE_PAS_PREPROCESSOR2 && ch == '(' && chNext == '*'  +			           && styler.SafeGetCharAt(i + 2) == '$') { +				ClassifyPascalPreprocessorFoldPoint(levelCurrent, lineFoldStateCurrent, i + 3, styler);  			}  		} -		if (foldComment && (style == SCE_C_COMMENTLINE)) { -			if ((ch == '/') && (chNext == '/')) { -				char chNext2 = styler.SafeGetCharAt(i + 2); -				if (chNext2 == '{') { -					levelCurrent++; -				} else if (chNext2 == '}') { -					levelCurrent--; -				} -			} +		if (stylePrev != SCE_PAS_WORD && style == SCE_PAS_WORD) +		{ +			// Store last word start point. +			lastStart = i;  		} - -		if (foldPreprocessor && (style == SCE_C_PREPROCESSOR)) { -			if (ch == '{' && chNext == '$') { -				unsigned int j=i+2; // skip {$ -				while ((j<endPos) && IsASpaceOrTab(styler.SafeGetCharAt(j))) { -					j++; -				} -				if (styler.Match(j, "region") || styler.Match(j, "if")) { -					levelCurrent++; -				} else if (styler.Match(j, "end")) { -					levelCurrent--; -				} +		if (stylePrev == SCE_PAS_WORD && !(lineFoldStateCurrent & stateFoldInPreprocessor)) { +			if(setWord.Contains(ch) && !setWord.Contains(chNext)) { +				ClassifyPascalWordFoldPoint(levelCurrent, lineFoldStateCurrent, startPos, endPos, lastStart, i, styler);  			}  		} -		if (foldComment && IsStreamCommentStyle(style)) { -			if (!IsStreamCommentStyle(stylePrev)) { -				levelCurrent++; -			} else if (!IsStreamCommentStyle(styleNext) && !atEOL) { -				// Comments don't end at end of line and the next character may be unstyled. -				levelCurrent--; -			} -		} +		if (!IsASpace(ch)) +			visibleChars++;  		if (atEOL) {  			int lev = levelPrev; @@ -350,23 +568,24 @@ static void FoldPascalDoc(unsigned int startPos, int length, int initStyle, Word  			if (lev != styler.LevelAt(lineCurrent)) {  				styler.SetLevel(lineCurrent, lev);  			} +			int newLineState = (styler.GetLineState(lineCurrent) & ~stateFoldMaskAll) | lineFoldStateCurrent; +			styler.SetLineState(lineCurrent, newLineState);  			lineCurrent++;  			levelPrev = levelCurrent;  			visibleChars = 0;  		} - -		if (!isspacechar(ch)) -			visibleChars++;  	} -	// Fill in the real level of the next line, keeping the current flags as they will be filled in later -	int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK; -	styler.SetLevel(lineCurrent, levelPrev | flagsNext); +	// If we didn't reach the EOL in previous loop, store line level and whitespace information. +	// The rest will be filled in later... +	int lev = levelPrev; +	if (visibleChars == 0 && foldCompact) +		lev |= SC_FOLDLEVELWHITEFLAG; +	styler.SetLevel(lineCurrent, lev);  }  static const char * const pascalWordListDesc[] = {  	"Keywords", -	"Classwords",  	0  }; | 
