diff options
| author | nyamatongwe <devnull@localhost> | 2011-01-12 14:50:20 +1100 | 
|---|---|---|
| committer | nyamatongwe <devnull@localhost> | 2011-01-12 14:50:20 +1100 | 
| commit | e45fd1ba8cb823e77be831d6243032fbe4f44f58 (patch) | |
| tree | e001367e3d931519c0c9c0f6843c4ab4214fff06 | |
| parent | bbef58d9821b621cb58401e7dca81795e614329b (diff) | |
| download | scintilla-mirror-e45fd1ba8cb823e77be831d6243032fbe4f44f58.tar.gz | |
Complete folding for CASE WHEN THEN. Feature #3135027.
Code structure updated to new style matching C++ lexer.
From Jérôme LAFORGE.
| -rw-r--r-- | doc/ScintillaHistory.html | 1 | ||||
| -rw-r--r-- | lexers/LexSQL.cxx | 485 | 
2 files changed, 403 insertions, 83 deletions
| diff --git a/doc/ScintillaHistory.html b/doc/ScintillaHistory.html index 0f698d635..eea01cabd 100644 --- a/doc/ScintillaHistory.html +++ b/doc/ScintillaHistory.html @@ -362,6 +362,7 @@  	<td>Martial Demolins</td>        </tr><tr>  	<td>Tino Weinkauf</td> +	<td>Jérôme Laforge</td>      </tr>      </table>      <p> diff --git a/lexers/LexSQL.cxx b/lexers/LexSQL.cxx index 9a5dc901d..927810e04 100644 --- a/lexers/LexSQL.cxx +++ b/lexers/LexSQL.cxx @@ -2,7 +2,7 @@  /** @file LexSQL.cxx   ** Lexer for SQL, including PL/SQL and SQL*Plus.   **/ -// Copyright 1998-2010 by Neil Hodgson <neilh@scintilla.org> +// Copyright 1998-2011 by Neil Hodgson <neilh@scintilla.org>  // The License.txt file describes the conditions under which this software may be distributed.  #include <stdlib.h> @@ -12,6 +12,15 @@  #include <assert.h>  #include <ctype.h> +#ifdef _MSC_VER +#pragma warning(disable: 4786) +#endif + +#include <string> +#include <vector> +#include <map> +#include <algorithm> +  #include "ILexer.h"  #include "Scintilla.h"  #include "SciLexer.h" @@ -23,6 +32,7 @@  #include "StyleContext.h"  #include "CharacterSet.h"  #include "LexerModule.h" +#include "OptionSet.h"  #ifdef SCI_NAMESPACE  using namespace Scintilla; @@ -47,36 +57,288 @@ static inline bool IsANumberChar(int ch) {  	// Not exactly following number definition (several dots are seen as OK, etc.)  	// but probably enough in most cases.  	return (ch < 0x80) && -	        (isdigit(ch) || toupper(ch) == 'E' || -             ch == '.' || ch == '-' || ch == '+'); +	       (isdigit(ch) || toupper(ch) == 'E' || +	        ch == '.' || ch == '-' || ch == '+');  } -static void ColouriseSQLDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[], -                            Accessor &styler) { -	WordList &keywords1 = *keywordlists[0]; -	WordList &keywords2 = *keywordlists[1]; -	WordList &kw_pldoc = *keywordlists[2]; -	WordList &kw_sqlplus = *keywordlists[3]; -	WordList &kw_user1 = *keywordlists[4]; -	WordList &kw_user2 = *keywordlists[5]; -	WordList &kw_user3 = *keywordlists[6]; -	WordList &kw_user4 = *keywordlists[7]; +class SQLStates { +public : +	void Set(int lineNumber, unsigned short int sqlStatesLine) { +		if (!sqlStatement.size() == 0 || !sqlStatesLine == 0) { +			sqlStatement.resize(lineNumber + 1, 0); +			sqlStatement[lineNumber] = sqlStatesLine; +		} +	} + +	unsigned short int IgnoreWhen (unsigned short int sqlStatesLine, bool enable) { +		if (enable) +			sqlStatesLine |= MASK_IGNORE_WHEN; +		else +			sqlStatesLine &= ~MASK_IGNORE_WHEN; + +		return sqlStatesLine; +	} -	StyleContext sc(startPos, length, initStyle, styler); +	unsigned short int IntoCondition (unsigned short int sqlStatesLine, bool enable) { +		if (enable) +			sqlStatesLine |= MASK_INTO_CONDITION; +		else +			sqlStatesLine &= ~MASK_INTO_CONDITION; + +		return sqlStatesLine; +	} + +	unsigned short int IntoExceptionBlock (unsigned short int sqlStatesLine, bool enable) { +		if (enable) +			sqlStatesLine |= MASK_INTO_EXCEPTION; +		else +			sqlStatesLine &= ~MASK_INTO_EXCEPTION; + +		return sqlStatesLine; +	} + +	unsigned short int IntoDeclareBlock (unsigned short int sqlStatesLine, bool enable) { +		if (enable) +			sqlStatesLine |= MASK_INTO_DECLARE; +		else +			sqlStatesLine &= ~MASK_INTO_DECLARE; + +		return sqlStatesLine; +	} + +	unsigned short int BeginCaseBlock (unsigned short int sqlStatesLine) { +		if ((sqlStatesLine & MASK_NESTED_CASES) < MASK_NESTED_CASES) { +			sqlStatesLine++; +		} +		return sqlStatesLine; +	} + +	unsigned short int EndCaseBlock (unsigned short int sqlStatesLine) { +		if ((sqlStatesLine & MASK_NESTED_CASES) > 0) { +			sqlStatesLine--; +		} +		return sqlStatesLine; +	} + +	bool IsIgnoreWhen (unsigned short int sqlStatesLine) { +		return (sqlStatesLine & MASK_IGNORE_WHEN) != 0; +	} + +	bool IsIntoCondition (unsigned short int sqlStatesLine) { +		return (sqlStatesLine & MASK_INTO_CONDITION) != 0; +	} + +	bool IsIntoCaseBlock (unsigned short int sqlStatesLine) { +		return (sqlStatesLine & MASK_NESTED_CASES) != 0; +	} + +	bool IsIntoExceptionBlock (unsigned short int sqlStatesLine) { +		return (sqlStatesLine & MASK_INTO_EXCEPTION) != 0; +	} + +	bool IsIntoDeclareBlock (unsigned short int sqlStatesLine) { +		return (sqlStatesLine & MASK_INTO_DECLARE) != 0; +	} + +	unsigned short int ForLine(int lineNumber) { +		if ((lineNumber > 0) && (sqlStatement.size() > static_cast<size_t>(lineNumber))) { +			return sqlStatement[lineNumber]; +		} else { +			return 0; +		} +	} + +	SQLStates() {} -	// property sql.backslash.escapes -	//	Enables backslash as an escape character in SQL. -	bool sqlBackslashEscapes = styler.GetPropertyInt("sql.backslash.escapes", 0) != 0; +private : +	std::vector <unsigned short int> sqlStatement; +	enum { +		MASK_INTO_DECLARE = 0x1000, +		MASK_INTO_EXCEPTION = 0x2000, +		MASK_INTO_CONDITION = 0x4000, +		MASK_IGNORE_WHEN = 0x8000, +		MASK_NESTED_CASES = 0x0FFF +	}; +}; + +// Options used for LexerSQL +struct OptionsSQL { +	bool fold; +	bool foldAtElse; +	bool foldComment; +	bool foldCompact; +	bool foldOnlyBegin; +	bool foldSqlExists; +	bool sqlBackticksIdentifier; +	bool sqlNumbersignComment; +	bool sqlBackslashEscapes; +	OptionsSQL() { +		fold = false; +		foldAtElse = false; +		foldComment = false; +		foldCompact = false; +		foldOnlyBegin = false; +		foldSqlExists = false; +		sqlBackticksIdentifier = false; +		sqlNumbersignComment = false; +		sqlBackslashEscapes = false; +	} +}; + +static const char * const sqlWordListDesc[] = { +	"Keywords", +	"Database Objects", +	"PLDoc", +	"SQL*Plus", +	"User Keywords 1", +	"User Keywords 2", +	"User Keywords 3", +	"User Keywords 4", +	0 +}; + +struct OptionSetSQL : public OptionSet<OptionsSQL> { +	OptionSetSQL() { +		DefineProperty("fold", &OptionsSQL::fold); + +		DefineProperty("lexer.sql.fold.at.else", &OptionsSQL::foldAtElse, +		               "This option enables SQL folding on a \"ELSE\" and \"ELSIF\"line of an IF statement."); + +		DefineProperty("fold.comment", &OptionsSQL::foldComment); -	bool sqlBackticksIdentifier = styler.GetPropertyInt("lexer.sql.backticks.identifier", 0) != 0; +		DefineProperty("fold.compact", &OptionsSQL::foldCompact); -	// property lexer.sql.numbersign.comment -	//  If "lexer.sql.numbersign.comment" property is set to 0 a line beginning with '#' will not be a comment. -	bool sqlNumbersignComment = styler.GetPropertyInt("lexer.sql.numbersign.comment", 1) != 0; +		DefineProperty("fold.sql.only.begin", &OptionsSQL::foldOnlyBegin); + +		DefineProperty("fold.sql.exists", &OptionsSQL::foldSqlExists, +		               "Enables \"EXISTS\" to end a fold as is started by \"IF\" in \"DROP TABLE IF EXISTS\"."); + +		DefineProperty("lexer.sql.backticks.identifier", &OptionsSQL::sqlBackticksIdentifier); + +		DefineProperty("lexer.sql.numbersign.comment", &OptionsSQL::sqlNumbersignComment, +		               "If \"lexer.sql.numbersign.comment\" property is set to 0 a line beginning with '#' will not be a comment."); + +		DefineProperty("sql.backslash.escapes", &OptionsSQL::sqlBackslashEscapes, +		               "Enables backslash as an escape character in SQL."); + +		DefineWordListSets(sqlWordListDesc); +	} +}; + +class LexerSQL : public ILexer { +public : +	LexerSQL() {} + +	int SCI_METHOD Version () const { +		return lvOriginal; +	} + +	void SCI_METHOD Release() { +		delete this; +	} + +	const char * SCI_METHOD PropertyNames() { +		return osSQL.PropertyNames(); +	} + +	int SCI_METHOD PropertyType(const char *name) { +		return osSQL.PropertyType(name); +	} + +	const char * SCI_METHOD DescribeProperty(const char *name) { +		return osSQL.DescribeProperty(name); +	} +	int SCI_METHOD PropertySet(const char *key, const char *val) { +		if (osSQL.PropertySet(&options, key, val)) { +			return 0; +		} +		return -1; +	} + +	const char * SCI_METHOD DescribeWordListSets() { +		return osSQL.DescribeWordListSets(); +	} + +	int SCI_METHOD WordListSet(int n, const char *wl); +	void SCI_METHOD Lex (unsigned int startPos, int lengthDoc, int initStyle, IDocument *pAccess); +	void SCI_METHOD Fold(unsigned int startPos, int lengthDoc, int initStyle, IDocument *pAccess); + +	void * SCI_METHOD PrivateCall(int, void *) { +		return 0; +	} + +	static ILexer *LexerFactorySQL() { +		return new LexerSQL(); +	} +private: +	bool IsStreamCommentStyle(int style) { +		return style == SCE_SQL_COMMENT || +		       style == SCE_SQL_COMMENTDOC || +		       style == SCE_SQL_COMMENTDOCKEYWORD || +		       style == SCE_SQL_COMMENTDOCKEYWORDERROR; +	} + +	OptionsSQL options; +	OptionSetSQL osSQL; +	SQLStates sqlStates; + +	WordList keywords1; +	WordList keywords2; +	WordList kw_pldoc; +	WordList kw_sqlplus; +	WordList kw_user1; +	WordList kw_user2; +	WordList kw_user3; +	WordList kw_user4; +}; + +int SCI_METHOD LexerSQL::WordListSet(int n, const char *wl) { +	WordList *wordListN = 0; +	switch (n) { +	case 0: +		wordListN = &keywords1; +		break; +	case 1: +		wordListN = &keywords2; +		break; +	case 2: +		wordListN = &kw_pldoc; +		break; +	case 3: +		wordListN = &kw_sqlplus; +		break; +	case 4: +		wordListN = &kw_user1; +		break; +	case 5: +		wordListN = &kw_user2; +		break; +	case 6: +		wordListN = &kw_user3; +		break; +	case 7: +		wordListN = &kw_user4; +	} +	int firstModification = -1; +	if (wordListN) { +		WordList wlNew; +		wlNew.Set(wl); +		if (*wordListN != wlNew) { +			wordListN->Set(wl); +			firstModification = 0; +		} +	} +	return firstModification; +} + +void SCI_METHOD LexerSQL::Lex(unsigned int startPos, int length, int initStyle, IDocument *pAccess) { +	LexAccessor styler(pAccess); +	StyleContext sc(startPos, length, initStyle, styler);  	int styleBeforeDCKeyword = SCE_SQL_DEFAULT; -	for (; sc.More(); sc.Forward()) { +	int offset = 0; +	for (; sc.More(); sc.Forward(), offset++) {  		// Determine if the current state should terminate.  		switch (sc.state) {  		case SCE_SQL_OPERATOR: @@ -166,7 +428,7 @@ static void ColouriseSQLDoc(unsigned int startPos, int length, int initStyle, Wo  			}  			break;  		case SCE_SQL_CHARACTER: -			if (sqlBackslashEscapes && sc.ch == '\\') { +			if (options.sqlBackslashEscapes && sc.ch == '\\') {  				sc.Forward();  			} else if (sc.ch == '\'') {  				if (sc.chNext == '\"') { @@ -196,7 +458,7 @@ static void ColouriseSQLDoc(unsigned int startPos, int length, int initStyle, Wo  				sc.SetState(SCE_SQL_NUMBER);  			} else if (IsAWordStart(sc.ch)) {  				sc.SetState(SCE_SQL_IDENTIFIER); -			} else if (sc.ch == 0x60 && sqlBackticksIdentifier) { +			} else if (sc.ch == 0x60 && options.sqlBackticksIdentifier) {  				sc.SetState(SCE_SQL_QUOTEDIDENTIFIER);  			} else if (sc.Match('/', '*')) {  				if (sc.Match("/**") || sc.Match("/*!")) {	// Support of Doxygen doc. style @@ -209,9 +471,9 @@ static void ColouriseSQLDoc(unsigned int startPos, int length, int initStyle, Wo  				// MySQL requires a space or control char after --  				// http://dev.mysql.com/doc/mysql/en/ansi-diff-comments.html  				// Perhaps we should enforce that with proper property: -//~ 			} else if (sc.Match("-- ")) { +				//~ 			} else if (sc.Match("-- ")) {  				sc.SetState(SCE_SQL_COMMENTLINE); -			} else if (sc.ch == '#' && sqlNumbersignComment) { +			} else if (sc.ch == '#' && options.sqlNumbersignComment) {  				sc.SetState(SCE_SQL_COMMENTLINEDOC);  			} else if (sc.ch == '\'') {  				sc.SetState(SCE_SQL_CHARACTER); @@ -225,26 +487,10 @@ static void ColouriseSQLDoc(unsigned int startPos, int length, int initStyle, Wo  	sc.Complete();  } -static bool IsStreamCommentStyle(int style) { -	return style == SCE_SQL_COMMENT || -	       style == SCE_SQL_COMMENTDOC || -	       style == SCE_SQL_COMMENTDOCKEYWORD || -	       style == SCE_SQL_COMMENTDOCKEYWORDERROR; -} - -// Store both the current line's fold level and the next lines in the -// level store to make it easy to pick up with each increment. -static void FoldSQLDoc(unsigned int startPos, int length, int initStyle, -                            WordList *[], Accessor &styler) { -	bool foldComment = styler.GetPropertyInt("fold.comment") != 0; -	bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0; -	bool foldOnlyBegin = styler.GetPropertyInt("fold.sql.only.begin", 0) != 0; -	bool foldAtElse = styler.GetPropertyInt("fold.at.else", 0) != 0; - -	// property fold.sql.exists -	//	Enables "EXISTS" to end a fold as is started by "IF" in "DROP TABLE IF EXISTS". -	bool foldSqlExists = styler.GetPropertyInt("fold.sql.exists", 1) != 0; - +void SCI_METHOD LexerSQL::Fold(unsigned int startPos, int length, int initStyle, IDocument *pAccess) { +	if (!options.fold) +		return; +	LexAccessor styler(pAccess);  	unsigned int endPos = startPos + length;  	int visibleChars = 0;  	int lineCurrent = styler.GetLine(startPos); @@ -261,6 +507,10 @@ static void FoldSQLDoc(unsigned int startPos, int length, int initStyle,  	// this statementFound flag avoids to fold when the statement is on only one line by ignoring ELSE or ELSIF  	// eg. "IF condition1 THEN ... ELSIF condition2 THEN ... ELSE ... END IF;"  	bool statementFound = false; +	unsigned short int sqlStatesCurrentLine = 0; +	if (!options.foldOnlyBegin) { +		sqlStatesCurrentLine = sqlStates.ForLine(lineCurrent); +	}  	for (unsigned int i = startPos; i < endPos; i++) {  		char ch = chNext;  		chNext = styler.SafeGetCharAt(i + 1); @@ -269,12 +519,15 @@ static void FoldSQLDoc(unsigned int startPos, int length, int initStyle,  		styleNext = styler.StyleAt(i + 1);  		bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');  		if (atEOL || (ch == ';')) { +			if (endFound) { +				//Maybe this is the end of "EXCEPTION" BLOCK (eg. "BEGIN ... EXCEPTION ... END;") +				sqlStatesCurrentLine = sqlStates.IntoExceptionBlock(sqlStatesCurrentLine, false); +			}  			// set endFound and isUnfoldingIgnored to false if EOL is reached or ';' is found  			endFound = false;  			isUnfoldingIgnored = false;  		} - -		if (foldComment && IsStreamCommentStyle(style)) { +		if (options.foldComment && IsStreamCommentStyle(style)) {  			if (!IsStreamCommentStyle(stylePrev)) {  				levelNext++;  			} else if (!IsStreamCommentStyle(styleNext) && !atEOL) { @@ -282,7 +535,7 @@ static void FoldSQLDoc(unsigned int startPos, int length, int initStyle,  				levelNext--;  			}  		} -		if (foldComment && (style == SCE_SQL_COMMENTLINE)) { +		if (options.foldComment && (style == SCE_SQL_COMMENTLINE)) {  			// MySQL needs -- comments to be followed by space or control char  			if ((ch == '-') && (chNext == '-')) {  				char chNext2 = styler.SafeGetCharAt(i + 2); @@ -299,11 +552,13 @@ static void FoldSQLDoc(unsigned int startPos, int length, int initStyle,  				levelNext++;  			} else if (ch == ')') {  				levelNext--; +			} else if ((!options.foldOnlyBegin) && ch == ';') { +				sqlStatesCurrentLine = sqlStates.IgnoreWhen(sqlStatesCurrentLine, false);  			}  		}  		// If new keyword (cannot trigger on elseif or nullif, does less tests)  		if (style == SCE_SQL_WORD && stylePrev != SCE_SQL_WORD) { -			const int MAX_KW_LEN = 6;	// Maximum length of folding keywords +			const int MAX_KW_LEN = 9;	// Maximum length of folding keywords  			char s[MAX_KW_LEN + 2];  			unsigned int j = 0;  			for (; j < MAX_KW_LEN + 1; j++) { @@ -318,57 +573,131 @@ static void FoldSQLDoc(unsigned int startPos, int length, int initStyle,  			} else {  				s[j] = '\0';  			} -			if (strcmp(s, "if") == 0 || -				strcmp(s, "loop") == 0 || -				strcmp(s, "case") == 0) { + +			if (strcmp(s, "if") == 0) {  				if (endFound) {  					endFound = false; -					if (foldOnlyBegin && !isUnfoldingIgnored) { +					if (options.foldOnlyBegin && !isUnfoldingIgnored) {  						// this end isn't for begin block, but for if block ("end if;") -						// or loop block ("end loop;") or case block ("end case;")  						// so ignore previous "end" by increment levelNext.  						levelNext++;  					} -				} else if (!foldOnlyBegin) { +				} else { +					if (!options.foldOnlyBegin) +						sqlStatesCurrentLine = sqlStates.IntoCondition(sqlStatesCurrentLine, true); +					if (levelCurrent > levelNext) { +						// doesn't include this line into the folding block +						// because doesn't hide IF (eg "END; IF") +						levelCurrent = levelNext; +					} +				} +			} else if (!options.foldOnlyBegin && +			           strcmp(s, "then") == 0 && +			           sqlStates.IsIntoCondition(sqlStatesCurrentLine)) { +				sqlStatesCurrentLine = sqlStates.IntoCondition(sqlStatesCurrentLine, false); +				if (!options.foldOnlyBegin) { +					if (levelCurrent > levelNext) { +						levelCurrent = levelNext; +					} +					if (!statementFound) +						levelNext++; +  					statementFound = true; +				} else if (levelCurrent > levelNext) { +					// doesn't include this line into the folding block +					// because doesn't hide LOOP or CASE (eg "END; LOOP" or "END; CASE") +					levelCurrent = levelNext; +				} +			} else if (strcmp(s, "loop") == 0 || +			           strcmp(s, "case") == 0) { +				if (endFound) { +					endFound = false; +					if (options.foldOnlyBegin && !isUnfoldingIgnored) { +						// this end isn't for begin block, but for loop block ("end loop;") or case block ("end case;") +						// so ignore previous "end" by increment levelNext. +						levelNext++; +					} +					if ((!options.foldOnlyBegin) && strcmp(s, "case") == 0) { +						sqlStatesCurrentLine = sqlStates.EndCaseBlock(sqlStatesCurrentLine); +						levelNext--; //again for the "end case;" and block when +					} +				} else if (!options.foldOnlyBegin) { +					if (strcmp(s, "case") == 0) { +						sqlStatesCurrentLine = sqlStates.BeginCaseBlock(sqlStatesCurrentLine); + +						//for case block increment 2 times +						if (!statementFound) +							levelNext++; +					} +  					if (levelCurrent > levelNext) {  						levelCurrent = levelNext;  					} -					levelNext++; +					if (!statementFound) +						levelNext++; + +					statementFound = true;  				} else if (levelCurrent > levelNext) {  					// doesn't include this line into the folding block -					// because doesn't hide IF, LOOP or CASE (eg "END; IF" or "END; LOOP" or "END; CASE") +					// because doesn't hide LOOP or CASE (eg "END; LOOP" or "END; CASE")  					levelCurrent = levelNext;  				} -			} else if ((!foldOnlyBegin) && ( -				// folding for ELSE and ELSIF block only if foldAtElse is set -				// and IF or CASE aren't on only one line with ELSE or ELSIF (with flag statementFound) -				foldAtElse && !statementFound && (strcmp(s, "elsif") == 0 || strcmp(s, "else") == 0))) { -				// prevent also ELSIF and ELSE are on the same line (eg. "ELSIF condition2 THEN ... ELSE ... END IF;") +			} else if ((!options.foldOnlyBegin) && ( +			               // folding for ELSE and ELSIF block only if foldAtElse is set +			               // and IF or CASE aren't on only one line with ELSE or ELSIF (with flag statementFound) +			               options.foldAtElse && !statementFound) && strcmp(s, "elsif") == 0) { +				sqlStatesCurrentLine = sqlStates.IntoCondition(sqlStatesCurrentLine, true); +				levelCurrent--; +				levelNext--; +			} else if ((!options.foldOnlyBegin) && ( +			               // folding for ELSE and ELSIF block only if foldAtElse is set +			               // and IF or CASE aren't on only one line with ELSE or ELSIF (with flag statementFound) +			               options.foldAtElse && !statementFound) && strcmp(s, "else") == 0) { +				// prevent also ELSE is on the same line (eg. "ELSE ... END IF;")  				statementFound = true;  				// we are in same case "} ELSE {" in C language  				levelCurrent--; +  			} else if (strcmp(s, "begin") == 0) {  				levelNext++; +				sqlStatesCurrentLine = sqlStates.IntoDeclareBlock(sqlStatesCurrentLine, false);  			} else if ((strcmp(s, "end") == 0) || -//						// DROP TABLE IF EXISTS or CREATE TABLE IF NOT EXISTS -						(foldSqlExists && (strcmp(s, "exists") == 0)) || -//						//  SQL Anywhere permits IF ... ELSE ... ENDIF -//						//      will only be active if "endif" appears in the -//						//		keyword list. -						(strcmp(s, "endif") == 0)) { +			           // DROP TABLE IF EXISTS or CREATE TABLE IF NOT EXISTS +			           (options.foldSqlExists && (strcmp(s, "exists") == 0)) || +			           //  SQL Anywhere permits IF ... ELSE ... ENDIF +			           //      will only be active if "endif" appears in the +			           //		keyword list. +			           (strcmp(s, "endif") == 0)) {  				endFound = true;  				levelNext--;  				if (levelNext < SC_FOLDLEVELBASE) {  					levelNext = SC_FOLDLEVELBASE;  					isUnfoldingIgnored = true;  				} +			} else if ((!options.foldOnlyBegin) && +			           strcmp(s, "when") == 0 && +			           !sqlStates.IsIgnoreWhen(sqlStatesCurrentLine) && +			           !sqlStates.IsIntoExceptionBlock(sqlStatesCurrentLine) && +			           sqlStates.IsIntoCaseBlock(sqlStatesCurrentLine)) { +				sqlStatesCurrentLine = sqlStates.IntoCondition(sqlStatesCurrentLine, true); + +				// Don't foldind when CASE and WHEN are on the same line (with flag statementFound) (eg. "CASE selector WHEN expression1 THEN sequence_of_statements1;\n") +				if (!statementFound) { +					levelCurrent--; +					levelNext--; +				} +			} else if ((!options.foldOnlyBegin) && strcmp(s, "exit") == 0) { +				sqlStatesCurrentLine = sqlStates.IgnoreWhen(sqlStatesCurrentLine, true); +			} else if ((!options.foldOnlyBegin) && !sqlStates.IsIntoDeclareBlock(sqlStatesCurrentLine) && strcmp(s, "exception") == 0) { +				sqlStatesCurrentLine = sqlStates.IntoExceptionBlock(sqlStatesCurrentLine, true); +			} else if ((!options.foldOnlyBegin) && strcmp (s, "declare") == 0) { +				sqlStatesCurrentLine = sqlStates.IntoDeclareBlock(sqlStatesCurrentLine, true);  			}  		}  		if (atEOL) {  			int levelUse = levelCurrent;  			int lev = levelUse | levelNext << 16; -			if (visibleChars == 0 && foldCompact) +			if (visibleChars == 0 && options.foldCompact)  				lev |= SC_FOLDLEVELWHITEFLAG;  			if (levelUse < levelNext)  				lev |= SC_FOLDLEVELHEADERFLAG; @@ -379,6 +708,8 @@ static void FoldSQLDoc(unsigned int startPos, int length, int initStyle,  			levelCurrent = levelNext;  			visibleChars = 0;  			statementFound = false; +			if (!options.foldOnlyBegin) +				sqlStates.Set(lineCurrent, sqlStatesCurrentLine);  		}  		if (!isspacechar(ch)) {  			visibleChars++; @@ -386,16 +717,4 @@ static void FoldSQLDoc(unsigned int startPos, int length, int initStyle,  	}  } -static const char * const sqlWordListDesc[] = { -	"Keywords", -	"Database Objects", -	"PLDoc", -	"SQL*Plus", -	"User Keywords 1", -	"User Keywords 2", -	"User Keywords 3", -	"User Keywords 4", -	0 -}; - -LexerModule lmSQL(SCLEX_SQL, ColouriseSQLDoc, "sql", FoldSQLDoc, sqlWordListDesc); +LexerModule lmSQL(SCLEX_SQL, LexerSQL::LexerFactorySQL, "sql", sqlWordListDesc); | 
