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