scintilla: Update scintilla with changeset 3662:1d1c06df8a2f using gtk+3
[anjuta-extras.git] / plugins / scintilla / scintilla / LexSQL.cxx
blob64a896ee4eb53547642e374cc04ed1773ef55eb3
1 // Scintilla source code edit control
2 /** @file LexSQL.cxx
3 ** Lexer for SQL, including PL/SQL and SQL*Plus.
4 **/
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.
8 #include <stdlib.h>
9 #include <string.h>
10 #include <stdio.h>
11 #include <stdarg.h>
12 #include <assert.h>
13 #include <ctype.h>
15 #ifdef _MSC_VER
16 #pragma warning(disable: 4786)
17 #endif
19 #include <string>
20 #include <vector>
21 #include <map>
22 #include <algorithm>
24 #include "ILexer.h"
25 #include "Scintilla.h"
26 #include "SciLexer.h"
28 #include "WordList.h"
29 #include "LexAccessor.h"
30 #include "Accessor.h"
31 #include "StyleContext.h"
32 #include "CharacterSet.h"
33 #include "LexerModule.h"
34 #include "OptionSet.h"
36 #ifdef SCI_NAMESPACE
37 using namespace Scintilla;
38 #endif
40 static inline bool IsAWordChar(int ch, bool sqlAllowDottedWord) {
41 if (!sqlAllowDottedWord)
42 return (ch < 0x80) && (isalnum(ch) || ch == '_');
43 else
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.
61 return (ch < 0x80) &&
62 (isdigit(ch) || toupper(ch) == 'E' ||
63 ch == '.' || ch == '-' || ch == '+');
67 class SQLStates {
68 public :
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) {
77 if (enable)
78 sqlStatesLine |= MASK_IGNORE_WHEN;
79 else
80 sqlStatesLine &= ~MASK_IGNORE_WHEN;
82 return sqlStatesLine;
85 unsigned short int IntoCondition (unsigned short int sqlStatesLine, bool enable) {
86 if (enable)
87 sqlStatesLine |= MASK_INTO_CONDITION;
88 else
89 sqlStatesLine &= ~MASK_INTO_CONDITION;
91 return sqlStatesLine;
94 unsigned short int IntoExceptionBlock (unsigned short int sqlStatesLine, bool enable) {
95 if (enable)
96 sqlStatesLine |= MASK_INTO_EXCEPTION;
97 else
98 sqlStatesLine &= ~MASK_INTO_EXCEPTION;
100 return sqlStatesLine;
103 unsigned short int IntoDeclareBlock (unsigned short int sqlStatesLine, bool enable) {
104 if (enable)
105 sqlStatesLine |= MASK_INTO_DECLARE;
106 else
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) {
114 sqlStatesLine++;
116 return sqlStatesLine;
119 unsigned short int EndCaseBlock (unsigned short int sqlStatesLine) {
120 if ((sqlStatesLine & MASK_NESTED_CASES) > 0) {
121 sqlStatesLine--;
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];
149 } else {
150 return 0;
154 SQLStates() {}
156 private :
157 std::vector <unsigned short int> sqlStatement;
158 enum {
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
168 struct OptionsSQL {
169 bool fold;
170 bool foldAtElse;
171 bool foldComment;
172 bool foldCompact;
173 bool foldOnlyBegin;
174 bool sqlBackticksIdentifier;
175 bool sqlNumbersignComment;
176 bool sqlBackslashEscapes;
177 bool sqlAllowDottedWord;
178 OptionsSQL() {
179 fold = false;
180 foldAtElse = false;
181 foldComment = false;
182 foldCompact = false;
183 foldOnlyBegin = false;
184 sqlBackticksIdentifier = false;
185 sqlNumbersignComment = false;
186 sqlBackslashEscapes = false;
187 sqlAllowDottedWord = false;
191 static const char * const sqlWordListDesc[] = {
192 "Keywords",
193 "Database Objects",
194 "PLDoc",
195 "SQL*Plus",
196 "User Keywords 1",
197 "User Keywords 2",
198 "User Keywords 3",
199 "User Keywords 4",
203 struct OptionSetSQL : public OptionSet<OptionsSQL> {
204 OptionSetSQL() {
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 {
233 public :
234 LexerSQL() {}
236 int SCI_METHOD Version () const {
237 return lvOriginal;
240 void SCI_METHOD Release() {
241 delete this;
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)) {
258 return 0;
260 return -1;
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 *) {
272 return 0;
275 static ILexer *LexerFactorySQL() {
276 return new LexerSQL();
278 private:
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) {
287 switch (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 :
294 return true;
295 default :
296 return false;
300 OptionsSQL options;
301 OptionSetSQL osSQL;
302 SQLStates sqlStates;
304 WordList keywords1;
305 WordList keywords2;
306 WordList kw_pldoc;
307 WordList kw_sqlplus;
308 WordList kw_user1;
309 WordList kw_user2;
310 WordList kw_user3;
311 WordList kw_user4;
314 int SCI_METHOD LexerSQL::WordListSet(int n, const char *wl) {
315 WordList *wordListN = 0;
316 switch (n) {
317 case 0:
318 wordListN = &keywords1;
319 break;
320 case 1:
321 wordListN = &keywords2;
322 break;
323 case 2:
324 wordListN = &kw_pldoc;
325 break;
326 case 3:
327 wordListN = &kw_sqlplus;
328 break;
329 case 4:
330 wordListN = &kw_user1;
331 break;
332 case 5:
333 wordListN = &kw_user2;
334 break;
335 case 6:
336 wordListN = &kw_user3;
337 break;
338 case 7:
339 wordListN = &kw_user4;
341 int firstModification = -1;
342 if (wordListN) {
343 WordList wlNew;
344 wlNew.Set(wl);
345 if (*wordListN != wlNew) {
346 wordListN->Set(wl);
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;
357 int offset = 0;
358 for (; sc.More(); sc.Forward(), offset++) {
359 // Determine if the current state should terminate.
360 switch (sc.state) {
361 case SCE_SQL_OPERATOR:
362 sc.SetState(SCE_SQL_DEFAULT);
363 break;
364 case SCE_SQL_NUMBER:
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);
369 break;
370 case SCE_SQL_IDENTIFIER:
371 if (!IsAWordChar(sc.ch, options.sqlAllowDottedWord)) {
372 int nextState = SCE_SQL_DEFAULT;
373 char s[1000];
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);
397 break;
398 case SCE_SQL_QUOTEDIDENTIFIER:
399 if (sc.ch == 0x60) {
400 if (sc.chNext == 0x60) {
401 sc.Forward(); // Ignore it
402 } else {
403 sc.ForwardSetState(SCE_SQL_DEFAULT);
406 break;
407 case SCE_SQL_COMMENT:
408 if (sc.Match('*', '/')) {
409 sc.Forward();
410 sc.ForwardSetState(SCE_SQL_DEFAULT);
412 break;
413 case SCE_SQL_COMMENTDOC:
414 if (sc.Match('*', '/')) {
415 sc.Forward();
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);
424 break;
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);
432 break;
433 case SCE_SQL_COMMENTDOCKEYWORD:
434 if ((styleBeforeDCKeyword == SCE_SQL_COMMENTDOC) && sc.Match('*', '/')) {
435 sc.ChangeState(SCE_SQL_COMMENTDOCKEYWORDERROR);
436 sc.Forward();
437 sc.ForwardSetState(SCE_SQL_DEFAULT);
438 } else if (!IsADoxygenChar(sc.ch)) {
439 char s[100];
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);
446 break;
447 case SCE_SQL_CHARACTER:
448 if (options.sqlBackslashEscapes && sc.ch == '\\') {
449 sc.Forward();
450 } else if (sc.ch == '\'') {
451 if (sc.chNext == '\"') {
452 sc.Forward();
453 } else {
454 sc.ForwardSetState(SCE_SQL_DEFAULT);
457 break;
458 case SCE_SQL_STRING:
459 if (sc.ch == '\\') {
460 // Escape sequence
461 sc.Forward();
462 } else if (sc.ch == '\"') {
463 if (sc.chNext == '\"') {
464 sc.Forward();
465 } else {
466 sc.ForwardSetState(SCE_SQL_DEFAULT);
469 break;
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);
483 } else {
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);
504 sc.Complete();
507 void SCI_METHOD LexerSQL::Fold(unsigned int startPos, int length, int initStyle, IDocument *pAccess) {
508 if (!options.fold)
509 return;
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++) {
532 char ch = chNext;
533 chNext = styler.SafeGetCharAt(i + 1);
534 int stylePrev = style;
535 style = styleNext;
536 styleNext = styler.StyleAt(i + 1);
537 bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
538 if (atEOL || (!IsCommentStyle(style) && ch == ';')) {
539 if (endFound) {
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
544 endFound = false;
545 isUnfoldingIgnored = false;
547 if (options.foldComment && IsStreamCommentStyle(style)) {
548 if (!IsStreamCommentStyle(stylePrev)) {
549 levelNext++;
550 } else if (!IsStreamCommentStyle(styleNext) && !atEOL) {
551 // Comments don't end at end of line and the next character may be unstyled.
552 levelNext--;
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 == '{') {
561 levelNext++;
562 } else if (chNext2 == '}' || chNext3 == '}') {
563 levelNext--;
567 if (style == SCE_SQL_OPERATOR) {
568 if (ch == '(') {
569 if (levelCurrent > levelNext)
570 levelCurrent--;
571 levelNext++;
572 } else if (ch == ')') {
573 levelNext--;
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];
582 unsigned int j = 0;
583 for (; j < MAX_KW_LEN + 1; j++) {
584 if (!iswordchar(styler[i + j])) {
585 break;
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
591 s[0] = '\0';
592 } else {
593 s[j] = '\0';
596 if (strcmp(s, "if") == 0) {
597 if (endFound) {
598 endFound = false;
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.
602 levelNext++;
604 } else {
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;
621 if (!statementFound)
622 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) {
632 if (endFound) {
633 endFound = false;
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.
637 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
648 if (!statementFound)
649 levelNext++;
652 if (levelCurrent > levelNext) {
653 levelCurrent = levelNext;
655 if (!statementFound)
656 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);
669 levelCurrent--;
670 levelNext--;
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
678 levelCurrent--;
680 } else if (strcmp(s, "begin") == 0) {
681 levelNext++;
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
686 // keyword list.
687 (strcmp(s, "endif") == 0)) {
688 endFound = true;
689 levelNext--;
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) {
703 levelCurrent--;
704 levelNext--;
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);
718 if (atEOL) {
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);
728 lineCurrent++;
729 levelCurrent = levelNext;
730 visibleChars = 0;
731 statementFound = false;
732 if (!options.foldOnlyBegin)
733 sqlStates.Set(lineCurrent, sqlStatesCurrentLine);
735 if (!isspacechar(ch)) {
736 visibleChars++;
741 LexerModule lmSQL(SCLEX_SQL, LexerSQL::LexerFactorySQL, "sql", sqlWordListDesc);