]>
Commit | Line | Data |
---|---|---|
591d01be RD |
1 | // Scintilla source code edit control |
2 | /** @file LexMSSQL.cxx | |
3 | ** Lexer for MSSQL. | |
4 | **/ | |
1e9bafca | 5 | // By Filip Yaghob <fyaghob@gmail.com> |
b8193d80 | 6 | // The License.txt file describes the conditions under which this software may be distributed. |
591d01be RD |
7 | |
8 | #include <stdlib.h> | |
9 | #include <string.h> | |
591d01be RD |
10 | #include <stdio.h> |
11 | #include <stdarg.h> | |
1dcf666d RD |
12 | #include <assert.h> |
13 | #include <ctype.h> | |
591d01be | 14 | |
1dcf666d | 15 | #include "ILexer.h" |
591d01be RD |
16 | #include "Scintilla.h" |
17 | #include "SciLexer.h" | |
18 | ||
1dcf666d RD |
19 | #include "WordList.h" |
20 | #include "LexAccessor.h" | |
21 | #include "Accessor.h" | |
22 | #include "StyleContext.h" | |
23 | #include "CharacterSet.h" | |
24 | #include "LexerModule.h" | |
25 | ||
7e0c58e9 RD |
26 | #ifdef SCI_NAMESPACE |
27 | using namespace Scintilla; | |
28 | #endif | |
29 | ||
591d01be RD |
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 | |
37 | ||
591d01be RD |
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; | |
49 | } | |
50 | ||
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] == '.'); | |
59 | ||
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]; | |
67 | ||
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'; | |
71 | } | |
72 | char chAttr = SCE_MSSQL_IDENTIFIER; | |
73 | ||
74 | if (actualState == SCE_MSSQL_GLOBAL_VARIABLE) { | |
75 | ||
76 | if (kwGlobalVariables.InList(&s[2])) | |
77 | chAttr = SCE_MSSQL_GLOBAL_VARIABLE; | |
78 | ||
79 | } else if (wordIsNumber) { | |
80 | chAttr = SCE_MSSQL_NUMBER; | |
81 | ||
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; | |
96 | ||
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; | |
110 | } | |
111 | ||
112 | styler.ColourTo(end, chAttr); | |
113 | ||
114 | return chAttr; | |
115 | } | |
116 | ||
117 | static void ColouriseMSSQLDoc(unsigned int startPos, int length, | |
118 | int initStyle, WordList *keywordlists[], Accessor &styler) { | |
119 | ||
120 | ||
121 | styler.StartAt(startPos); | |
122 | ||
123 | bool fold = styler.GetPropertyInt("fold") != 0; | |
124 | int lineCurrent = styler.GetLine(startPos); | |
125 | int spaceFlags = 0; | |
591d01be | 126 | |
591d01be RD |
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); | |
136 | ||
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; | |
145 | } | |
146 | } | |
147 | if (fold) { | |
148 | styler.SetLevel(lineCurrent, lev); | |
149 | } | |
150 | } | |
151 | ||
152 | if (styler.IsLeadByte(ch)) { | |
153 | chNext = styler.SafeGetCharAt(i + 2); | |
154 | chPrev = ' '; | |
155 | i += 1; | |
156 | continue; | |
157 | } | |
158 | ||
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; | |
169 | ||
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); | |
175 | ||
176 | prevState = state; | |
177 | ||
178 | if (stateTmp == SCE_MSSQL_IDENTIFIER || stateTmp == SCE_MSSQL_VARIABLE) | |
179 | state = SCE_MSSQL_DEFAULT_PREF_DATATYPE; | |
180 | else | |
181 | state = SCE_MSSQL_DEFAULT; | |
182 | } | |
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; | |
188 | } | |
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; | |
194 | } | |
195 | } | |
196 | ||
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; | |
237 | } | |
238 | ||
239 | ||
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; | |
249 | } | |
250 | } | |
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++; | |
262 | } | |
263 | //ch = chNext; | |
264 | //chNext = styler.SafeGetCharAt(i + 1); | |
265 | } | |
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++; | |
277 | } | |
278 | } | |
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++; | |
285 | } | |
286 | } | |
287 | ||
288 | chPrev = ch; | |
289 | } | |
290 | styler.ColourTo(lengthDoc - 1, state); | |
291 | } | |
292 | ||
1e9bafca RD |
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); | |
316 | } | |
317 | if (style == SCE_MSSQL_STATEMENT) { | |
7e0c58e9 RD |
318 | // Folding between begin or case and end |
319 | if (ch == 'b' || ch == 'B' || ch == 'c' || ch == 'C' || ch == 'e' || ch == 'E') { | |
1e9bafca RD |
320 | for (unsigned int j = 0; j < 5; j++) { |
321 | if (!iswordchar(styler[i + j])) { | |
322 | break; | |
323 | } | |
7e0c58e9 | 324 | s[j] = static_cast<char>(tolower(styler[i + j])); |
1e9bafca RD |
325 | s[j + 1] = '\0'; |
326 | } | |
7e0c58e9 | 327 | if ((strcmp(s, "begin") == 0) || (strcmp(s, "case") == 0)) { |
1e9bafca RD |
328 | levelCurrent++; |
329 | } | |
330 | if (strcmp(s, "end") == 0) { | |
331 | levelCurrent--; | |
332 | } | |
333 | } | |
334 | } | |
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); | |
343 | } | |
344 | lineCurrent++; | |
345 | levelPrev = levelCurrent; | |
346 | visibleChars = 0; | |
347 | } | |
348 | if (!isspacechar(ch)) | |
349 | visibleChars++; | |
350 | } | |
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); | |
354 | } | |
355 | ||
591d01be RD |
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", | |
364 | 0, | |
365 | }; | |
366 | ||
1e9bafca | 367 | LexerModule lmMSSQL(SCLEX_MSSQL, ColouriseMSSQLDoc, "mssql", FoldMSSQLDoc, sqlWordListDesc); |