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. |
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 | #include <string> |
16 | #include <string_view> |
17 | |
18 | #include "ILexer.h" |
19 | #include "Scintilla.h" |
20 | #include "SciLexer.h" |
21 | |
22 | #include "WordList.h" |
23 | #include "LexAccessor.h" |
24 | #include "Accessor.h" |
25 | #include "StyleContext.h" |
26 | #include "CharacterSet.h" |
27 | #include "LexerModule.h" |
28 | |
29 | using namespace Lexilla; |
30 | |
31 | #define KW_MSSQL_STATEMENTS 0 |
32 | #define KW_MSSQL_DATA_TYPES 1 |
33 | #define KW_MSSQL_SYSTEM_TABLES 2 |
34 | #define KW_MSSQL_GLOBAL_VARIABLES 3 |
35 | #define KW_MSSQL_FUNCTIONS 4 |
36 | #define KW_MSSQL_STORED_PROCEDURES 5 |
37 | #define KW_MSSQL_OPERATORS 6 |
38 | |
39 | static char classifyWordSQL(Sci_PositionU start, |
40 | Sci_PositionU end, |
41 | WordList *keywordlists[], |
42 | Accessor &styler, |
43 | unsigned int actualState, |
44 | unsigned int prevState) { |
45 | char s[256]; |
46 | bool wordIsNumber = isdigit(styler[start]) || (styler[start] == '.'); |
47 | |
48 | WordList &kwStatements = *keywordlists[KW_MSSQL_STATEMENTS]; |
49 | WordList &kwDataTypes = *keywordlists[KW_MSSQL_DATA_TYPES]; |
50 | WordList &kwSystemTables = *keywordlists[KW_MSSQL_SYSTEM_TABLES]; |
51 | WordList &kwGlobalVariables = *keywordlists[KW_MSSQL_GLOBAL_VARIABLES]; |
52 | WordList &kwFunctions = *keywordlists[KW_MSSQL_FUNCTIONS]; |
53 | WordList &kwStoredProcedures = *keywordlists[KW_MSSQL_STORED_PROCEDURES]; |
54 | WordList &kwOperators = *keywordlists[KW_MSSQL_OPERATORS]; |
55 | |
56 | for (Sci_PositionU i = 0; i < end - start + 1 && i < 128; i++) { |
57 | s[i] = static_cast<char>(tolower(styler[start + i])); |
58 | s[i + 1] = '\0'; |
59 | } |
60 | char chAttr = SCE_MSSQL_IDENTIFIER; |
61 | |
62 | if (actualState == SCE_MSSQL_GLOBAL_VARIABLE) { |
63 | |
64 | if (kwGlobalVariables.InList(&s[2])) |
65 | chAttr = SCE_MSSQL_GLOBAL_VARIABLE; |
66 | |
67 | } else if (wordIsNumber) { |
68 | chAttr = SCE_MSSQL_NUMBER; |
69 | |
70 | } else if (prevState == SCE_MSSQL_DEFAULT_PREF_DATATYPE) { |
71 | // Look first in datatypes |
72 | if (kwDataTypes.InList(s)) |
73 | chAttr = SCE_MSSQL_DATATYPE; |
74 | else if (kwOperators.InList(s)) |
75 | chAttr = SCE_MSSQL_OPERATOR; |
76 | else if (kwStatements.InList(s)) |
77 | chAttr = SCE_MSSQL_STATEMENT; |
78 | else if (kwSystemTables.InList(s)) |
79 | chAttr = SCE_MSSQL_SYSTABLE; |
80 | else if (kwFunctions.InList(s)) |
81 | chAttr = SCE_MSSQL_FUNCTION; |
82 | else if (kwStoredProcedures.InList(s)) |
83 | chAttr = SCE_MSSQL_STORED_PROCEDURE; |
84 | |
85 | } else { |
86 | 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 | else if (kwDataTypes.InList(s)) |
97 | chAttr = SCE_MSSQL_DATATYPE; |
98 | } |
99 | |
100 | styler.ColourTo(end, chAttr); |
101 | |
102 | return chAttr; |
103 | } |
104 | |
105 | static void ColouriseMSSQLDoc(Sci_PositionU startPos, Sci_Position length, |
106 | int initStyle, WordList *keywordlists[], Accessor &styler) { |
107 | |
108 | |
109 | styler.StartAt(startPos); |
110 | |
111 | bool fold = styler.GetPropertyInt("fold" ) != 0; |
112 | Sci_Position lineCurrent = styler.GetLine(startPos); |
113 | int spaceFlags = 0; |
114 | |
115 | int state = initStyle; |
116 | int prevState = initStyle; |
117 | char chPrev = ' '; |
118 | char chNext = styler[startPos]; |
119 | styler.StartSegment(startPos); |
120 | Sci_PositionU lengthDoc = startPos + length; |
121 | for (Sci_PositionU i = startPos; i < lengthDoc; i++) { |
122 | char ch = chNext; |
123 | chNext = styler.SafeGetCharAt(i + 1); |
124 | |
125 | if ((ch == '\r' && chNext != '\n') || (ch == '\n')) { |
126 | int indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags); |
127 | int lev = indentCurrent; |
128 | if (!(indentCurrent & SC_FOLDLEVELWHITEFLAG)) { |
129 | // Only non whitespace lines can be headers |
130 | int indentNext = styler.IndentAmount(lineCurrent + 1, &spaceFlags); |
131 | if (indentCurrent < (indentNext & ~SC_FOLDLEVELWHITEFLAG)) { |
132 | lev |= SC_FOLDLEVELHEADERFLAG; |
133 | } |
134 | } |
135 | if (fold) { |
136 | styler.SetLevel(lineCurrent, lev); |
137 | } |
138 | } |
139 | |
140 | if (styler.IsLeadByte(ch)) { |
141 | chNext = styler.SafeGetCharAt(i + 2); |
142 | chPrev = ' '; |
143 | i += 1; |
144 | continue; |
145 | } |
146 | |
147 | // When the last char isn't part of the state (have to deal with it too)... |
148 | if ( (state == SCE_MSSQL_IDENTIFIER) || |
149 | (state == SCE_MSSQL_STORED_PROCEDURE) || |
150 | (state == SCE_MSSQL_DATATYPE) || |
151 | //~ (state == SCE_MSSQL_COLUMN_NAME) || |
152 | (state == SCE_MSSQL_FUNCTION) || |
153 | //~ (state == SCE_MSSQL_GLOBAL_VARIABLE) || |
154 | (state == SCE_MSSQL_VARIABLE)) { |
155 | if (!iswordchar(ch)) { |
156 | int stateTmp; |
157 | |
158 | if ((state == SCE_MSSQL_VARIABLE) || (state == SCE_MSSQL_COLUMN_NAME)) { |
159 | styler.ColourTo(i - 1, state); |
160 | stateTmp = state; |
161 | } else |
162 | stateTmp = classifyWordSQL(styler.GetStartSegment(), i - 1, keywordlists, styler, state, prevState); |
163 | |
164 | prevState = state; |
165 | |
166 | if (stateTmp == SCE_MSSQL_IDENTIFIER || stateTmp == SCE_MSSQL_VARIABLE) |
167 | state = SCE_MSSQL_DEFAULT_PREF_DATATYPE; |
168 | else |
169 | state = SCE_MSSQL_DEFAULT; |
170 | } |
171 | } else if (state == SCE_MSSQL_LINE_COMMENT) { |
172 | if (ch == '\r' || ch == '\n') { |
173 | styler.ColourTo(i - 1, state); |
174 | prevState = state; |
175 | state = SCE_MSSQL_DEFAULT; |
176 | } |
177 | } else if (state == SCE_MSSQL_GLOBAL_VARIABLE) { |
178 | if ((ch != '@') && !iswordchar(ch)) { |
179 | classifyWordSQL(styler.GetStartSegment(), i - 1, keywordlists, styler, state, prevState); |
180 | prevState = state; |
181 | state = SCE_MSSQL_DEFAULT; |
182 | } |
183 | } |
184 | |
185 | // If is the default or one of the above succeeded |
186 | if (state == SCE_MSSQL_DEFAULT || state == SCE_MSSQL_DEFAULT_PREF_DATATYPE) { |
187 | if (iswordstart(ch)) { |
188 | styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT); |
189 | prevState = state; |
190 | state = SCE_MSSQL_IDENTIFIER; |
191 | } else if (ch == '/' && chNext == '*') { |
192 | styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT); |
193 | prevState = state; |
194 | state = SCE_MSSQL_COMMENT; |
195 | } else if (ch == '-' && chNext == '-') { |
196 | styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT); |
197 | prevState = state; |
198 | state = SCE_MSSQL_LINE_COMMENT; |
199 | } else if (ch == '\'') { |
200 | styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT); |
201 | prevState = state; |
202 | state = SCE_MSSQL_STRING; |
203 | } else if (ch == '"') { |
204 | styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT); |
205 | prevState = state; |
206 | state = SCE_MSSQL_COLUMN_NAME; |
207 | } else if (ch == '[') { |
208 | styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT); |
209 | prevState = state; |
210 | state = SCE_MSSQL_COLUMN_NAME_2; |
211 | } else if (isoperator(ch)) { |
212 | styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT); |
213 | styler.ColourTo(i, SCE_MSSQL_OPERATOR); |
214 | //~ style = SCE_MSSQL_DEFAULT; |
215 | prevState = state; |
216 | state = SCE_MSSQL_DEFAULT; |
217 | } else if (ch == '@') { |
218 | styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT); |
219 | prevState = state; |
220 | if (chNext == '@') { |
221 | state = SCE_MSSQL_GLOBAL_VARIABLE; |
222 | // i += 2; |
223 | } else |
224 | state = SCE_MSSQL_VARIABLE; |
225 | } |
226 | |
227 | |
228 | // When the last char is part of the state... |
229 | } else if (state == SCE_MSSQL_COMMENT) { |
230 | if (ch == '/' && chPrev == '*') { |
231 | if (((i > (styler.GetStartSegment() + 2)) || ((initStyle == SCE_MSSQL_COMMENT) && |
232 | (styler.GetStartSegment() == startPos)))) { |
233 | styler.ColourTo(i, state); |
234 | //~ state = SCE_MSSQL_COMMENT; |
235 | prevState = state; |
236 | state = SCE_MSSQL_DEFAULT; |
237 | } |
238 | } |
239 | } else if (state == SCE_MSSQL_STRING) { |
240 | if (ch == '\'') { |
241 | if ( chNext == '\'' ) { |
242 | i++; |
243 | ch = chNext; |
244 | chNext = styler.SafeGetCharAt(i + 1); |
245 | } else { |
246 | styler.ColourTo(i, state); |
247 | prevState = state; |
248 | state = SCE_MSSQL_DEFAULT; |
249 | //i++; |
250 | } |
251 | //ch = chNext; |
252 | //chNext = styler.SafeGetCharAt(i + 1); |
253 | } |
254 | } else if (state == SCE_MSSQL_COLUMN_NAME) { |
255 | if (ch == '"') { |
256 | if (chNext == '"') { |
257 | i++; |
258 | ch = chNext; |
259 | chNext = styler.SafeGetCharAt(i + 1); |
260 | } else { |
261 | styler.ColourTo(i, state); |
262 | prevState = state; |
263 | state = SCE_MSSQL_DEFAULT_PREF_DATATYPE; |
264 | //i++; |
265 | } |
266 | } |
267 | } else if (state == SCE_MSSQL_COLUMN_NAME_2) { |
268 | if (ch == ']') { |
269 | styler.ColourTo(i, state); |
270 | prevState = state; |
271 | state = SCE_MSSQL_DEFAULT_PREF_DATATYPE; |
272 | //i++; |
273 | } |
274 | } |
275 | |
276 | chPrev = ch; |
277 | } |
278 | styler.ColourTo(lengthDoc - 1, state); |
279 | } |
280 | |
281 | static void FoldMSSQLDoc(Sci_PositionU startPos, Sci_Position length, int, WordList *[], Accessor &styler) { |
282 | bool = styler.GetPropertyInt("fold.comment" ) != 0; |
283 | bool foldCompact = styler.GetPropertyInt("fold.compact" , 1) != 0; |
284 | Sci_PositionU endPos = startPos + length; |
285 | int visibleChars = 0; |
286 | Sci_Position lineCurrent = styler.GetLine(startPos); |
287 | int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK; |
288 | int levelCurrent = levelPrev; |
289 | char chNext = styler[startPos]; |
290 | bool = (styler.StyleAt(startPos-1) == SCE_MSSQL_COMMENT); |
291 | char s[10] = "" ; |
292 | for (Sci_PositionU i = startPos; i < endPos; i++) { |
293 | char ch = chNext; |
294 | chNext = styler.SafeGetCharAt(i + 1); |
295 | int style = styler.StyleAt(i); |
296 | bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n'); |
297 | // Comment folding |
298 | if (foldComment) { |
299 | if (!inComment && (style == SCE_MSSQL_COMMENT)) |
300 | levelCurrent++; |
301 | else if (inComment && (style != SCE_MSSQL_COMMENT)) |
302 | levelCurrent--; |
303 | inComment = (style == SCE_MSSQL_COMMENT); |
304 | } |
305 | if (style == SCE_MSSQL_STATEMENT) { |
306 | // Folding between begin or case and end |
307 | if (ch == 'b' || ch == 'B' || ch == 'c' || ch == 'C' || ch == 'e' || ch == 'E') { |
308 | for (Sci_PositionU j = 0; j < 5; j++) { |
309 | if (!iswordchar(styler[i + j])) { |
310 | break; |
311 | } |
312 | s[j] = static_cast<char>(tolower(styler[i + j])); |
313 | s[j + 1] = '\0'; |
314 | } |
315 | if ((strcmp(s, "begin" ) == 0) || (strcmp(s, "case" ) == 0)) { |
316 | levelCurrent++; |
317 | } |
318 | if (strcmp(s, "end" ) == 0) { |
319 | levelCurrent--; |
320 | } |
321 | } |
322 | } |
323 | if (atEOL) { |
324 | int lev = levelPrev; |
325 | if (visibleChars == 0 && foldCompact) |
326 | lev |= SC_FOLDLEVELWHITEFLAG; |
327 | if ((levelCurrent > levelPrev) && (visibleChars > 0)) |
328 | lev |= SC_FOLDLEVELHEADERFLAG; |
329 | if (lev != styler.LevelAt(lineCurrent)) { |
330 | styler.SetLevel(lineCurrent, lev); |
331 | } |
332 | lineCurrent++; |
333 | levelPrev = levelCurrent; |
334 | visibleChars = 0; |
335 | } |
336 | if (!isspacechar(ch)) |
337 | visibleChars++; |
338 | } |
339 | // Fill in the real level of the next line, keeping the current flags as they will be filled in later |
340 | int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK; |
341 | styler.SetLevel(lineCurrent, levelPrev | flagsNext); |
342 | } |
343 | |
344 | static const char * const sqlWordListDesc[] = { |
345 | "Statements" , |
346 | "Data Types" , |
347 | "System tables" , |
348 | "Global variables" , |
349 | "Functions" , |
350 | "System Stored Procedures" , |
351 | "Operators" , |
352 | 0, |
353 | }; |
354 | |
355 | LexerModule lmMSSQL(SCLEX_MSSQL, ColouriseMSSQLDoc, "mssql" , FoldMSSQLDoc, sqlWordListDesc); |
356 | |