~ubuntu-branches/ubuntu/wily/geany/wily

« back to all changes in this revision

Viewing changes to scintilla/lexers/LexSQL.cxx

  • Committer: Package Import Robot
  • Author(s): Chow Loong Jin
  • Date: 2011-12-10 07:43:26 UTC
  • mfrom: (3.3.7 sid)
  • Revision ID: package-import@ubuntu.com-20111210074326-s8yqbew5i20h33tf
Tags: 0.21-1ubuntu1
* Merge from Debian Unstable, remaining changes:
  - debian/patches/20_use_evince_viewer.patch:
     + use evince as viewer for pdf and dvi files
  - debian/patches/20_use_x_terminal_emulator.patch:
     + use x-terminal-emulator as terminal
  - debian/control
     + Add breaks on geany-plugins-common << 0.20
* Also fixes bugs:
  - Filter for MATLAB/Octave files filters everythign (LP: 885505)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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.
 
7
 
 
8
#include <stdlib.h>
 
9
#include <string.h>
 
10
#include <stdio.h>
 
11
#include <stdarg.h>
 
12
#include <assert.h>
 
13
#include <ctype.h>
 
14
 
 
15
#ifdef _MSC_VER
 
16
#pragma warning(disable: 4786)
 
17
#endif
 
18
 
 
19
#include <string>
 
20
#include <vector>
 
21
#include <map>
 
22
#include <algorithm>
 
23
 
 
24
#include "ILexer.h"
 
25
#include "Scintilla.h"
 
26
#include "SciLexer.h"
 
27
 
 
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"
 
35
 
 
36
#ifdef SCI_NAMESPACE
 
37
using namespace Scintilla;
 
38
#endif
 
39
 
 
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 == '.');
 
45
}
 
46
 
 
47
static inline bool IsAWordStart(int ch) {
 
48
        return (ch < 0x80) && (isalpha(ch) || ch == '_');
 
49
}
 
50
 
 
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 == ']');
 
56
}
 
57
 
 
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 == '+');
 
64
}
 
65
 
 
66
 
 
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;
 
73
                }
 
74
        }
 
75
 
 
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;
 
81
 
 
82
                return sqlStatesLine;
 
83
        }
 
84
 
 
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;
 
90
 
 
91
                return sqlStatesLine;
 
92
        }
 
93
 
 
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;
 
99
 
 
100
                return sqlStatesLine;
 
101
        }
 
102
 
 
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;
 
108
 
 
109
                return sqlStatesLine;
 
110
        }
 
111
 
 
112
        unsigned short int BeginCaseBlock (unsigned short int sqlStatesLine) {
 
113
                if ((sqlStatesLine & MASK_NESTED_CASES) < MASK_NESTED_CASES) {
 
114
                        sqlStatesLine++;
 
115
                }
 
116
                return sqlStatesLine;
 
117
        }
 
118
 
 
119
        unsigned short int EndCaseBlock (unsigned short int sqlStatesLine) {
 
120
                if ((sqlStatesLine & MASK_NESTED_CASES) > 0) {
 
121
                        sqlStatesLine--;
 
122
                }
 
123
                return sqlStatesLine;
 
124
        }
 
125
 
 
126
        bool IsIgnoreWhen (unsigned short int sqlStatesLine) {
 
127
                return (sqlStatesLine & MASK_IGNORE_WHEN) != 0;
 
128
        }
 
129
 
 
130
        bool IsIntoCondition (unsigned short int sqlStatesLine) {
 
131
                return (sqlStatesLine & MASK_INTO_CONDITION) != 0;
 
132
        }
 
133
 
 
134
        bool IsIntoCaseBlock (unsigned short int sqlStatesLine) {
 
135
                return (sqlStatesLine & MASK_NESTED_CASES) != 0;
 
136
        }
 
137
 
 
138
        bool IsIntoExceptionBlock (unsigned short int sqlStatesLine) {
 
139
                return (sqlStatesLine & MASK_INTO_EXCEPTION) != 0;
 
140
        }
 
141
 
 
142
        bool IsIntoDeclareBlock (unsigned short int sqlStatesLine) {
 
143
                return (sqlStatesLine & MASK_INTO_DECLARE) != 0;
 
144
        }
 
145
 
 
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;
 
151
                }
 
152
        }
 
153
 
 
154
        SQLStates() {}
 
155
 
 
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
 
164
        };
 
165
};
 
166
 
 
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;
 
188
        }
 
189
};
 
190
 
 
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",
 
200
        0
 
201
};
 
202
 
 
203
struct OptionSetSQL : public OptionSet<OptionsSQL> {
 
204
        OptionSetSQL() {
 
205
                DefineProperty("fold", &OptionsSQL::fold);
 
206
 
 
207
                DefineProperty("lexer.sql.fold.at.else", &OptionsSQL::foldAtElse,
 
208
                               "This option enables SQL folding on a \"ELSE\" and \"ELSIF\"line of an IF statement.");
 
209
 
 
210
                DefineProperty("fold.comment", &OptionsSQL::foldComment);
 
211
 
 
212
                DefineProperty("fold.compact", &OptionsSQL::foldCompact);
 
213
 
 
214
                DefineProperty("fold.sql.only.begin", &OptionsSQL::foldOnlyBegin);
 
215
 
 
216
                DefineProperty("lexer.sql.backticks.identifier", &OptionsSQL::sqlBackticksIdentifier);
 
217
 
 
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.");
 
220
 
 
221
                DefineProperty("sql.backslash.escapes", &OptionsSQL::sqlBackslashEscapes,
 
222
                               "Enables backslash as an escape character in SQL.");
 
223
 
 
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).");
 
227
 
 
228
                DefineWordListSets(sqlWordListDesc);
 
229
        }
 
230
};
 
231
 
 
232
class LexerSQL : public ILexer {
 
233
public :
 
234
        LexerSQL() {}
 
235
 
 
236
        int SCI_METHOD Version () const {
 
237
                return lvOriginal;
 
238
        }
 
239
 
 
240
        void SCI_METHOD Release() {
 
241
                delete this;
 
242
        }
 
243
 
 
244
        const char * SCI_METHOD PropertyNames() {
 
245
                return osSQL.PropertyNames();
 
246
        }
 
247
 
 
248
        int SCI_METHOD PropertyType(const char *name) {
 
249
                return osSQL.PropertyType(name);
 
250
        }
 
251
 
 
252
        const char * SCI_METHOD DescribeProperty(const char *name) {
 
253
                return osSQL.DescribeProperty(name);
 
254
        }
 
255
 
 
256
        int SCI_METHOD PropertySet(const char *key, const char *val) {
 
257
                if (osSQL.PropertySet(&options, key, val)) {
 
258
                        return 0;
 
259
                }
 
260
                return -1;
 
261
        }
 
262
 
 
263
        const char * SCI_METHOD DescribeWordListSets() {
 
264
                return osSQL.DescribeWordListSets();
 
265
        }
 
266
 
 
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);
 
270
 
 
271
        void * SCI_METHOD PrivateCall(int, void *) {
 
272
                return 0;
 
273
        }
 
274
 
 
275
        static ILexer *LexerFactorySQL() {
 
276
                return new LexerSQL();
 
277
        }
 
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;
 
284
        }
 
285
 
 
286
        OptionsSQL options;
 
287
        OptionSetSQL osSQL;
 
288
        SQLStates sqlStates;
 
289
 
 
290
        WordList keywords1;
 
291
        WordList keywords2;
 
292
        WordList kw_pldoc;
 
293
        WordList kw_sqlplus;
 
294
        WordList kw_user1;
 
295
        WordList kw_user2;
 
296
        WordList kw_user3;
 
297
        WordList kw_user4;
 
298
};
 
299
 
 
300
int SCI_METHOD LexerSQL::WordListSet(int n, const char *wl) {
 
301
        WordList *wordListN = 0;
 
302
        switch (n) {
 
303
        case 0:
 
304
                wordListN = &keywords1;
 
305
                break;
 
306
        case 1:
 
307
                wordListN = &keywords2;
 
308
                break;
 
309
        case 2:
 
310
                wordListN = &kw_pldoc;
 
311
                break;
 
312
        case 3:
 
313
                wordListN = &kw_sqlplus;
 
314
                break;
 
315
        case 4:
 
316
                wordListN = &kw_user1;
 
317
                break;
 
318
        case 5:
 
319
                wordListN = &kw_user2;
 
320
                break;
 
321
        case 6:
 
322
                wordListN = &kw_user3;
 
323
                break;
 
324
        case 7:
 
325
                wordListN = &kw_user4;
 
326
        }
 
327
        int firstModification = -1;
 
328
        if (wordListN) {
 
329
                WordList wlNew;
 
330
                wlNew.Set(wl);
 
331
                if (*wordListN != wlNew) {
 
332
                        wordListN->Set(wl);
 
333
                        firstModification = 0;
 
334
                }
 
335
        }
 
336
        return firstModification;
 
337
}
 
338
 
 
339
void SCI_METHOD LexerSQL::Lex(unsigned int startPos, int length, int initStyle, IDocument *pAccess) {
 
340
        LexAccessor styler(pAccess);
 
341
        StyleContext sc(startPos, length, initStyle, styler);
 
342
        int styleBeforeDCKeyword = SCE_SQL_DEFAULT;
 
343
        int offset = 0;
 
344
        for (; sc.More(); sc.Forward(), offset++) {
 
345
                // Determine if the current state should terminate.
 
346
                switch (sc.state) {
 
347
                case SCE_SQL_OPERATOR:
 
348
                        sc.SetState(SCE_SQL_DEFAULT);
 
349
                        break;
 
350
                case SCE_SQL_NUMBER:
 
351
                        // We stop the number definition on non-numerical non-dot non-eE non-sign char
 
352
                        if (!IsANumberChar(sc.ch)) {
 
353
                                sc.SetState(SCE_SQL_DEFAULT);
 
354
                        }
 
355
                        break;
 
356
                case SCE_SQL_IDENTIFIER:
 
357
                        if (!IsAWordChar(sc.ch, options.sqlAllowDottedWord)) {
 
358
                                int nextState = SCE_SQL_DEFAULT;
 
359
                                char s[1000];
 
360
                                sc.GetCurrentLowered(s, sizeof(s));
 
361
                                if (keywords1.InList(s)) {
 
362
                                        sc.ChangeState(SCE_SQL_WORD);
 
363
                                } else if (keywords2.InList(s)) {
 
364
                                        sc.ChangeState(SCE_SQL_WORD2);
 
365
                                } else if (kw_sqlplus.InListAbbreviated(s, '~')) {
 
366
                                        sc.ChangeState(SCE_SQL_SQLPLUS);
 
367
                                        if (strncmp(s, "rem", 3) == 0) {
 
368
                                                nextState = SCE_SQL_SQLPLUS_COMMENT;
 
369
                                        } else if (strncmp(s, "pro", 3) == 0) {
 
370
                                                nextState = SCE_SQL_SQLPLUS_PROMPT;
 
371
                                        }
 
372
                                } else if (kw_user1.InList(s)) {
 
373
                                        sc.ChangeState(SCE_SQL_USER1);
 
374
                                } else if (kw_user2.InList(s)) {
 
375
                                        sc.ChangeState(SCE_SQL_USER2);
 
376
                                } else if (kw_user3.InList(s)) {
 
377
                                        sc.ChangeState(SCE_SQL_USER3);
 
378
                                } else if (kw_user4.InList(s)) {
 
379
                                        sc.ChangeState(SCE_SQL_USER4);
 
380
                                }
 
381
                                sc.SetState(nextState);
 
382
                        }
 
383
                        break;
 
384
                case SCE_SQL_QUOTEDIDENTIFIER:
 
385
                        if (sc.ch == 0x60) {
 
386
                                if (sc.chNext == 0x60) {
 
387
                                        sc.Forward();   // Ignore it
 
388
                                } else {
 
389
                                        sc.ForwardSetState(SCE_SQL_DEFAULT);
 
390
                                }
 
391
                        }
 
392
                        break;
 
393
                case SCE_SQL_COMMENT:
 
394
                        if (sc.Match('*', '/')) {
 
395
                                sc.Forward();
 
396
                                sc.ForwardSetState(SCE_SQL_DEFAULT);
 
397
                        }
 
398
                        break;
 
399
                case SCE_SQL_COMMENTDOC:
 
400
                        if (sc.Match('*', '/')) {
 
401
                                sc.Forward();
 
402
                                sc.ForwardSetState(SCE_SQL_DEFAULT);
 
403
                        } else if (sc.ch == '@' || sc.ch == '\\') { // Doxygen support
 
404
                                // Verify that we have the conditions to mark a comment-doc-keyword
 
405
                                if ((IsASpace(sc.chPrev) || sc.chPrev == '*') && (!IsASpace(sc.chNext))) {
 
406
                                        styleBeforeDCKeyword = SCE_SQL_COMMENTDOC;
 
407
                                        sc.SetState(SCE_SQL_COMMENTDOCKEYWORD);
 
408
                                }
 
409
                        }
 
410
                        break;
 
411
                case SCE_SQL_COMMENTLINE:
 
412
                case SCE_SQL_COMMENTLINEDOC:
 
413
                case SCE_SQL_SQLPLUS_COMMENT:
 
414
                case SCE_SQL_SQLPLUS_PROMPT:
 
415
                        if (sc.atLineStart) {
 
416
                                sc.SetState(SCE_SQL_DEFAULT);
 
417
                        }
 
418
                        break;
 
419
                case SCE_SQL_COMMENTDOCKEYWORD:
 
420
                        if ((styleBeforeDCKeyword == SCE_SQL_COMMENTDOC) && sc.Match('*', '/')) {
 
421
                                sc.ChangeState(SCE_SQL_COMMENTDOCKEYWORDERROR);
 
422
                                sc.Forward();
 
423
                                sc.ForwardSetState(SCE_SQL_DEFAULT);
 
424
                        } else if (!IsADoxygenChar(sc.ch)) {
 
425
                                char s[100];
 
426
                                sc.GetCurrentLowered(s, sizeof(s));
 
427
                                if (!isspace(sc.ch) || !kw_pldoc.InList(s + 1)) {
 
428
                                        sc.ChangeState(SCE_SQL_COMMENTDOCKEYWORDERROR);
 
429
                                }
 
430
                                sc.SetState(styleBeforeDCKeyword);
 
431
                        }
 
432
                        break;
 
433
                case SCE_SQL_CHARACTER:
 
434
                        if (options.sqlBackslashEscapes && sc.ch == '\\') {
 
435
                                sc.Forward();
 
436
                        } else if (sc.ch == '\'') {
 
437
                                if (sc.chNext == '\"') {
 
438
                                        sc.Forward();
 
439
                                } else {
 
440
                                        sc.ForwardSetState(SCE_SQL_DEFAULT);
 
441
                                }
 
442
                        }
 
443
                        break;
 
444
                case SCE_SQL_STRING:
 
445
                        if (sc.ch == '\\') {
 
446
                                // Escape sequence
 
447
                                sc.Forward();
 
448
                        } else if (sc.ch == '\"') {
 
449
                                if (sc.chNext == '\"') {
 
450
                                        sc.Forward();
 
451
                                } else {
 
452
                                        sc.ForwardSetState(SCE_SQL_DEFAULT);
 
453
                                }
 
454
                        }
 
455
                        break;
 
456
                }
 
457
 
 
458
                // Determine if a new state should be entered.
 
459
                if (sc.state == SCE_SQL_DEFAULT) {
 
460
                        if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
 
461
                                sc.SetState(SCE_SQL_NUMBER);
 
462
                        } else if (IsAWordStart(sc.ch)) {
 
463
                                sc.SetState(SCE_SQL_IDENTIFIER);
 
464
                        } else if (sc.ch == 0x60 && options.sqlBackticksIdentifier) {
 
465
                                sc.SetState(SCE_SQL_QUOTEDIDENTIFIER);
 
466
                        } else if (sc.Match('/', '*')) {
 
467
                                if (sc.Match("/**") || sc.Match("/*!")) {       // Support of Doxygen doc. style
 
468
                                        sc.SetState(SCE_SQL_COMMENTDOC);
 
469
                                } else {
 
470
                                        sc.SetState(SCE_SQL_COMMENT);
 
471
                                }
 
472
                                sc.Forward();   // Eat the * so it isn't used for the end of the comment
 
473
                        } else if (sc.Match('-', '-')) {
 
474
                                // MySQL requires a space or control char after --
 
475
                                // http://dev.mysql.com/doc/mysql/en/ansi-diff-comments.html
 
476
                                // Perhaps we should enforce that with proper property:
 
477
                                //~                     } else if (sc.Match("-- ")) {
 
478
                                sc.SetState(SCE_SQL_COMMENTLINE);
 
479
                        } else if (sc.ch == '#' && options.sqlNumbersignComment) {
 
480
                                sc.SetState(SCE_SQL_COMMENTLINEDOC);
 
481
                        } else if (sc.ch == '\'') {
 
482
                                sc.SetState(SCE_SQL_CHARACTER);
 
483
                        } else if (sc.ch == '\"') {
 
484
                                sc.SetState(SCE_SQL_STRING);
 
485
                        } else if (isoperator(static_cast<char>(sc.ch))) {
 
486
                                sc.SetState(SCE_SQL_OPERATOR);
 
487
                        }
 
488
                }
 
489
        }
 
490
        sc.Complete();
 
491
}
 
492
 
 
493
void SCI_METHOD LexerSQL::Fold(unsigned int startPos, int length, int initStyle, IDocument *pAccess) {
 
494
        if (!options.fold)
 
495
                return;
 
496
        LexAccessor styler(pAccess);
 
497
        unsigned int endPos = startPos + length;
 
498
        int visibleChars = 0;
 
499
        int lineCurrent = styler.GetLine(startPos);
 
500
        int levelCurrent = SC_FOLDLEVELBASE;
 
501
        if (lineCurrent > 0) {
 
502
                levelCurrent = styler.LevelAt(lineCurrent - 1) >> 16;
 
503
        }
 
504
        int levelNext = levelCurrent;
 
505
        char chNext = styler[startPos];
 
506
        int styleNext = styler.StyleAt(startPos);
 
507
        int style = initStyle;
 
508
        bool endFound = false;
 
509
        bool isUnfoldingIgnored = false;
 
510
        // this statementFound flag avoids to fold when the statement is on only one line by ignoring ELSE or ELSIF
 
511
        // eg. "IF condition1 THEN ... ELSIF condition2 THEN ... ELSE ... END IF;"
 
512
        bool statementFound = false;
 
513
        unsigned short int sqlStatesCurrentLine = 0;
 
514
        if (!options.foldOnlyBegin) {
 
515
                sqlStatesCurrentLine = sqlStates.ForLine(lineCurrent);
 
516
        }
 
517
        for (unsigned int i = startPos; i < endPos; i++) {
 
518
                char ch = chNext;
 
519
                chNext = styler.SafeGetCharAt(i + 1);
 
520
                int stylePrev = style;
 
521
                style = styleNext;
 
522
                styleNext = styler.StyleAt(i + 1);
 
523
                bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
 
524
                if (atEOL || (ch == ';')) {
 
525
                        if (endFound) {
 
526
                                //Maybe this is the end of "EXCEPTION" BLOCK (eg. "BEGIN ... EXCEPTION ... END;")
 
527
                                sqlStatesCurrentLine = sqlStates.IntoExceptionBlock(sqlStatesCurrentLine, false);
 
528
                        }
 
529
                        // set endFound and isUnfoldingIgnored to false if EOL is reached or ';' is found
 
530
                        endFound = false;
 
531
                        isUnfoldingIgnored = false;
 
532
                }
 
533
                if (options.foldComment && IsStreamCommentStyle(style)) {
 
534
                        if (!IsStreamCommentStyle(stylePrev)) {
 
535
                                levelNext++;
 
536
                        } else if (!IsStreamCommentStyle(styleNext) && !atEOL) {
 
537
                                // Comments don't end at end of line and the next character may be unstyled.
 
538
                                levelNext--;
 
539
                        }
 
540
                }
 
541
                if (options.foldComment && (style == SCE_SQL_COMMENTLINE)) {
 
542
                        // MySQL needs -- comments to be followed by space or control char
 
543
                        if ((ch == '-') && (chNext == '-')) {
 
544
                                char chNext2 = styler.SafeGetCharAt(i + 2);
 
545
                                char chNext3 = styler.SafeGetCharAt(i + 3);
 
546
                                if (chNext2 == '{' || chNext3 == '{') {
 
547
                                        levelNext++;
 
548
                                } else if (chNext2 == '}' || chNext3 == '}') {
 
549
                                        levelNext--;
 
550
                                }
 
551
                        }
 
552
                }
 
553
                if (style == SCE_SQL_OPERATOR) {
 
554
                        if (ch == '(') {
 
555
                                if (levelCurrent > levelNext)
 
556
                                        levelCurrent--;
 
557
                                levelNext++;
 
558
                        } else if (ch == ')') {
 
559
                                levelNext--;
 
560
                        } else if ((!options.foldOnlyBegin) && ch == ';') {
 
561
                                sqlStatesCurrentLine = sqlStates.IgnoreWhen(sqlStatesCurrentLine, false);
 
562
                        }
 
563
                }
 
564
                // If new keyword (cannot trigger on elseif or nullif, does less tests)
 
565
                if (style == SCE_SQL_WORD && stylePrev != SCE_SQL_WORD) {
 
566
                        const int MAX_KW_LEN = 9;       // Maximum length of folding keywords
 
567
                        char s[MAX_KW_LEN + 2];
 
568
                        unsigned int j = 0;
 
569
                        for (; j < MAX_KW_LEN + 1; j++) {
 
570
                                if (!iswordchar(styler[i + j])) {
 
571
                                        break;
 
572
                                }
 
573
                                s[j] = static_cast<char>(tolower(styler[i + j]));
 
574
                        }
 
575
                        if (j == MAX_KW_LEN + 1) {
 
576
                                // Keyword too long, don't test it
 
577
                                s[0] = '\0';
 
578
                        } else {
 
579
                                s[j] = '\0';
 
580
                        }
 
581
 
 
582
                        if (strcmp(s, "if") == 0) {
 
583
                                if (endFound) {
 
584
                                        endFound = false;
 
585
                                        if (options.foldOnlyBegin && !isUnfoldingIgnored) {
 
586
                                                // this end isn't for begin block, but for if block ("end if;")
 
587
                                                // so ignore previous "end" by increment levelNext.
 
588
                                                levelNext++;
 
589
                                        }
 
590
                                } else {
 
591
                                        if (!options.foldOnlyBegin)
 
592
                                                sqlStatesCurrentLine = sqlStates.IntoCondition(sqlStatesCurrentLine, true);
 
593
                                        if (levelCurrent > levelNext) {
 
594
                                                // doesn't include this line into the folding block
 
595
                                                // because doesn't hide IF (eg "END; IF")
 
596
                                                levelCurrent = levelNext;
 
597
                                        }
 
598
                                }
 
599
                        } else if (!options.foldOnlyBegin &&
 
600
                                   strcmp(s, "then") == 0 &&
 
601
                                   sqlStates.IsIntoCondition(sqlStatesCurrentLine)) {
 
602
                                sqlStatesCurrentLine = sqlStates.IntoCondition(sqlStatesCurrentLine, false);
 
603
                                if (!options.foldOnlyBegin) {
 
604
                                        if (levelCurrent > levelNext) {
 
605
                                                levelCurrent = levelNext;
 
606
                                        }
 
607
                                        if (!statementFound)
 
608
                                                levelNext++;
 
609
 
 
610
                                        statementFound = true;
 
611
                                } else if (levelCurrent > levelNext) {
 
612
                                        // doesn't include this line into the folding block
 
613
                                        // because doesn't hide LOOP or CASE (eg "END; LOOP" or "END; CASE")
 
614
                                        levelCurrent = levelNext;
 
615
                                }
 
616
                        } else if (strcmp(s, "loop") == 0 ||
 
617
                                   strcmp(s, "case") == 0) {
 
618
                                if (endFound) {
 
619
                                        endFound = false;
 
620
                                        if (options.foldOnlyBegin && !isUnfoldingIgnored) {
 
621
                                                // this end isn't for begin block, but for loop block ("end loop;") or case block ("end case;")
 
622
                                                // so ignore previous "end" by increment levelNext.
 
623
                                                levelNext++;
 
624
                                        }
 
625
                                        if ((!options.foldOnlyBegin) && strcmp(s, "case") == 0) {
 
626
                                                sqlStatesCurrentLine = sqlStates.EndCaseBlock(sqlStatesCurrentLine);
 
627
                                                levelNext--; //again for the "end case;" and block when
 
628
                                        }
 
629
                                } else if (!options.foldOnlyBegin) {
 
630
                                        if (strcmp(s, "case") == 0) {
 
631
                                                sqlStatesCurrentLine = sqlStates.BeginCaseBlock(sqlStatesCurrentLine);
 
632
 
 
633
                                                //for case block increment 2 times
 
634
                                                if (!statementFound)
 
635
                                                        levelNext++;
 
636
                                        }
 
637
 
 
638
                                        if (levelCurrent > levelNext) {
 
639
                                                levelCurrent = levelNext;
 
640
                                        }
 
641
                                        if (!statementFound)
 
642
                                                levelNext++;
 
643
 
 
644
                                        statementFound = true;
 
645
                                } else if (levelCurrent > levelNext) {
 
646
                                        // doesn't include this line into the folding block
 
647
                                        // because doesn't hide LOOP or CASE (eg "END; LOOP" or "END; CASE")
 
648
                                        levelCurrent = levelNext;
 
649
                                }
 
650
                        } else if ((!options.foldOnlyBegin) && (
 
651
                                       // folding for ELSE and ELSIF block only if foldAtElse is set
 
652
                                       // and IF or CASE aren't on only one line with ELSE or ELSIF (with flag statementFound)
 
653
                                       options.foldAtElse && !statementFound) && strcmp(s, "elsif") == 0) {
 
654
                                sqlStatesCurrentLine = sqlStates.IntoCondition(sqlStatesCurrentLine, true);
 
655
                                levelCurrent--;
 
656
                                levelNext--;
 
657
                        } else if ((!options.foldOnlyBegin) && (
 
658
                                       // folding for ELSE and ELSIF block only if foldAtElse is set
 
659
                                       // and IF or CASE aren't on only one line with ELSE or ELSIF (with flag statementFound)
 
660
                                       options.foldAtElse && !statementFound) && strcmp(s, "else") == 0) {
 
661
                                // prevent also ELSE is on the same line (eg. "ELSE ... END IF;")
 
662
                                statementFound = true;
 
663
                                // we are in same case "} ELSE {" in C language
 
664
                                levelCurrent--;
 
665
 
 
666
                        } else if (strcmp(s, "begin") == 0) {
 
667
                                levelNext++;
 
668
                                sqlStatesCurrentLine = sqlStates.IntoDeclareBlock(sqlStatesCurrentLine, false);
 
669
                        } else if ((strcmp(s, "end") == 0) ||
 
670
                                   // SQL Anywhere permits IF ... ELSE ... ENDIF
 
671
                                   // will only be active if "endif" appears in the
 
672
                                   // keyword list.
 
673
                                   (strcmp(s, "endif") == 0)) {
 
674
                                endFound = true;
 
675
                                levelNext--;
 
676
                                if (levelNext < SC_FOLDLEVELBASE) {
 
677
                                        levelNext = SC_FOLDLEVELBASE;
 
678
                                        isUnfoldingIgnored = true;
 
679
                                }
 
680
                        } else if ((!options.foldOnlyBegin) &&
 
681
                                   strcmp(s, "when") == 0 &&
 
682
                                   !sqlStates.IsIgnoreWhen(sqlStatesCurrentLine) &&
 
683
                                   !sqlStates.IsIntoExceptionBlock(sqlStatesCurrentLine) &&
 
684
                                   sqlStates.IsIntoCaseBlock(sqlStatesCurrentLine)) {
 
685
                                sqlStatesCurrentLine = sqlStates.IntoCondition(sqlStatesCurrentLine, true);
 
686
 
 
687
                                // 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")
 
688
                                if (!statementFound) {
 
689
                                        levelCurrent--;
 
690
                                        levelNext--;
 
691
                                }
 
692
                        } else if ((!options.foldOnlyBegin) && strcmp(s, "exit") == 0) {
 
693
                                sqlStatesCurrentLine = sqlStates.IgnoreWhen(sqlStatesCurrentLine, true);
 
694
                        } else if ((!options.foldOnlyBegin) && !sqlStates.IsIntoDeclareBlock(sqlStatesCurrentLine) && strcmp(s, "exception") == 0) {
 
695
                                sqlStatesCurrentLine = sqlStates.IntoExceptionBlock(sqlStatesCurrentLine, true);
 
696
                        } else if ((!options.foldOnlyBegin) &&
 
697
                                   (strcmp(s, "declare") == 0 ||
 
698
                                    strcmp(s, "function") == 0 ||
 
699
                                    strcmp(s, "procedure") == 0 ||
 
700
                                    strcmp(s, "package") == 0)) {
 
701
                                sqlStatesCurrentLine = sqlStates.IntoDeclareBlock(sqlStatesCurrentLine, true);
 
702
                        }
 
703
                }
 
704
                if (atEOL) {
 
705
                        int levelUse = levelCurrent;
 
706
                        int lev = levelUse | levelNext << 16;
 
707
                        if (visibleChars == 0 && options.foldCompact)
 
708
                                lev |= SC_FOLDLEVELWHITEFLAG;
 
709
                        if (levelUse < levelNext)
 
710
                                lev |= SC_FOLDLEVELHEADERFLAG;
 
711
                        if (lev != styler.LevelAt(lineCurrent)) {
 
712
                                styler.SetLevel(lineCurrent, lev);
 
713
                        }
 
714
                        lineCurrent++;
 
715
                        levelCurrent = levelNext;
 
716
                        visibleChars = 0;
 
717
                        statementFound = false;
 
718
                        if (!options.foldOnlyBegin)
 
719
                                sqlStates.Set(lineCurrent, sqlStatesCurrentLine);
 
720
                }
 
721
                if (!isspacechar(ch)) {
 
722
                        visibleChars++;
 
723
                }
 
724
        }
 
725
}
 
726
 
 
727
LexerModule lmSQL(SCLEX_SQL, LexerSQL::LexerFactorySQL, "sql", sqlWordListDesc);