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