1 // Scintilla source code edit control
3 ** Lexer for SQL, including PL/SQL and SQL*Plus.
5 // Copyright 1998-2011 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
16 #pragma warning(disable: 4786)
25 #include "Scintilla.h"
29 #include "LexAccessor.h"
31 #include "StyleContext.h"
32 #include "CharacterSet.h"
33 #include "LexerModule.h"
34 #include "OptionSet.h"
37 using namespace Scintilla
;
40 static inline bool IsAWordChar(int ch
, bool sqlAllowDottedWord
) {
41 if (!sqlAllowDottedWord
)
42 return (ch
< 0x80) && (isalnum(ch
) || ch
== '_');
44 return (ch
< 0x80) && (isalnum(ch
) || ch
== '_' || ch
== '.');
47 static inline bool IsAWordStart(int ch
) {
48 return (ch
< 0x80) && (isalpha(ch
) || ch
== '_');
51 static inline bool IsADoxygenChar(int ch
) {
52 return (islower(ch
) || ch
== '$' || ch
== '@' ||
53 ch
== '\\' || ch
== '&' || ch
== '<' ||
54 ch
== '>' || ch
== '#' || ch
== '{' ||
55 ch
== '}' || ch
== '[' || ch
== ']');
58 static inline bool IsANumberChar(int ch
) {
59 // Not exactly following number definition (several dots are seen as OK, etc.)
60 // but probably enough in most cases.
62 (isdigit(ch
) || toupper(ch
) == 'E' ||
63 ch
== '.' || ch
== '-' || ch
== '+');
69 void Set(int lineNumber
, unsigned short int sqlStatesLine
) {
70 if (!sqlStatement
.size() == 0 || !sqlStatesLine
== 0) {
71 sqlStatement
.resize(lineNumber
+ 1, 0);
72 sqlStatement
[lineNumber
] = sqlStatesLine
;
76 unsigned short int IgnoreWhen (unsigned short int sqlStatesLine
, bool enable
) {
78 sqlStatesLine
|= MASK_IGNORE_WHEN
;
80 sqlStatesLine
&= ~MASK_IGNORE_WHEN
;
85 unsigned short int IntoCondition (unsigned short int sqlStatesLine
, bool enable
) {
87 sqlStatesLine
|= MASK_INTO_CONDITION
;
89 sqlStatesLine
&= ~MASK_INTO_CONDITION
;
94 unsigned short int IntoExceptionBlock (unsigned short int sqlStatesLine
, bool enable
) {
96 sqlStatesLine
|= MASK_INTO_EXCEPTION
;
98 sqlStatesLine
&= ~MASK_INTO_EXCEPTION
;
100 return sqlStatesLine
;
103 unsigned short int IntoDeclareBlock (unsigned short int sqlStatesLine
, bool enable
) {
105 sqlStatesLine
|= MASK_INTO_DECLARE
;
107 sqlStatesLine
&= ~MASK_INTO_DECLARE
;
109 return sqlStatesLine
;
112 unsigned short int BeginCaseBlock (unsigned short int sqlStatesLine
) {
113 if ((sqlStatesLine
& MASK_NESTED_CASES
) < MASK_NESTED_CASES
) {
116 return sqlStatesLine
;
119 unsigned short int EndCaseBlock (unsigned short int sqlStatesLine
) {
120 if ((sqlStatesLine
& MASK_NESTED_CASES
) > 0) {
123 return sqlStatesLine
;
126 bool IsIgnoreWhen (unsigned short int sqlStatesLine
) {
127 return (sqlStatesLine
& MASK_IGNORE_WHEN
) != 0;
130 bool IsIntoCondition (unsigned short int sqlStatesLine
) {
131 return (sqlStatesLine
& MASK_INTO_CONDITION
) != 0;
134 bool IsIntoCaseBlock (unsigned short int sqlStatesLine
) {
135 return (sqlStatesLine
& MASK_NESTED_CASES
) != 0;
138 bool IsIntoExceptionBlock (unsigned short int sqlStatesLine
) {
139 return (sqlStatesLine
& MASK_INTO_EXCEPTION
) != 0;
142 bool IsIntoDeclareBlock (unsigned short int sqlStatesLine
) {
143 return (sqlStatesLine
& MASK_INTO_DECLARE
) != 0;
146 unsigned short int ForLine(int lineNumber
) {
147 if ((lineNumber
> 0) && (sqlStatement
.size() > static_cast<size_t>(lineNumber
))) {
148 return sqlStatement
[lineNumber
];
157 std::vector
<unsigned short int> sqlStatement
;
159 MASK_INTO_DECLARE
= 0x1000,
160 MASK_INTO_EXCEPTION
= 0x2000,
161 MASK_INTO_CONDITION
= 0x4000,
162 MASK_IGNORE_WHEN
= 0x8000,
163 MASK_NESTED_CASES
= 0x0FFF
167 // Options used for LexerSQL
174 bool sqlBackticksIdentifier
;
175 bool sqlNumbersignComment
;
176 bool sqlBackslashEscapes
;
177 bool sqlAllowDottedWord
;
183 foldOnlyBegin
= false;
184 sqlBackticksIdentifier
= false;
185 sqlNumbersignComment
= false;
186 sqlBackslashEscapes
= false;
187 sqlAllowDottedWord
= false;
191 static const char * const sqlWordListDesc
[] = {
203 struct OptionSetSQL
: public OptionSet
<OptionsSQL
> {
205 DefineProperty("fold", &OptionsSQL::fold
);
207 DefineProperty("fold.sql.at.else", &OptionsSQL::foldAtElse
,
208 "This option enables SQL folding on a \"ELSE\" and \"ELSIF\" line of an IF statement.");
210 DefineProperty("fold.comment", &OptionsSQL::foldComment
);
212 DefineProperty("fold.compact", &OptionsSQL::foldCompact
);
214 DefineProperty("fold.sql.only.begin", &OptionsSQL::foldOnlyBegin
);
216 DefineProperty("lexer.sql.backticks.identifier", &OptionsSQL::sqlBackticksIdentifier
);
218 DefineProperty("lexer.sql.numbersign.comment", &OptionsSQL::sqlNumbersignComment
,
219 "If \"lexer.sql.numbersign.comment\" property is set to 0 a line beginning with '#' will not be a comment.");
221 DefineProperty("sql.backslash.escapes", &OptionsSQL::sqlBackslashEscapes
,
222 "Enables backslash as an escape character in SQL.");
224 DefineProperty("lexer.sql.allow.dotted.word", &OptionsSQL::sqlAllowDottedWord
,
225 "Set to 1 to colourise recognized words with dots "
226 "(recommended for Oracle PL/SQL objects).");
228 DefineWordListSets(sqlWordListDesc
);
232 class LexerSQL
: public ILexer
{
236 int SCI_METHOD
Version () const {
240 void SCI_METHOD
Release() {
244 const char * SCI_METHOD
PropertyNames() {
245 return osSQL
.PropertyNames();
248 int SCI_METHOD
PropertyType(const char *name
) {
249 return osSQL
.PropertyType(name
);
252 const char * SCI_METHOD
DescribeProperty(const char *name
) {
253 return osSQL
.DescribeProperty(name
);
256 int SCI_METHOD
PropertySet(const char *key
, const char *val
) {
257 if (osSQL
.PropertySet(&options
, key
, val
)) {
263 const char * SCI_METHOD
DescribeWordListSets() {
264 return osSQL
.DescribeWordListSets();
267 int SCI_METHOD
WordListSet(int n
, const char *wl
);
268 void SCI_METHOD
Lex (unsigned int startPos
, int lengthDoc
, int initStyle
, IDocument
*pAccess
);
269 void SCI_METHOD
Fold(unsigned int startPos
, int lengthDoc
, int initStyle
, IDocument
*pAccess
);
271 void * SCI_METHOD
PrivateCall(int, void *) {
275 static ILexer
*LexerFactorySQL() {
276 return new LexerSQL();
279 bool IsStreamCommentStyle(int style
) {
280 return style
== SCE_SQL_COMMENT
||
281 style
== SCE_SQL_COMMENTDOC
||
282 style
== SCE_SQL_COMMENTDOCKEYWORD
||
283 style
== SCE_SQL_COMMENTDOCKEYWORDERROR
;
286 bool IsCommentStyle (int style
) {
288 case SCE_SQL_COMMENT
:
289 case SCE_SQL_COMMENTDOC
:
290 case SCE_SQL_COMMENTLINE
:
291 case SCE_SQL_COMMENTLINEDOC
:
292 case SCE_SQL_COMMENTDOCKEYWORD
:
293 case SCE_SQL_COMMENTDOCKEYWORDERROR
:
314 int SCI_METHOD
LexerSQL::WordListSet(int n
, const char *wl
) {
315 WordList
*wordListN
= 0;
318 wordListN
= &keywords1
;
321 wordListN
= &keywords2
;
324 wordListN
= &kw_pldoc
;
327 wordListN
= &kw_sqlplus
;
330 wordListN
= &kw_user1
;
333 wordListN
= &kw_user2
;
336 wordListN
= &kw_user3
;
339 wordListN
= &kw_user4
;
341 int firstModification
= -1;
345 if (*wordListN
!= wlNew
) {
347 firstModification
= 0;
350 return firstModification
;
353 void SCI_METHOD
LexerSQL::Lex(unsigned int startPos
, int length
, int initStyle
, IDocument
*pAccess
) {
354 LexAccessor
styler(pAccess
);
355 StyleContext
sc(startPos
, length
, initStyle
, styler
);
356 int styleBeforeDCKeyword
= SCE_SQL_DEFAULT
;
358 for (; sc
.More(); sc
.Forward(), offset
++) {
359 // Determine if the current state should terminate.
361 case SCE_SQL_OPERATOR
:
362 sc
.SetState(SCE_SQL_DEFAULT
);
365 // We stop the number definition on non-numerical non-dot non-eE non-sign char
366 if (!IsANumberChar(sc
.ch
)) {
367 sc
.SetState(SCE_SQL_DEFAULT
);
370 case SCE_SQL_IDENTIFIER
:
371 if (!IsAWordChar(sc
.ch
, options
.sqlAllowDottedWord
)) {
372 int nextState
= SCE_SQL_DEFAULT
;
374 sc
.GetCurrentLowered(s
, sizeof(s
));
375 if (keywords1
.InList(s
)) {
376 sc
.ChangeState(SCE_SQL_WORD
);
377 } else if (keywords2
.InList(s
)) {
378 sc
.ChangeState(SCE_SQL_WORD2
);
379 } else if (kw_sqlplus
.InListAbbreviated(s
, '~')) {
380 sc
.ChangeState(SCE_SQL_SQLPLUS
);
381 if (strncmp(s
, "rem", 3) == 0) {
382 nextState
= SCE_SQL_SQLPLUS_COMMENT
;
383 } else if (strncmp(s
, "pro", 3) == 0) {
384 nextState
= SCE_SQL_SQLPLUS_PROMPT
;
386 } else if (kw_user1
.InList(s
)) {
387 sc
.ChangeState(SCE_SQL_USER1
);
388 } else if (kw_user2
.InList(s
)) {
389 sc
.ChangeState(SCE_SQL_USER2
);
390 } else if (kw_user3
.InList(s
)) {
391 sc
.ChangeState(SCE_SQL_USER3
);
392 } else if (kw_user4
.InList(s
)) {
393 sc
.ChangeState(SCE_SQL_USER4
);
395 sc
.SetState(nextState
);
398 case SCE_SQL_QUOTEDIDENTIFIER
:
400 if (sc
.chNext
== 0x60) {
401 sc
.Forward(); // Ignore it
403 sc
.ForwardSetState(SCE_SQL_DEFAULT
);
407 case SCE_SQL_COMMENT
:
408 if (sc
.Match('*', '/')) {
410 sc
.ForwardSetState(SCE_SQL_DEFAULT
);
413 case SCE_SQL_COMMENTDOC
:
414 if (sc
.Match('*', '/')) {
416 sc
.ForwardSetState(SCE_SQL_DEFAULT
);
417 } else if (sc
.ch
== '@' || sc
.ch
== '\\') { // Doxygen support
418 // Verify that we have the conditions to mark a comment-doc-keyword
419 if ((IsASpace(sc
.chPrev
) || sc
.chPrev
== '*') && (!IsASpace(sc
.chNext
))) {
420 styleBeforeDCKeyword
= SCE_SQL_COMMENTDOC
;
421 sc
.SetState(SCE_SQL_COMMENTDOCKEYWORD
);
425 case SCE_SQL_COMMENTLINE
:
426 case SCE_SQL_COMMENTLINEDOC
:
427 case SCE_SQL_SQLPLUS_COMMENT
:
428 case SCE_SQL_SQLPLUS_PROMPT
:
429 if (sc
.atLineStart
) {
430 sc
.SetState(SCE_SQL_DEFAULT
);
433 case SCE_SQL_COMMENTDOCKEYWORD
:
434 if ((styleBeforeDCKeyword
== SCE_SQL_COMMENTDOC
) && sc
.Match('*', '/')) {
435 sc
.ChangeState(SCE_SQL_COMMENTDOCKEYWORDERROR
);
437 sc
.ForwardSetState(SCE_SQL_DEFAULT
);
438 } else if (!IsADoxygenChar(sc
.ch
)) {
440 sc
.GetCurrentLowered(s
, sizeof(s
));
441 if (!isspace(sc
.ch
) || !kw_pldoc
.InList(s
+ 1)) {
442 sc
.ChangeState(SCE_SQL_COMMENTDOCKEYWORDERROR
);
444 sc
.SetState(styleBeforeDCKeyword
);
447 case SCE_SQL_CHARACTER
:
448 if (options
.sqlBackslashEscapes
&& sc
.ch
== '\\') {
450 } else if (sc
.ch
== '\'') {
451 if (sc
.chNext
== '\"') {
454 sc
.ForwardSetState(SCE_SQL_DEFAULT
);
462 } else if (sc
.ch
== '\"') {
463 if (sc
.chNext
== '\"') {
466 sc
.ForwardSetState(SCE_SQL_DEFAULT
);
472 // Determine if a new state should be entered.
473 if (sc
.state
== SCE_SQL_DEFAULT
) {
474 if (IsADigit(sc
.ch
) || (sc
.ch
== '.' && IsADigit(sc
.chNext
))) {
475 sc
.SetState(SCE_SQL_NUMBER
);
476 } else if (IsAWordStart(sc
.ch
)) {
477 sc
.SetState(SCE_SQL_IDENTIFIER
);
478 } else if (sc
.ch
== 0x60 && options
.sqlBackticksIdentifier
) {
479 sc
.SetState(SCE_SQL_QUOTEDIDENTIFIER
);
480 } else if (sc
.Match('/', '*')) {
481 if (sc
.Match("/**") || sc
.Match("/*!")) { // Support of Doxygen doc. style
482 sc
.SetState(SCE_SQL_COMMENTDOC
);
484 sc
.SetState(SCE_SQL_COMMENT
);
486 sc
.Forward(); // Eat the * so it isn't used for the end of the comment
487 } else if (sc
.Match('-', '-')) {
488 // MySQL requires a space or control char after --
489 // http://dev.mysql.com/doc/mysql/en/ansi-diff-comments.html
490 // Perhaps we should enforce that with proper property:
491 //~ } else if (sc.Match("-- ")) {
492 sc
.SetState(SCE_SQL_COMMENTLINE
);
493 } else if (sc
.ch
== '#' && options
.sqlNumbersignComment
) {
494 sc
.SetState(SCE_SQL_COMMENTLINEDOC
);
495 } else if (sc
.ch
== '\'') {
496 sc
.SetState(SCE_SQL_CHARACTER
);
497 } else if (sc
.ch
== '\"') {
498 sc
.SetState(SCE_SQL_STRING
);
499 } else if (isoperator(static_cast<char>(sc
.ch
))) {
500 sc
.SetState(SCE_SQL_OPERATOR
);
507 void SCI_METHOD
LexerSQL::Fold(unsigned int startPos
, int length
, int initStyle
, IDocument
*pAccess
) {
510 LexAccessor
styler(pAccess
);
511 unsigned int endPos
= startPos
+ length
;
512 int visibleChars
= 0;
513 int lineCurrent
= styler
.GetLine(startPos
);
514 int levelCurrent
= SC_FOLDLEVELBASE
;
515 if (lineCurrent
> 0) {
516 levelCurrent
= styler
.LevelAt(lineCurrent
- 1) >> 16;
518 int levelNext
= levelCurrent
;
519 char chNext
= styler
[startPos
];
520 int styleNext
= styler
.StyleAt(startPos
);
521 int style
= initStyle
;
522 bool endFound
= false;
523 bool isUnfoldingIgnored
= false;
524 // this statementFound flag avoids to fold when the statement is on only one line by ignoring ELSE or ELSIF
525 // eg. "IF condition1 THEN ... ELSIF condition2 THEN ... ELSE ... END IF;"
526 bool statementFound
= false;
527 unsigned short int sqlStatesCurrentLine
= 0;
528 if (!options
.foldOnlyBegin
) {
529 sqlStatesCurrentLine
= sqlStates
.ForLine(lineCurrent
);
531 for (unsigned int i
= startPos
; i
< endPos
; i
++) {
533 chNext
= styler
.SafeGetCharAt(i
+ 1);
534 int stylePrev
= style
;
536 styleNext
= styler
.StyleAt(i
+ 1);
537 bool atEOL
= (ch
== '\r' && chNext
!= '\n') || (ch
== '\n');
538 if (atEOL
|| (!IsCommentStyle(style
) && ch
== ';')) {
540 //Maybe this is the end of "EXCEPTION" BLOCK (eg. "BEGIN ... EXCEPTION ... END;")
541 sqlStatesCurrentLine
= sqlStates
.IntoExceptionBlock(sqlStatesCurrentLine
, false);
543 // set endFound and isUnfoldingIgnored to false if EOL is reached or ';' is found
545 isUnfoldingIgnored
= false;
547 if (options
.foldComment
&& IsStreamCommentStyle(style
)) {
548 if (!IsStreamCommentStyle(stylePrev
)) {
550 } else if (!IsStreamCommentStyle(styleNext
) && !atEOL
) {
551 // Comments don't end at end of line and the next character may be unstyled.
555 if (options
.foldComment
&& (style
== SCE_SQL_COMMENTLINE
)) {
556 // MySQL needs -- comments to be followed by space or control char
557 if ((ch
== '-') && (chNext
== '-')) {
558 char chNext2
= styler
.SafeGetCharAt(i
+ 2);
559 char chNext3
= styler
.SafeGetCharAt(i
+ 3);
560 if (chNext2
== '{' || chNext3
== '{') {
562 } else if (chNext2
== '}' || chNext3
== '}') {
567 if (style
== SCE_SQL_OPERATOR
) {
569 if (levelCurrent
> levelNext
)
572 } else if (ch
== ')') {
574 } else if ((!options
.foldOnlyBegin
) && ch
== ';') {
575 sqlStatesCurrentLine
= sqlStates
.IgnoreWhen(sqlStatesCurrentLine
, false);
578 // If new keyword (cannot trigger on elseif or nullif, does less tests)
579 if (style
== SCE_SQL_WORD
&& stylePrev
!= SCE_SQL_WORD
) {
580 const int MAX_KW_LEN
= 9; // Maximum length of folding keywords
581 char s
[MAX_KW_LEN
+ 2];
583 for (; j
< MAX_KW_LEN
+ 1; j
++) {
584 if (!iswordchar(styler
[i
+ j
])) {
587 s
[j
] = static_cast<char>(tolower(styler
[i
+ j
]));
589 if (j
== MAX_KW_LEN
+ 1) {
590 // Keyword too long, don't test it
596 if (strcmp(s
, "if") == 0) {
599 if (options
.foldOnlyBegin
&& !isUnfoldingIgnored
) {
600 // this end isn't for begin block, but for if block ("end if;")
601 // so ignore previous "end" by increment levelNext.
605 if (!options
.foldOnlyBegin
)
606 sqlStatesCurrentLine
= sqlStates
.IntoCondition(sqlStatesCurrentLine
, true);
607 if (levelCurrent
> levelNext
) {
608 // doesn't include this line into the folding block
609 // because doesn't hide IF (eg "END; IF")
610 levelCurrent
= levelNext
;
613 } else if (!options
.foldOnlyBegin
&&
614 strcmp(s
, "then") == 0 &&
615 sqlStates
.IsIntoCondition(sqlStatesCurrentLine
)) {
616 sqlStatesCurrentLine
= sqlStates
.IntoCondition(sqlStatesCurrentLine
, false);
617 if (!options
.foldOnlyBegin
) {
618 if (levelCurrent
> levelNext
) {
619 levelCurrent
= levelNext
;
624 statementFound
= true;
625 } else if (levelCurrent
> levelNext
) {
626 // doesn't include this line into the folding block
627 // because doesn't hide LOOP or CASE (eg "END; LOOP" or "END; CASE")
628 levelCurrent
= levelNext
;
630 } else if (strcmp(s
, "loop") == 0 ||
631 strcmp(s
, "case") == 0) {
634 if (options
.foldOnlyBegin
&& !isUnfoldingIgnored
) {
635 // this end isn't for begin block, but for loop block ("end loop;") or case block ("end case;")
636 // so ignore previous "end" by increment levelNext.
639 if ((!options
.foldOnlyBegin
) && strcmp(s
, "case") == 0) {
640 sqlStatesCurrentLine
= sqlStates
.EndCaseBlock(sqlStatesCurrentLine
);
641 levelNext
--; //again for the "end case;" and block when
643 } else if (!options
.foldOnlyBegin
) {
644 if (strcmp(s
, "case") == 0) {
645 sqlStatesCurrentLine
= sqlStates
.BeginCaseBlock(sqlStatesCurrentLine
);
647 //for case block increment 2 times
652 if (levelCurrent
> levelNext
) {
653 levelCurrent
= levelNext
;
658 statementFound
= true;
659 } else if (levelCurrent
> levelNext
) {
660 // doesn't include this line into the folding block
661 // because doesn't hide LOOP or CASE (eg "END; LOOP" or "END; CASE")
662 levelCurrent
= levelNext
;
664 } else if ((!options
.foldOnlyBegin
) && (
665 // folding for ELSE and ELSIF block only if foldAtElse is set
666 // and IF or CASE aren't on only one line with ELSE or ELSIF (with flag statementFound)
667 options
.foldAtElse
&& !statementFound
) && strcmp(s
, "elsif") == 0) {
668 sqlStatesCurrentLine
= sqlStates
.IntoCondition(sqlStatesCurrentLine
, true);
671 } else if ((!options
.foldOnlyBegin
) && (
672 // folding for ELSE and ELSIF block only if foldAtElse is set
673 // and IF or CASE aren't on only one line with ELSE or ELSIF (with flag statementFound)
674 options
.foldAtElse
&& !statementFound
) && strcmp(s
, "else") == 0) {
675 // prevent also ELSE is on the same line (eg. "ELSE ... END IF;")
676 statementFound
= true;
677 // we are in same case "} ELSE {" in C language
680 } else if (strcmp(s
, "begin") == 0) {
682 sqlStatesCurrentLine
= sqlStates
.IntoDeclareBlock(sqlStatesCurrentLine
, false);
683 } else if ((strcmp(s
, "end") == 0) ||
684 // SQL Anywhere permits IF ... ELSE ... ENDIF
685 // will only be active if "endif" appears in the
687 (strcmp(s
, "endif") == 0)) {
690 if (levelNext
< SC_FOLDLEVELBASE
) {
691 levelNext
= SC_FOLDLEVELBASE
;
692 isUnfoldingIgnored
= true;
694 } else if ((!options
.foldOnlyBegin
) &&
695 strcmp(s
, "when") == 0 &&
696 !sqlStates
.IsIgnoreWhen(sqlStatesCurrentLine
) &&
697 !sqlStates
.IsIntoExceptionBlock(sqlStatesCurrentLine
) &&
698 sqlStates
.IsIntoCaseBlock(sqlStatesCurrentLine
)) {
699 sqlStatesCurrentLine
= sqlStates
.IntoCondition(sqlStatesCurrentLine
, true);
701 // 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")
702 if (!statementFound
) {
706 } else if ((!options
.foldOnlyBegin
) && strcmp(s
, "exit") == 0) {
707 sqlStatesCurrentLine
= sqlStates
.IgnoreWhen(sqlStatesCurrentLine
, true);
708 } else if ((!options
.foldOnlyBegin
) && !sqlStates
.IsIntoDeclareBlock(sqlStatesCurrentLine
) && strcmp(s
, "exception") == 0) {
709 sqlStatesCurrentLine
= sqlStates
.IntoExceptionBlock(sqlStatesCurrentLine
, true);
710 } else if ((!options
.foldOnlyBegin
) &&
711 (strcmp(s
, "declare") == 0 ||
712 strcmp(s
, "function") == 0 ||
713 strcmp(s
, "procedure") == 0 ||
714 strcmp(s
, "package") == 0)) {
715 sqlStatesCurrentLine
= sqlStates
.IntoDeclareBlock(sqlStatesCurrentLine
, true);
719 int levelUse
= levelCurrent
;
720 int lev
= levelUse
| levelNext
<< 16;
721 if (visibleChars
== 0 && options
.foldCompact
)
722 lev
|= SC_FOLDLEVELWHITEFLAG
;
723 if (levelUse
< levelNext
)
724 lev
|= SC_FOLDLEVELHEADERFLAG
;
725 if (lev
!= styler
.LevelAt(lineCurrent
)) {
726 styler
.SetLevel(lineCurrent
, lev
);
729 levelCurrent
= levelNext
;
731 statementFound
= false;
732 if (!options
.foldOnlyBegin
)
733 sqlStates
.Set(lineCurrent
, sqlStatesCurrentLine
);
735 if (!isspacechar(ch
)) {
741 LexerModule
lmSQL(SCLEX_SQL
, LexerSQL::LexerFactorySQL
, "sql", sqlWordListDesc
);