updated Scintilla to 2.29
[TortoiseGit.git] / ext / scintilla / lexers / LexMSSQL.cxx
blobf2cf6ed8df722ed8763ca60c5f7b00ba90dcb6bd
1 // Scintilla source code edit control
2 /** @file LexMSSQL.cxx
3 ** Lexer for MSSQL.
4 **/
5 // By Filip Yaghob <fyaghob@gmail.com>
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 #include "ILexer.h"
16 #include "Scintilla.h"
17 #include "SciLexer.h"
19 #include "WordList.h"
20 #include "LexAccessor.h"
21 #include "Accessor.h"
22 #include "StyleContext.h"
23 #include "CharacterSet.h"
24 #include "LexerModule.h"
26 #ifdef SCI_NAMESPACE
27 using namespace Scintilla;
28 #endif
30 #define KW_MSSQL_STATEMENTS 0
31 #define KW_MSSQL_DATA_TYPES 1
32 #define KW_MSSQL_SYSTEM_TABLES 2
33 #define KW_MSSQL_GLOBAL_VARIABLES 3
34 #define KW_MSSQL_FUNCTIONS 4
35 #define KW_MSSQL_STORED_PROCEDURES 5
36 #define KW_MSSQL_OPERATORS 6
38 static bool isMSSQLOperator(char ch) {
39 if (isascii(ch) && isalnum(ch))
40 return false;
41 // '.' left out as it is used to make up numbers
42 if (ch == '%' || ch == '^' || ch == '&' || ch == '*' ||
43 ch == '-' || ch == '+' || ch == '=' || ch == '|' ||
44 ch == '<' || ch == '>' || ch == '/' ||
45 ch == '!' || ch == '~' || ch == '(' || ch == ')' ||
46 ch == ',')
47 return true;
48 return false;
51 static char classifyWordSQL(unsigned int start,
52 unsigned int end,
53 WordList *keywordlists[],
54 Accessor &styler,
55 unsigned int actualState,
56 unsigned int prevState) {
57 char s[256];
58 bool wordIsNumber = isdigit(styler[start]) || (styler[start] == '.');
60 WordList &kwStatements = *keywordlists[KW_MSSQL_STATEMENTS];
61 WordList &kwDataTypes = *keywordlists[KW_MSSQL_DATA_TYPES];
62 WordList &kwSystemTables = *keywordlists[KW_MSSQL_SYSTEM_TABLES];
63 WordList &kwGlobalVariables = *keywordlists[KW_MSSQL_GLOBAL_VARIABLES];
64 WordList &kwFunctions = *keywordlists[KW_MSSQL_FUNCTIONS];
65 WordList &kwStoredProcedures = *keywordlists[KW_MSSQL_STORED_PROCEDURES];
66 WordList &kwOperators = *keywordlists[KW_MSSQL_OPERATORS];
68 for (unsigned int i = 0; i < end - start + 1 && i < 128; i++) {
69 s[i] = static_cast<char>(tolower(styler[start + i]));
70 s[i + 1] = '\0';
72 char chAttr = SCE_MSSQL_IDENTIFIER;
74 if (actualState == SCE_MSSQL_GLOBAL_VARIABLE) {
76 if (kwGlobalVariables.InList(&s[2]))
77 chAttr = SCE_MSSQL_GLOBAL_VARIABLE;
79 } else if (wordIsNumber) {
80 chAttr = SCE_MSSQL_NUMBER;
82 } else if (prevState == SCE_MSSQL_DEFAULT_PREF_DATATYPE) {
83 // Look first in datatypes
84 if (kwDataTypes.InList(s))
85 chAttr = SCE_MSSQL_DATATYPE;
86 else if (kwOperators.InList(s))
87 chAttr = SCE_MSSQL_OPERATOR;
88 else if (kwStatements.InList(s))
89 chAttr = SCE_MSSQL_STATEMENT;
90 else if (kwSystemTables.InList(s))
91 chAttr = SCE_MSSQL_SYSTABLE;
92 else if (kwFunctions.InList(s))
93 chAttr = SCE_MSSQL_FUNCTION;
94 else if (kwStoredProcedures.InList(s))
95 chAttr = SCE_MSSQL_STORED_PROCEDURE;
97 } else {
98 if (kwOperators.InList(s))
99 chAttr = SCE_MSSQL_OPERATOR;
100 else if (kwStatements.InList(s))
101 chAttr = SCE_MSSQL_STATEMENT;
102 else if (kwSystemTables.InList(s))
103 chAttr = SCE_MSSQL_SYSTABLE;
104 else if (kwFunctions.InList(s))
105 chAttr = SCE_MSSQL_FUNCTION;
106 else if (kwStoredProcedures.InList(s))
107 chAttr = SCE_MSSQL_STORED_PROCEDURE;
108 else if (kwDataTypes.InList(s))
109 chAttr = SCE_MSSQL_DATATYPE;
112 styler.ColourTo(end, chAttr);
114 return chAttr;
117 static void ColouriseMSSQLDoc(unsigned int startPos, int length,
118 int initStyle, WordList *keywordlists[], Accessor &styler) {
121 styler.StartAt(startPos);
123 bool fold = styler.GetPropertyInt("fold") != 0;
124 int lineCurrent = styler.GetLine(startPos);
125 int spaceFlags = 0;
127 int state = initStyle;
128 int prevState = initStyle;
129 char chPrev = ' ';
130 char chNext = styler[startPos];
131 styler.StartSegment(startPos);
132 unsigned int lengthDoc = startPos + length;
133 for (unsigned int i = startPos; i < lengthDoc; i++) {
134 char ch = chNext;
135 chNext = styler.SafeGetCharAt(i + 1);
137 if ((ch == '\r' && chNext != '\n') || (ch == '\n')) {
138 int indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags);
139 int lev = indentCurrent;
140 if (!(indentCurrent & SC_FOLDLEVELWHITEFLAG)) {
141 // Only non whitespace lines can be headers
142 int indentNext = styler.IndentAmount(lineCurrent + 1, &spaceFlags);
143 if (indentCurrent < (indentNext & ~SC_FOLDLEVELWHITEFLAG)) {
144 lev |= SC_FOLDLEVELHEADERFLAG;
147 if (fold) {
148 styler.SetLevel(lineCurrent, lev);
152 if (styler.IsLeadByte(ch)) {
153 chNext = styler.SafeGetCharAt(i + 2);
154 chPrev = ' ';
155 i += 1;
156 continue;
159 // When the last char isn't part of the state (have to deal with it too)...
160 if ( (state == SCE_MSSQL_IDENTIFIER) ||
161 (state == SCE_MSSQL_STORED_PROCEDURE) ||
162 (state == SCE_MSSQL_DATATYPE) ||
163 //~ (state == SCE_MSSQL_COLUMN_NAME) ||
164 (state == SCE_MSSQL_FUNCTION) ||
165 //~ (state == SCE_MSSQL_GLOBAL_VARIABLE) ||
166 (state == SCE_MSSQL_VARIABLE)) {
167 if (!iswordchar(ch)) {
168 int stateTmp;
170 if ((state == SCE_MSSQL_VARIABLE) || (state == SCE_MSSQL_COLUMN_NAME)) {
171 styler.ColourTo(i - 1, state);
172 stateTmp = state;
173 } else
174 stateTmp = classifyWordSQL(styler.GetStartSegment(), i - 1, keywordlists, styler, state, prevState);
176 prevState = state;
178 if (stateTmp == SCE_MSSQL_IDENTIFIER || stateTmp == SCE_MSSQL_VARIABLE)
179 state = SCE_MSSQL_DEFAULT_PREF_DATATYPE;
180 else
181 state = SCE_MSSQL_DEFAULT;
183 } else if (state == SCE_MSSQL_LINE_COMMENT) {
184 if (ch == '\r' || ch == '\n') {
185 styler.ColourTo(i - 1, state);
186 prevState = state;
187 state = SCE_MSSQL_DEFAULT;
189 } else if (state == SCE_MSSQL_GLOBAL_VARIABLE) {
190 if ((ch != '@') && !iswordchar(ch)) {
191 classifyWordSQL(styler.GetStartSegment(), i - 1, keywordlists, styler, state, prevState);
192 prevState = state;
193 state = SCE_MSSQL_DEFAULT;
197 // If is the default or one of the above succeeded
198 if (state == SCE_MSSQL_DEFAULT || state == SCE_MSSQL_DEFAULT_PREF_DATATYPE) {
199 if (iswordstart(ch)) {
200 styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT);
201 prevState = state;
202 state = SCE_MSSQL_IDENTIFIER;
203 } else if (ch == '/' && chNext == '*') {
204 styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT);
205 prevState = state;
206 state = SCE_MSSQL_COMMENT;
207 } else if (ch == '-' && chNext == '-') {
208 styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT);
209 prevState = state;
210 state = SCE_MSSQL_LINE_COMMENT;
211 } else if (ch == '\'') {
212 styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT);
213 prevState = state;
214 state = SCE_MSSQL_STRING;
215 } else if (ch == '"') {
216 styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT);
217 prevState = state;
218 state = SCE_MSSQL_COLUMN_NAME;
219 } else if (ch == '[') {
220 styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT);
221 prevState = state;
222 state = SCE_MSSQL_COLUMN_NAME_2;
223 } else if (isMSSQLOperator(ch)) {
224 styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT);
225 styler.ColourTo(i, SCE_MSSQL_OPERATOR);
226 //~ style = SCE_MSSQL_DEFAULT;
227 prevState = state;
228 state = SCE_MSSQL_DEFAULT;
229 } else if (ch == '@') {
230 styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT);
231 prevState = state;
232 if (chNext == '@') {
233 state = SCE_MSSQL_GLOBAL_VARIABLE;
234 // i += 2;
235 } else
236 state = SCE_MSSQL_VARIABLE;
240 // When the last char is part of the state...
241 } else if (state == SCE_MSSQL_COMMENT) {
242 if (ch == '/' && chPrev == '*') {
243 if (((i > (styler.GetStartSegment() + 2)) || ((initStyle == SCE_MSSQL_COMMENT) &&
244 (styler.GetStartSegment() == startPos)))) {
245 styler.ColourTo(i, state);
246 //~ state = SCE_MSSQL_COMMENT;
247 prevState = state;
248 state = SCE_MSSQL_DEFAULT;
251 } else if (state == SCE_MSSQL_STRING) {
252 if (ch == '\'') {
253 if ( chNext == '\'' ) {
254 i++;
255 ch = chNext;
256 chNext = styler.SafeGetCharAt(i + 1);
257 } else {
258 styler.ColourTo(i, state);
259 prevState = state;
260 state = SCE_MSSQL_DEFAULT;
261 //i++;
263 //ch = chNext;
264 //chNext = styler.SafeGetCharAt(i + 1);
266 } else if (state == SCE_MSSQL_COLUMN_NAME) {
267 if (ch == '"') {
268 if (chNext == '"') {
269 i++;
270 ch = chNext;
271 chNext = styler.SafeGetCharAt(i + 1);
272 } else {
273 styler.ColourTo(i, state);
274 prevState = state;
275 state = SCE_MSSQL_DEFAULT_PREF_DATATYPE;
276 //i++;
279 } else if (state == SCE_MSSQL_COLUMN_NAME_2) {
280 if (ch == ']') {
281 styler.ColourTo(i, state);
282 prevState = state;
283 state = SCE_MSSQL_DEFAULT_PREF_DATATYPE;
284 //i++;
288 chPrev = ch;
290 styler.ColourTo(lengthDoc - 1, state);
293 static void FoldMSSQLDoc(unsigned int startPos, int length, int, WordList *[], Accessor &styler) {
294 bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
295 bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
296 unsigned int endPos = startPos + length;
297 int visibleChars = 0;
298 int lineCurrent = styler.GetLine(startPos);
299 int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
300 int levelCurrent = levelPrev;
301 char chNext = styler[startPos];
302 bool inComment = (styler.StyleAt(startPos-1) == SCE_MSSQL_COMMENT);
303 char s[10];
304 for (unsigned int i = startPos; i < endPos; i++) {
305 char ch = chNext;
306 chNext = styler.SafeGetCharAt(i + 1);
307 int style = styler.StyleAt(i);
308 bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
309 // Comment folding
310 if (foldComment) {
311 if (!inComment && (style == SCE_MSSQL_COMMENT))
312 levelCurrent++;
313 else if (inComment && (style != SCE_MSSQL_COMMENT))
314 levelCurrent--;
315 inComment = (style == SCE_MSSQL_COMMENT);
317 if (style == SCE_MSSQL_STATEMENT) {
318 // Folding between begin or case and end
319 if (ch == 'b' || ch == 'B' || ch == 'c' || ch == 'C' || ch == 'e' || ch == 'E') {
320 for (unsigned int j = 0; j < 5; j++) {
321 if (!iswordchar(styler[i + j])) {
322 break;
324 s[j] = static_cast<char>(tolower(styler[i + j]));
325 s[j + 1] = '\0';
327 if ((strcmp(s, "begin") == 0) || (strcmp(s, "case") == 0)) {
328 levelCurrent++;
330 if (strcmp(s, "end") == 0) {
331 levelCurrent--;
335 if (atEOL) {
336 int lev = levelPrev;
337 if (visibleChars == 0 && foldCompact)
338 lev |= SC_FOLDLEVELWHITEFLAG;
339 if ((levelCurrent > levelPrev) && (visibleChars > 0))
340 lev |= SC_FOLDLEVELHEADERFLAG;
341 if (lev != styler.LevelAt(lineCurrent)) {
342 styler.SetLevel(lineCurrent, lev);
344 lineCurrent++;
345 levelPrev = levelCurrent;
346 visibleChars = 0;
348 if (!isspacechar(ch))
349 visibleChars++;
351 // Fill in the real level of the next line, keeping the current flags as they will be filled in later
352 int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK;
353 styler.SetLevel(lineCurrent, levelPrev | flagsNext);
356 static const char * const sqlWordListDesc[] = {
357 "Statements",
358 "Data Types",
359 "System tables",
360 "Global variables",
361 "Functions",
362 "System Stored Procedures",
363 "Operators",
367 LexerModule lmMSSQL(SCLEX_MSSQL, ColouriseMSSQLDoc, "mssql", FoldMSSQLDoc, sqlWordListDesc);