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