~ubuntu-branches/ubuntu/wily/tora/wily-proposed

« back to all changes in this revision

Viewing changes to src/qscintilla2/src/LexSQL.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Michael Meskes
  • Date: 2009-11-19 15:18:19 UTC
  • mfrom: (1.2.9 upstream) (3.3.3 squeeze)
  • Revision ID: james.westby@ubuntu.com-20091119151819-me89ezmxzkvl0lws
Tags: 2.1.1-1
New upstream version.

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-2005 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 <ctype.h>
 
11
#include <stdio.h>
 
12
#include <stdarg.h>
 
13
 
 
14
#include "Platform.h"
 
15
 
 
16
#include "PropSet.h"
 
17
#include "Accessor.h"
 
18
#include "StyleContext.h"
 
19
#include "KeyWords.h"
 
20
#include "Scintilla.h"
 
21
#include "SciLexer.h"
 
22
 
 
23
#ifdef SCI_NAMESPACE
 
24
using namespace Scintilla;
 
25
#endif
 
26
 
 
27
static inline bool IsAWordChar(int ch) {
 
28
        return (ch < 0x80) && (isalnum(ch) || ch == '_');
 
29
}
 
30
 
 
31
static inline bool IsAWordStart(int ch) {
 
32
        return (ch < 0x80) && (isalpha(ch) || ch == '_');
 
33
}
 
34
 
 
35
static inline bool IsADoxygenChar(int ch) {
 
36
        return (islower(ch) || ch == '$' || ch == '@' ||
 
37
                ch == '\\' || ch == '&' || ch == '<' ||
 
38
                ch == '>' || ch == '#' || ch == '{' ||
 
39
                ch == '}' || ch == '[' || ch == ']');
 
40
}
 
41
 
 
42
static inline bool IsANumberChar(int ch) {
 
43
        // Not exactly following number definition (several dots are seen as OK, etc.)
 
44
        // but probably enough in most cases.
 
45
        return (ch < 0x80) &&
 
46
                (isdigit(ch) || toupper(ch) == 'E' ||
 
47
             ch == '.' || ch == '-' || ch == '+');
 
48
}
 
49
 
 
50
static void ColouriseSQLDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
 
51
                            Accessor &styler) {
 
52
 
 
53
        WordList &keywords1 = *keywordlists[0];
 
54
        WordList &keywords2 = *keywordlists[1];
 
55
        WordList &kw_pldoc = *keywordlists[2];
 
56
        WordList &kw_sqlplus = *keywordlists[3];
 
57
        WordList &kw_user1 = *keywordlists[4];
 
58
        WordList &kw_user2 = *keywordlists[5];
 
59
        WordList &kw_user3 = *keywordlists[6];
 
60
        WordList &kw_user4 = *keywordlists[7];
 
61
 
 
62
        StyleContext sc(startPos, length, initStyle, styler);
 
63
 
 
64
        bool sqlBackslashEscapes = styler.GetPropertyInt("sql.backslash.escapes", 0) != 0;
 
65
        bool sqlBackticksIdentifier = styler.GetPropertyInt("lexer.sql.backticks.identifier", 0) != 0;
 
66
        int styleBeforeDCKeyword = SCE_SQL_DEFAULT;
 
67
        for (; sc.More(); sc.Forward()) {
 
68
                // Determine if the current state should terminate.
 
69
                switch (sc.state) {
 
70
                case SCE_SQL_OPERATOR:
 
71
                        sc.SetState(SCE_SQL_DEFAULT);
 
72
                        break;
 
73
                case SCE_SQL_NUMBER:
 
74
                        // We stop the number definition on non-numerical non-dot non-eE non-sign char
 
75
                        if (!IsANumberChar(sc.ch)) {
 
76
                                sc.SetState(SCE_SQL_DEFAULT);
 
77
                        }
 
78
                        break;
 
79
                case SCE_SQL_IDENTIFIER:
 
80
                        if (!IsAWordChar(sc.ch)) {
 
81
                                int nextState = SCE_SQL_DEFAULT;
 
82
                                char s[1000];
 
83
                                sc.GetCurrentLowered(s, sizeof(s));
 
84
                                if (keywords1.InList(s)) {
 
85
                                        sc.ChangeState(SCE_SQL_WORD);
 
86
                                } else if (keywords2.InList(s)) {
 
87
                                        sc.ChangeState(SCE_SQL_WORD2);
 
88
                                } else if (kw_sqlplus.InListAbbreviated(s, '~')) {
 
89
                                        sc.ChangeState(SCE_SQL_SQLPLUS);
 
90
                                        if (strncmp(s, "rem", 3) == 0) {
 
91
                                                nextState = SCE_SQL_SQLPLUS_COMMENT;
 
92
                                        } else if (strncmp(s, "pro", 3) == 0) {
 
93
                                                nextState = SCE_SQL_SQLPLUS_PROMPT;
 
94
                                        }
 
95
                                } else if (kw_user1.InList(s)) {
 
96
                                        sc.ChangeState(SCE_SQL_USER1);
 
97
                                } else if (kw_user2.InList(s)) {
 
98
                                        sc.ChangeState(SCE_SQL_USER2);
 
99
                                } else if (kw_user3.InList(s)) {
 
100
                                        sc.ChangeState(SCE_SQL_USER3);
 
101
                                } else if (kw_user4.InList(s)) {
 
102
                                        sc.ChangeState(SCE_SQL_USER4);
 
103
                                }
 
104
                                sc.SetState(nextState);
 
105
                        }
 
106
                        break;
 
107
                case SCE_SQL_QUOTEDIDENTIFIER:
 
108
                        if (sc.ch == 0x60) {
 
109
                                if (sc.chNext == 0x60) {
 
110
                                        sc.Forward();   // Ignore it
 
111
                                } else {
 
112
                                        sc.ForwardSetState(SCE_SQL_DEFAULT);
 
113
                                }
 
114
                        }
 
115
                        break;
 
116
                case SCE_SQL_COMMENT:
 
117
                        if (sc.Match('*', '/')) {
 
118
                                sc.Forward();
 
119
                                sc.ForwardSetState(SCE_SQL_DEFAULT);
 
120
                        }
 
121
                        break;
 
122
                case SCE_SQL_COMMENTDOC:
 
123
                        if (sc.Match('*', '/')) {
 
124
                                sc.Forward();
 
125
                                sc.ForwardSetState(SCE_SQL_DEFAULT);
 
126
                        } else if (sc.ch == '@' || sc.ch == '\\') { // Doxygen support
 
127
                                // Verify that we have the conditions to mark a comment-doc-keyword
 
128
                                if ((IsASpace(sc.chPrev) || sc.chPrev == '*') && (!IsASpace(sc.chNext))) {
 
129
                                        styleBeforeDCKeyword = SCE_SQL_COMMENTDOC;
 
130
                                        sc.SetState(SCE_SQL_COMMENTDOCKEYWORD);
 
131
                                }
 
132
                        }
 
133
                        break;
 
134
                case SCE_SQL_COMMENTLINE:
 
135
                case SCE_SQL_COMMENTLINEDOC:
 
136
                case SCE_SQL_SQLPLUS_COMMENT:
 
137
                case SCE_SQL_SQLPLUS_PROMPT:
 
138
                        if (sc.atLineStart) {
 
139
                                sc.SetState(SCE_SQL_DEFAULT);
 
140
                        }
 
141
                        break;
 
142
                case SCE_SQL_COMMENTDOCKEYWORD:
 
143
                        if ((styleBeforeDCKeyword == SCE_SQL_COMMENTDOC) && sc.Match('*', '/')) {
 
144
                                sc.ChangeState(SCE_SQL_COMMENTDOCKEYWORDERROR);
 
145
                                sc.Forward();
 
146
                                sc.ForwardSetState(SCE_SQL_DEFAULT);
 
147
                        } else if (!IsADoxygenChar(sc.ch)) {
 
148
                                char s[100];
 
149
                                sc.GetCurrentLowered(s, sizeof(s));
 
150
                                if (!isspace(sc.ch) || !kw_pldoc.InList(s + 1)) {
 
151
                                        sc.ChangeState(SCE_SQL_COMMENTDOCKEYWORDERROR);
 
152
                                }
 
153
                                sc.SetState(styleBeforeDCKeyword);
 
154
                        }
 
155
                        break;
 
156
                case SCE_SQL_CHARACTER:
 
157
                        if (sqlBackslashEscapes && sc.ch == '\\') {
 
158
                                sc.Forward();
 
159
                        } else if (sc.ch == '\'') {
 
160
                                if (sc.chNext == '\"') {
 
161
                                        sc.Forward();
 
162
                                } else {
 
163
                                        sc.ForwardSetState(SCE_SQL_DEFAULT);
 
164
                                }
 
165
                        }
 
166
                        break;
 
167
                case SCE_SQL_STRING:
 
168
                        if (sc.ch == '\\') {
 
169
                                // Escape sequence
 
170
                                sc.Forward();
 
171
                        } else if (sc.ch == '\"') {
 
172
                                if (sc.chNext == '\"') {
 
173
                                        sc.Forward();
 
174
                                } else {
 
175
                                        sc.ForwardSetState(SCE_SQL_DEFAULT);
 
176
                                }
 
177
                        }
 
178
                        break;
 
179
                }
 
180
 
 
181
                // Determine if a new state should be entered.
 
182
                if (sc.state == SCE_SQL_DEFAULT) {
 
183
                        if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
 
184
                                sc.SetState(SCE_SQL_NUMBER);
 
185
                        } else if (IsAWordStart(sc.ch)) {
 
186
                                sc.SetState(SCE_SQL_IDENTIFIER);
 
187
                        } else if (sc.ch == 0x60 && sqlBackticksIdentifier) {
 
188
                                sc.SetState(SCE_SQL_QUOTEDIDENTIFIER);
 
189
                        } else if (sc.Match('/', '*')) {
 
190
                                if (sc.Match("/**") || sc.Match("/*!")) {       // Support of Doxygen doc. style
 
191
                                        sc.SetState(SCE_SQL_COMMENTDOC);
 
192
                                } else {
 
193
                                        sc.SetState(SCE_SQL_COMMENT);
 
194
                                }
 
195
                                sc.Forward();   // Eat the * so it isn't used for the end of the comment
 
196
                        } else if (sc.Match('-', '-')) {
 
197
                                // MySQL requires a space or control char after --
 
198
                                // http://dev.mysql.com/doc/mysql/en/ansi-diff-comments.html
 
199
                                // Perhaps we should enforce that with proper property:
 
200
//~                     } else if (sc.Match("-- ")) {
 
201
                                sc.SetState(SCE_SQL_COMMENTLINE);
 
202
                        } else if (sc.ch == '#') {
 
203
                                sc.SetState(SCE_SQL_COMMENTLINEDOC);
 
204
                        } else if (sc.ch == '\'') {
 
205
                                sc.SetState(SCE_SQL_CHARACTER);
 
206
                        } else if (sc.ch == '\"') {
 
207
                                sc.SetState(SCE_SQL_STRING);
 
208
                        } else if (isoperator(static_cast<char>(sc.ch))) {
 
209
                                sc.SetState(SCE_SQL_OPERATOR);
 
210
                        }
 
211
                }
 
212
        }
 
213
        sc.Complete();
 
214
}
 
215
 
 
216
static bool IsStreamCommentStyle(int style) {
 
217
        return style == SCE_SQL_COMMENT ||
 
218
               style == SCE_SQL_COMMENTDOC ||
 
219
               style == SCE_SQL_COMMENTDOCKEYWORD ||
 
220
               style == SCE_SQL_COMMENTDOCKEYWORDERROR;
 
221
}
 
222
 
 
223
// Store both the current line's fold level and the next lines in the
 
224
// level store to make it easy to pick up with each increment.
 
225
static void FoldSQLDoc(unsigned int startPos, int length, int initStyle,
 
226
                            WordList *[], Accessor &styler) {
 
227
        bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
 
228
        bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
 
229
        bool foldOnlyBegin = styler.GetPropertyInt("fold.sql.only.begin", 0) != 0;
 
230
 
 
231
        unsigned int endPos = startPos + length;
 
232
        int visibleChars = 0;
 
233
        int lineCurrent = styler.GetLine(startPos);
 
234
        int levelCurrent = SC_FOLDLEVELBASE;
 
235
        if (lineCurrent > 0) {
 
236
                levelCurrent = styler.LevelAt(lineCurrent - 1) >> 16;
 
237
        }
 
238
        int levelNext = levelCurrent;
 
239
        char chNext = styler[startPos];
 
240
        int styleNext = styler.StyleAt(startPos);
 
241
        int style = initStyle;
 
242
        bool endFound = false;
 
243
        for (unsigned int i = startPos; i < endPos; i++) {
 
244
                char ch = chNext;
 
245
                chNext = styler.SafeGetCharAt(i + 1);
 
246
                int stylePrev = style;
 
247
                style = styleNext;
 
248
                styleNext = styler.StyleAt(i + 1);
 
249
                bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
 
250
                if (foldComment && IsStreamCommentStyle(style)) {
 
251
                        if (!IsStreamCommentStyle(stylePrev)) {
 
252
                                levelNext++;
 
253
                        } else if (!IsStreamCommentStyle(styleNext) && !atEOL) {
 
254
                                // Comments don't end at end of line and the next character may be unstyled.
 
255
                                levelNext--;
 
256
                        }
 
257
                }
 
258
                if (foldComment && (style == SCE_SQL_COMMENTLINE)) {
 
259
                        // MySQL needs -- comments to be followed by space or control char
 
260
                        if ((ch == '-') && (chNext == '-')) {
 
261
                                char chNext2 = styler.SafeGetCharAt(i + 2);
 
262
                                char chNext3 = styler.SafeGetCharAt(i + 3);
 
263
                                if (chNext2 == '{' || chNext3 == '{') {
 
264
                                        levelNext++;
 
265
                                } else if (chNext2 == '}' || chNext3 == '}') {
 
266
                                        levelNext--;
 
267
                                }
 
268
                        }
 
269
                }
 
270
                if (style == SCE_SQL_OPERATOR) {
 
271
                        if (ch == '(') {
 
272
                                levelNext++;
 
273
                        } else if (ch == ')') {
 
274
                                levelNext--;
 
275
                        }
 
276
                }
 
277
                // If new keyword (cannot trigger on elseif or nullif, does less tests)
 
278
                if (style == SCE_SQL_WORD && stylePrev != SCE_SQL_WORD) {
 
279
                        const int MAX_KW_LEN = 6;       // Maximum length of folding keywords
 
280
                        char s[MAX_KW_LEN + 2];
 
281
                        unsigned int j = 0;
 
282
                        for (; j < MAX_KW_LEN + 1; j++) {
 
283
                                if (!iswordchar(styler[i + j])) {
 
284
                                        break;
 
285
                                }
 
286
                                s[j] = static_cast<char>(tolower(styler[i + j]));
 
287
                        }
 
288
                        if (j == MAX_KW_LEN + 1) {
 
289
                                // Keyword too long, don't test it
 
290
                                s[0] = '\0';
 
291
                        } else {
 
292
                                s[j] = '\0';
 
293
                        }
 
294
                        if ((!foldOnlyBegin) && (strcmp(s, "if") == 0 || strcmp(s, "loop") == 0)) {
 
295
                                if (endFound) {
 
296
                                        // ignore
 
297
                                        endFound = false;
 
298
                                } else {
 
299
                                        levelNext++;
 
300
                                }
 
301
                        } else if (strcmp(s, "begin") == 0) {
 
302
                                levelNext++;
 
303
                        } else if (strcmp(s, "end") == 0 ||
 
304
                                                // DROP TABLE IF EXISTS or CREATE TABLE IF NOT EXISTS
 
305
                                                strcmp(s, "exists") == 0) {
 
306
                                endFound = true;
 
307
                                levelNext--;
 
308
                                if (levelNext < SC_FOLDLEVELBASE) {
 
309
                                        levelNext = SC_FOLDLEVELBASE;
 
310
                                }
 
311
                        }
 
312
                }
 
313
                if (atEOL) {
 
314
                        int levelUse = levelCurrent;
 
315
                        int lev = levelUse | levelNext << 16;
 
316
                        if (visibleChars == 0 && foldCompact)
 
317
                                lev |= SC_FOLDLEVELWHITEFLAG;
 
318
                        if (levelUse < levelNext)
 
319
                                lev |= SC_FOLDLEVELHEADERFLAG;
 
320
                        if (lev != styler.LevelAt(lineCurrent)) {
 
321
                                styler.SetLevel(lineCurrent, lev);
 
322
                        }
 
323
                        lineCurrent++;
 
324
                        levelCurrent = levelNext;
 
325
                        visibleChars = 0;
 
326
                        endFound = false;
 
327
                }
 
328
                if (!isspacechar(ch)) {
 
329
                        visibleChars++;
 
330
                }
 
331
        }
 
332
}
 
333
 
 
334
static const char * const sqlWordListDesc[] = {
 
335
        "Keywords",
 
336
        "Database Objects",
 
337
        "PLDoc",
 
338
        "SQL*Plus",
 
339
        "User Keywords 1",
 
340
        "User Keywords 2",
 
341
        "User Keywords 3",
 
342
        "User Keywords 4",
 
343
        0
 
344
};
 
345
 
 
346
LexerModule lmSQL(SCLEX_SQL, ColouriseSQLDoc, "sql", FoldSQLDoc, sqlWordListDesc);