2009-04-24 23:35:41 +00:00
// Scintilla source code edit control
/** @file LexSQL.cxx
* * Lexer for SQL , including PL / SQL and SQL * Plus .
* */
2011-03-22 00:16:49 +00:00
// Copyright 1998-2011 by Neil Hodgson <neilh@scintilla.org>
2009-04-24 23:35:41 +00:00
// The License.txt file describes the conditions under which this software may be distributed.
# include <stdlib.h>
# include <string.h>
# include <stdio.h>
# include <stdarg.h>
2010-08-21 23:59:56 +00:00
# include <assert.h>
# include <ctype.h>
2009-04-24 23:35:41 +00:00
2011-03-22 00:16:49 +00:00
# ifdef _MSC_VER
# pragma warning(disable: 4786)
# endif
# include <string>
# include <vector>
# include <map>
# include <algorithm>
2010-08-21 23:59:56 +00:00
# include "ILexer.h"
# include "Scintilla.h"
# include "SciLexer.h"
2009-04-24 23:35:41 +00:00
2010-08-21 23:59:56 +00:00
# include "WordList.h"
# include "LexAccessor.h"
2009-04-24 23:35:41 +00:00
# include "Accessor.h"
# include "StyleContext.h"
2010-08-21 23:59:56 +00:00
# include "CharacterSet.h"
# include "LexerModule.h"
2011-03-22 00:16:49 +00:00
# include "OptionSet.h"
2009-04-24 23:35:41 +00:00
# ifdef SCI_NAMESPACE
using namespace Scintilla ;
# endif
2011-03-22 00:16:49 +00:00
static inline bool IsAWordChar ( int ch , bool sqlAllowDottedWord ) {
if ( ! sqlAllowDottedWord )
return ( ch < 0x80 ) & & ( isalnum ( ch ) | | ch = = ' _ ' ) ;
else
return ( ch < 0x80 ) & & ( isalnum ( ch ) | | ch = = ' _ ' | | ch = = ' . ' ) ;
2009-04-24 23:35:41 +00:00
}
static inline bool IsAWordStart ( int ch ) {
return ( ch < 0x80 ) & & ( isalpha ( ch ) | | ch = = ' _ ' ) ;
}
static inline bool IsADoxygenChar ( int ch ) {
return ( islower ( ch ) | | ch = = ' $ ' | | ch = = ' @ ' | |
ch = = ' \\ ' | | ch = = ' & ' | | ch = = ' < ' | |
ch = = ' > ' | | ch = = ' # ' | | ch = = ' { ' | |
ch = = ' } ' | | ch = = ' [ ' | | ch = = ' ] ' ) ;
}
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 ) & &
2011-03-22 00:16:49 +00:00
( isdigit ( ch ) | | toupper ( ch ) = = ' E ' | |
ch = = ' . ' | | ch = = ' - ' | | ch = = ' + ' ) ;
2009-04-24 23:35:41 +00:00
}
2011-03-22 00:16:49 +00:00
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 ;
}
}
2009-04-24 23:35:41 +00:00
2011-03-22 00:16:49 +00:00
unsigned short int IgnoreWhen ( unsigned short int sqlStatesLine , bool enable ) {
if ( enable )
sqlStatesLine | = MASK_IGNORE_WHEN ;
else
sqlStatesLine & = ~ MASK_IGNORE_WHEN ;
return sqlStatesLine ;
}
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 ( ) { }
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 sqlBackticksIdentifier ;
bool sqlNumbersignComment ;
bool sqlBackslashEscapes ;
bool sqlAllowDottedWord ;
OptionsSQL ( ) {
fold = false ;
foldAtElse = false ;
foldComment = false ;
foldCompact = false ;
foldOnlyBegin = false ;
sqlBackticksIdentifier = false ;
sqlNumbersignComment = false ;
sqlBackslashEscapes = false ;
sqlAllowDottedWord = 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 ) ;
2011-07-17 22:30:49 +00:00
DefineProperty ( " fold.sql.at.else " , & OptionsSQL : : foldAtElse ,
" This option enables SQL folding on a \" ELSE \" and \" ELSIF \" line of an IF statement. " ) ;
2011-03-22 00:16:49 +00:00
DefineProperty ( " fold.comment " , & OptionsSQL : : foldComment ) ;
DefineProperty ( " fold.compact " , & OptionsSQL : : foldCompact ) ;
DefineProperty ( " fold.sql.only.begin " , & OptionsSQL : : foldOnlyBegin ) ;
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. " ) ;
DefineProperty ( " lexer.sql.allow.dotted.word " , & OptionsSQL : : sqlAllowDottedWord ,
" Set to 1 to colourise recognized words with dots "
" (recommended for Oracle PL/SQL objects). " ) ;
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 ;
}
2009-04-24 23:35:41 +00:00
2011-07-17 22:30:49 +00:00
bool IsCommentStyle ( int style ) {
switch ( style ) {
case SCE_SQL_COMMENT :
case SCE_SQL_COMMENTDOC :
case SCE_SQL_COMMENTLINE :
case SCE_SQL_COMMENTLINEDOC :
case SCE_SQL_COMMENTDOCKEYWORD :
case SCE_SQL_COMMENTDOCKEYWORDERROR :
return true ;
default :
return false ;
}
}
2011-03-22 00:16:49 +00:00
OptionsSQL options ;
OptionSetSQL osSQL ;
SQLStates sqlStates ;
2009-06-24 19:09:31 +00:00
2011-03-22 00:16:49 +00:00
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 ) ;
2009-04-24 23:35:41 +00:00
int styleBeforeDCKeyword = SCE_SQL_DEFAULT ;
2011-03-22 00:16:49 +00:00
int offset = 0 ;
for ( ; sc . More ( ) ; sc . Forward ( ) , offset + + ) {
2009-04-24 23:35:41 +00:00
// Determine if the current state should terminate.
switch ( sc . state ) {
case SCE_SQL_OPERATOR :
sc . SetState ( SCE_SQL_DEFAULT ) ;
break ;
case SCE_SQL_NUMBER :
// We stop the number definition on non-numerical non-dot non-eE non-sign char
if ( ! IsANumberChar ( sc . ch ) ) {
sc . SetState ( SCE_SQL_DEFAULT ) ;
}
break ;
case SCE_SQL_IDENTIFIER :
2011-03-22 00:16:49 +00:00
if ( ! IsAWordChar ( sc . ch , options . sqlAllowDottedWord ) ) {
2009-04-24 23:35:41 +00:00
int nextState = SCE_SQL_DEFAULT ;
char s [ 1000 ] ;
sc . GetCurrentLowered ( s , sizeof ( s ) ) ;
if ( keywords1 . InList ( s ) ) {
sc . ChangeState ( SCE_SQL_WORD ) ;
} else if ( keywords2 . InList ( s ) ) {
sc . ChangeState ( SCE_SQL_WORD2 ) ;
} else if ( kw_sqlplus . InListAbbreviated ( s , ' ~ ' ) ) {
sc . ChangeState ( SCE_SQL_SQLPLUS ) ;
if ( strncmp ( s , " rem " , 3 ) = = 0 ) {
nextState = SCE_SQL_SQLPLUS_COMMENT ;
} else if ( strncmp ( s , " pro " , 3 ) = = 0 ) {
nextState = SCE_SQL_SQLPLUS_PROMPT ;
}
} else if ( kw_user1 . InList ( s ) ) {
sc . ChangeState ( SCE_SQL_USER1 ) ;
} else if ( kw_user2 . InList ( s ) ) {
sc . ChangeState ( SCE_SQL_USER2 ) ;
} else if ( kw_user3 . InList ( s ) ) {
sc . ChangeState ( SCE_SQL_USER3 ) ;
} else if ( kw_user4 . InList ( s ) ) {
sc . ChangeState ( SCE_SQL_USER4 ) ;
}
sc . SetState ( nextState ) ;
}
break ;
case SCE_SQL_QUOTEDIDENTIFIER :
if ( sc . ch = = 0x60 ) {
if ( sc . chNext = = 0x60 ) {
sc . Forward ( ) ; // Ignore it
} else {
sc . ForwardSetState ( SCE_SQL_DEFAULT ) ;
}
}
break ;
case SCE_SQL_COMMENT :
if ( sc . Match ( ' * ' , ' / ' ) ) {
sc . Forward ( ) ;
sc . ForwardSetState ( SCE_SQL_DEFAULT ) ;
}
break ;
case SCE_SQL_COMMENTDOC :
if ( sc . Match ( ' * ' , ' / ' ) ) {
sc . Forward ( ) ;
sc . ForwardSetState ( SCE_SQL_DEFAULT ) ;
} else if ( sc . ch = = ' @ ' | | sc . ch = = ' \\ ' ) { // Doxygen support
// Verify that we have the conditions to mark a comment-doc-keyword
if ( ( IsASpace ( sc . chPrev ) | | sc . chPrev = = ' * ' ) & & ( ! IsASpace ( sc . chNext ) ) ) {
styleBeforeDCKeyword = SCE_SQL_COMMENTDOC ;
sc . SetState ( SCE_SQL_COMMENTDOCKEYWORD ) ;
}
}
break ;
case SCE_SQL_COMMENTLINE :
case SCE_SQL_COMMENTLINEDOC :
case SCE_SQL_SQLPLUS_COMMENT :
case SCE_SQL_SQLPLUS_PROMPT :
if ( sc . atLineStart ) {
sc . SetState ( SCE_SQL_DEFAULT ) ;
}
break ;
case SCE_SQL_COMMENTDOCKEYWORD :
if ( ( styleBeforeDCKeyword = = SCE_SQL_COMMENTDOC ) & & sc . Match ( ' * ' , ' / ' ) ) {
sc . ChangeState ( SCE_SQL_COMMENTDOCKEYWORDERROR ) ;
sc . Forward ( ) ;
sc . ForwardSetState ( SCE_SQL_DEFAULT ) ;
} else if ( ! IsADoxygenChar ( sc . ch ) ) {
char s [ 100 ] ;
sc . GetCurrentLowered ( s , sizeof ( s ) ) ;
if ( ! isspace ( sc . ch ) | | ! kw_pldoc . InList ( s + 1 ) ) {
sc . ChangeState ( SCE_SQL_COMMENTDOCKEYWORDERROR ) ;
}
sc . SetState ( styleBeforeDCKeyword ) ;
}
break ;
case SCE_SQL_CHARACTER :
2011-03-22 00:16:49 +00:00
if ( options . sqlBackslashEscapes & & sc . ch = = ' \\ ' ) {
2009-04-24 23:35:41 +00:00
sc . Forward ( ) ;
} else if ( sc . ch = = ' \' ' ) {
if ( sc . chNext = = ' \" ' ) {
sc . Forward ( ) ;
} else {
sc . ForwardSetState ( SCE_SQL_DEFAULT ) ;
}
}
break ;
case SCE_SQL_STRING :
if ( sc . ch = = ' \\ ' ) {
// Escape sequence
sc . Forward ( ) ;
} else if ( sc . ch = = ' \" ' ) {
if ( sc . chNext = = ' \" ' ) {
sc . Forward ( ) ;
} else {
sc . ForwardSetState ( SCE_SQL_DEFAULT ) ;
}
}
break ;
}
// Determine if a new state should be entered.
if ( sc . state = = SCE_SQL_DEFAULT ) {
if ( IsADigit ( sc . ch ) | | ( sc . ch = = ' . ' & & IsADigit ( sc . chNext ) ) ) {
sc . SetState ( SCE_SQL_NUMBER ) ;
} else if ( IsAWordStart ( sc . ch ) ) {
sc . SetState ( SCE_SQL_IDENTIFIER ) ;
2011-03-22 00:16:49 +00:00
} else if ( sc . ch = = 0x60 & & options . sqlBackticksIdentifier ) {
2009-04-24 23:35:41 +00:00
sc . SetState ( SCE_SQL_QUOTEDIDENTIFIER ) ;
} else if ( sc . Match ( ' / ' , ' * ' ) ) {
if ( sc . Match ( " /** " ) | | sc . Match ( " /*! " ) ) { // Support of Doxygen doc. style
sc . SetState ( SCE_SQL_COMMENTDOC ) ;
} else {
sc . SetState ( SCE_SQL_COMMENT ) ;
}
sc . Forward ( ) ; // Eat the * so it isn't used for the end of the comment
} else if ( sc . Match ( ' - ' , ' - ' ) ) {
// 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:
2011-03-22 00:16:49 +00:00
//~ } else if (sc.Match("-- ")) {
2009-04-24 23:35:41 +00:00
sc . SetState ( SCE_SQL_COMMENTLINE ) ;
2011-03-22 00:16:49 +00:00
} else if ( sc . ch = = ' # ' & & options . sqlNumbersignComment ) {
2009-04-24 23:35:41 +00:00
sc . SetState ( SCE_SQL_COMMENTLINEDOC ) ;
} else if ( sc . ch = = ' \' ' ) {
sc . SetState ( SCE_SQL_CHARACTER ) ;
} else if ( sc . ch = = ' \" ' ) {
sc . SetState ( SCE_SQL_STRING ) ;
} else if ( isoperator ( static_cast < char > ( sc . ch ) ) ) {
sc . SetState ( SCE_SQL_OPERATOR ) ;
}
}
}
sc . Complete ( ) ;
}
2011-03-22 00:16:49 +00:00
void SCI_METHOD LexerSQL : : Fold ( unsigned int startPos , int length , int initStyle , IDocument * pAccess ) {
if ( ! options . fold )
return ;
LexAccessor styler ( pAccess ) ;
2009-04-24 23:35:41 +00:00
unsigned int endPos = startPos + length ;
int visibleChars = 0 ;
int lineCurrent = styler . GetLine ( startPos ) ;
int levelCurrent = SC_FOLDLEVELBASE ;
if ( lineCurrent > 0 ) {
levelCurrent = styler . LevelAt ( lineCurrent - 1 ) > > 16 ;
}
int levelNext = levelCurrent ;
char chNext = styler [ startPos ] ;
int styleNext = styler . StyleAt ( startPos ) ;
int style = initStyle ;
bool endFound = false ;
2011-03-22 00:16:49 +00:00
bool isUnfoldingIgnored = false ;
// 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 ) ;
}
2009-04-24 23:35:41 +00:00
for ( unsigned int i = startPos ; i < endPos ; i + + ) {
char ch = chNext ;
chNext = styler . SafeGetCharAt ( i + 1 ) ;
int stylePrev = style ;
style = styleNext ;
styleNext = styler . StyleAt ( i + 1 ) ;
bool atEOL = ( ch = = ' \r ' & & chNext ! = ' \n ' ) | | ( ch = = ' \n ' ) ;
2011-07-17 22:30:49 +00:00
if ( atEOL | | ( ! IsCommentStyle ( style ) & & ch = = ' ; ' ) ) {
2011-03-22 00:16:49 +00:00
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 ( options . foldComment & & IsStreamCommentStyle ( style ) ) {
2009-04-24 23:35:41 +00:00
if ( ! IsStreamCommentStyle ( stylePrev ) ) {
levelNext + + ;
} else if ( ! IsStreamCommentStyle ( styleNext ) & & ! atEOL ) {
// Comments don't end at end of line and the next character may be unstyled.
levelNext - - ;
}
}
2011-03-22 00:16:49 +00:00
if ( options . foldComment & & ( style = = SCE_SQL_COMMENTLINE ) ) {
2009-04-24 23:35:41 +00:00
// MySQL needs -- comments to be followed by space or control char
if ( ( ch = = ' - ' ) & & ( chNext = = ' - ' ) ) {
char chNext2 = styler . SafeGetCharAt ( i + 2 ) ;
char chNext3 = styler . SafeGetCharAt ( i + 3 ) ;
if ( chNext2 = = ' { ' | | chNext3 = = ' { ' ) {
levelNext + + ;
} else if ( chNext2 = = ' } ' | | chNext3 = = ' } ' ) {
levelNext - - ;
}
}
}
if ( style = = SCE_SQL_OPERATOR ) {
if ( ch = = ' ( ' ) {
2011-03-22 00:16:49 +00:00
if ( levelCurrent > levelNext )
levelCurrent - - ;
2011-07-17 22:30:49 +00:00
levelNext + + ;
2009-04-24 23:35:41 +00:00
} else if ( ch = = ' ) ' ) {
levelNext - - ;
2011-03-22 00:16:49 +00:00
} else if ( ( ! options . foldOnlyBegin ) & & ch = = ' ; ' ) {
sqlStatesCurrentLine = sqlStates . IgnoreWhen ( sqlStatesCurrentLine , false ) ;
2009-04-24 23:35:41 +00:00
}
}
// If new keyword (cannot trigger on elseif or nullif, does less tests)
if ( style = = SCE_SQL_WORD & & stylePrev ! = SCE_SQL_WORD ) {
2011-03-22 00:16:49 +00:00
const int MAX_KW_LEN = 9 ; // Maximum length of folding keywords
2009-04-24 23:35:41 +00:00
char s [ MAX_KW_LEN + 2 ] ;
unsigned int j = 0 ;
for ( ; j < MAX_KW_LEN + 1 ; j + + ) {
if ( ! iswordchar ( styler [ i + j ] ) ) {
break ;
}
s [ j ] = static_cast < char > ( tolower ( styler [ i + j ] ) ) ;
}
if ( j = = MAX_KW_LEN + 1 ) {
// Keyword too long, don't test it
s [ 0 ] = ' \0 ' ;
} else {
s [ j ] = ' \0 ' ;
}
2011-03-22 00:16:49 +00:00
if ( strcmp ( s , " if " ) = = 0 ) {
2009-04-24 23:35:41 +00:00
if ( endFound ) {
endFound = false ;
2011-03-22 00:16:49 +00:00
if ( options . foldOnlyBegin & & ! isUnfoldingIgnored ) {
// this end isn't for begin block, but for if block ("end if;")
// so ignore previous "end" by increment levelNext.
levelNext + + ;
}
2009-04-24 23:35:41 +00:00
} else {
2011-03-22 00:16:49 +00:00
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 ;
}
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 ;
2009-04-24 23:35:41 +00:00
}
2011-03-22 00:16:49 +00:00
} 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 - - ;
2009-04-24 23:35:41 +00:00
} else if ( strcmp ( s , " begin " ) = = 0 ) {
levelNext + + ;
2011-03-22 00:16:49 +00:00
sqlStatesCurrentLine = sqlStates . IntoDeclareBlock ( sqlStatesCurrentLine , false ) ;
2010-07-12 22:19:51 +00:00
} else if ( ( strcmp ( s , " end " ) = = 0 ) | |
2011-03-22 00:16:49 +00:00
// SQL Anywhere permits IF ... ELSE ... ENDIF
// will only be active if "endif" appears in the
// keyword list.
( strcmp ( s , " endif " ) = = 0 ) ) {
2009-04-24 23:35:41 +00:00
endFound = true ;
levelNext - - ;
if ( levelNext < SC_FOLDLEVELBASE ) {
levelNext = SC_FOLDLEVELBASE ;
2011-03-22 00:16:49 +00:00
isUnfoldingIgnored = true ;
2009-04-24 23:35:41 +00:00
}
2011-03-22 00:16:49 +00:00
} 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 | |
strcmp ( s , " function " ) = = 0 | |
strcmp ( s , " procedure " ) = = 0 | |
strcmp ( s , " package " ) = = 0 ) ) {
sqlStatesCurrentLine = sqlStates . IntoDeclareBlock ( sqlStatesCurrentLine , true ) ;
2009-04-24 23:35:41 +00:00
}
}
if ( atEOL ) {
int levelUse = levelCurrent ;
int lev = levelUse | levelNext < < 16 ;
2011-03-22 00:16:49 +00:00
if ( visibleChars = = 0 & & options . foldCompact )
2009-04-24 23:35:41 +00:00
lev | = SC_FOLDLEVELWHITEFLAG ;
if ( levelUse < levelNext )
lev | = SC_FOLDLEVELHEADERFLAG ;
if ( lev ! = styler . LevelAt ( lineCurrent ) ) {
styler . SetLevel ( lineCurrent , lev ) ;
}
lineCurrent + + ;
levelCurrent = levelNext ;
visibleChars = 0 ;
2011-03-22 00:16:49 +00:00
statementFound = false ;
if ( ! options . foldOnlyBegin )
sqlStates . Set ( lineCurrent , sqlStatesCurrentLine ) ;
2009-04-24 23:35:41 +00:00
}
if ( ! isspacechar ( ch ) ) {
visibleChars + + ;
}
}
}
2011-03-22 00:16:49 +00:00
LexerModule lmSQL ( SCLEX_SQL , LexerSQL : : LexerFactorySQL , " sql " , sqlWordListDesc ) ;