1 // Scintilla source code edit control
\r
2 /** @file LexMSSQL.cxx
\r
5 // By Filip Yaghob <fyaghob@gmail.com>
\r
6 // The License.txt file describes the conditions under which this software may be distributed.
\r
14 #include "Platform.h"
\r
16 #include "PropSet.h"
\r
17 #include "Accessor.h"
\r
18 #include "KeyWords.h"
\r
19 #include "Scintilla.h"
\r
20 #include "SciLexer.h"
\r
22 #ifdef SCI_NAMESPACE
\r
23 using namespace Scintilla;
\r
26 #define KW_MSSQL_STATEMENTS 0
\r
27 #define KW_MSSQL_DATA_TYPES 1
\r
28 #define KW_MSSQL_SYSTEM_TABLES 2
\r
29 #define KW_MSSQL_GLOBAL_VARIABLES 3
\r
30 #define KW_MSSQL_FUNCTIONS 4
\r
31 #define KW_MSSQL_STORED_PROCEDURES 5
\r
32 #define KW_MSSQL_OPERATORS 6
\r
34 static bool isMSSQLOperator(char ch) {
\r
35 if (isascii(ch) && isalnum(ch))
\r
37 // '.' left out as it is used to make up numbers
\r
38 if (ch == '%' || ch == '^' || ch == '&' || ch == '*' ||
\r
39 ch == '-' || ch == '+' || ch == '=' || ch == '|' ||
\r
40 ch == '<' || ch == '>' || ch == '/' ||
\r
41 ch == '!' || ch == '~' || ch == '(' || ch == ')' ||
\r
47 static char classifyWordSQL(unsigned int start,
\r
49 WordList *keywordlists[],
\r
51 unsigned int actualState,
\r
52 unsigned int prevState) {
\r
54 bool wordIsNumber = isdigit(styler[start]) || (styler[start] == '.');
\r
56 WordList &kwStatements = *keywordlists[KW_MSSQL_STATEMENTS];
\r
57 WordList &kwDataTypes = *keywordlists[KW_MSSQL_DATA_TYPES];
\r
58 WordList &kwSystemTables = *keywordlists[KW_MSSQL_SYSTEM_TABLES];
\r
59 WordList &kwGlobalVariables = *keywordlists[KW_MSSQL_GLOBAL_VARIABLES];
\r
60 WordList &kwFunctions = *keywordlists[KW_MSSQL_FUNCTIONS];
\r
61 WordList &kwStoredProcedures = *keywordlists[KW_MSSQL_STORED_PROCEDURES];
\r
62 WordList &kwOperators = *keywordlists[KW_MSSQL_OPERATORS];
\r
64 for (unsigned int i = 0; i < end - start + 1 && i < 128; i++) {
\r
65 s[i] = static_cast<char>(tolower(styler[start + i]));
\r
68 char chAttr = SCE_MSSQL_IDENTIFIER;
\r
70 if (actualState == SCE_MSSQL_GLOBAL_VARIABLE) {
\r
72 if (kwGlobalVariables.InList(&s[2]))
\r
73 chAttr = SCE_MSSQL_GLOBAL_VARIABLE;
\r
75 } else if (wordIsNumber) {
\r
76 chAttr = SCE_MSSQL_NUMBER;
\r
78 } else if (prevState == SCE_MSSQL_DEFAULT_PREF_DATATYPE) {
\r
79 // Look first in datatypes
\r
80 if (kwDataTypes.InList(s))
\r
81 chAttr = SCE_MSSQL_DATATYPE;
\r
82 else if (kwOperators.InList(s))
\r
83 chAttr = SCE_MSSQL_OPERATOR;
\r
84 else if (kwStatements.InList(s))
\r
85 chAttr = SCE_MSSQL_STATEMENT;
\r
86 else if (kwSystemTables.InList(s))
\r
87 chAttr = SCE_MSSQL_SYSTABLE;
\r
88 else if (kwFunctions.InList(s))
\r
89 chAttr = SCE_MSSQL_FUNCTION;
\r
90 else if (kwStoredProcedures.InList(s))
\r
91 chAttr = SCE_MSSQL_STORED_PROCEDURE;
\r
94 if (kwOperators.InList(s))
\r
95 chAttr = SCE_MSSQL_OPERATOR;
\r
96 else if (kwStatements.InList(s))
\r
97 chAttr = SCE_MSSQL_STATEMENT;
\r
98 else if (kwSystemTables.InList(s))
\r
99 chAttr = SCE_MSSQL_SYSTABLE;
\r
100 else if (kwFunctions.InList(s))
\r
101 chAttr = SCE_MSSQL_FUNCTION;
\r
102 else if (kwStoredProcedures.InList(s))
\r
103 chAttr = SCE_MSSQL_STORED_PROCEDURE;
\r
104 else if (kwDataTypes.InList(s))
\r
105 chAttr = SCE_MSSQL_DATATYPE;
\r
108 styler.ColourTo(end, chAttr);
\r
113 static void ColouriseMSSQLDoc(unsigned int startPos, int length,
\r
114 int initStyle, WordList *keywordlists[], Accessor &styler) {
\r
117 styler.StartAt(startPos);
\r
119 bool fold = styler.GetPropertyInt("fold") != 0;
\r
120 int lineCurrent = styler.GetLine(startPos);
\r
121 int spaceFlags = 0;
\r
123 int state = initStyle;
\r
124 int prevState = initStyle;
\r
126 char chNext = styler[startPos];
\r
127 styler.StartSegment(startPos);
\r
128 unsigned int lengthDoc = startPos + length;
\r
129 for (unsigned int i = startPos; i < lengthDoc; i++) {
\r
131 chNext = styler.SafeGetCharAt(i + 1);
\r
133 if ((ch == '\r' && chNext != '\n') || (ch == '\n')) {
\r
134 int indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags);
\r
135 int lev = indentCurrent;
\r
136 if (!(indentCurrent & SC_FOLDLEVELWHITEFLAG)) {
\r
137 // Only non whitespace lines can be headers
\r
138 int indentNext = styler.IndentAmount(lineCurrent + 1, &spaceFlags);
\r
139 if (indentCurrent < (indentNext & ~SC_FOLDLEVELWHITEFLAG)) {
\r
140 lev |= SC_FOLDLEVELHEADERFLAG;
\r
144 styler.SetLevel(lineCurrent, lev);
\r
148 if (styler.IsLeadByte(ch)) {
\r
149 chNext = styler.SafeGetCharAt(i + 2);
\r
155 // When the last char isn't part of the state (have to deal with it too)...
\r
156 if ( (state == SCE_MSSQL_IDENTIFIER) ||
\r
157 (state == SCE_MSSQL_STORED_PROCEDURE) ||
\r
158 (state == SCE_MSSQL_DATATYPE) ||
\r
159 //~ (state == SCE_MSSQL_COLUMN_NAME) ||
\r
160 (state == SCE_MSSQL_FUNCTION) ||
\r
161 //~ (state == SCE_MSSQL_GLOBAL_VARIABLE) ||
\r
162 (state == SCE_MSSQL_VARIABLE)) {
\r
163 if (!iswordchar(ch)) {
\r
166 if ((state == SCE_MSSQL_VARIABLE) || (state == SCE_MSSQL_COLUMN_NAME)) {
\r
167 styler.ColourTo(i - 1, state);
\r
170 stateTmp = classifyWordSQL(styler.GetStartSegment(), i - 1, keywordlists, styler, state, prevState);
\r
174 if (stateTmp == SCE_MSSQL_IDENTIFIER || stateTmp == SCE_MSSQL_VARIABLE)
\r
175 state = SCE_MSSQL_DEFAULT_PREF_DATATYPE;
\r
177 state = SCE_MSSQL_DEFAULT;
\r
179 } else if (state == SCE_MSSQL_LINE_COMMENT) {
\r
180 if (ch == '\r' || ch == '\n') {
\r
181 styler.ColourTo(i - 1, state);
\r
183 state = SCE_MSSQL_DEFAULT;
\r
185 } else if (state == SCE_MSSQL_GLOBAL_VARIABLE) {
\r
186 if ((ch != '@') && !iswordchar(ch)) {
\r
187 classifyWordSQL(styler.GetStartSegment(), i - 1, keywordlists, styler, state, prevState);
\r
189 state = SCE_MSSQL_DEFAULT;
\r
193 // If is the default or one of the above succeeded
\r
194 if (state == SCE_MSSQL_DEFAULT || state == SCE_MSSQL_DEFAULT_PREF_DATATYPE) {
\r
195 if (iswordstart(ch)) {
\r
196 styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT);
\r
198 state = SCE_MSSQL_IDENTIFIER;
\r
199 } else if (ch == '/' && chNext == '*') {
\r
200 styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT);
\r
202 state = SCE_MSSQL_COMMENT;
\r
203 } else if (ch == '-' && chNext == '-') {
\r
204 styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT);
\r
206 state = SCE_MSSQL_LINE_COMMENT;
\r
207 } else if (ch == '\'') {
\r
208 styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT);
\r
210 state = SCE_MSSQL_STRING;
\r
211 } else if (ch == '"') {
\r
212 styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT);
\r
214 state = SCE_MSSQL_COLUMN_NAME;
\r
215 } else if (ch == '[') {
\r
216 styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT);
\r
218 state = SCE_MSSQL_COLUMN_NAME_2;
\r
219 } else if (isMSSQLOperator(ch)) {
\r
220 styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT);
\r
221 styler.ColourTo(i, SCE_MSSQL_OPERATOR);
\r
222 //~ style = SCE_MSSQL_DEFAULT;
\r
224 state = SCE_MSSQL_DEFAULT;
\r
225 } else if (ch == '@') {
\r
226 styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT);
\r
228 if (chNext == '@') {
\r
229 state = SCE_MSSQL_GLOBAL_VARIABLE;
\r
232 state = SCE_MSSQL_VARIABLE;
\r
236 // When the last char is part of the state...
\r
237 } else if (state == SCE_MSSQL_COMMENT) {
\r
238 if (ch == '/' && chPrev == '*') {
\r
239 if (((i > (styler.GetStartSegment() + 2)) || ((initStyle == SCE_MSSQL_COMMENT) &&
\r
240 (styler.GetStartSegment() == startPos)))) {
\r
241 styler.ColourTo(i, state);
\r
242 //~ state = SCE_MSSQL_COMMENT;
\r
244 state = SCE_MSSQL_DEFAULT;
\r
247 } else if (state == SCE_MSSQL_STRING) {
\r
249 if ( chNext == '\'' ) {
\r
252 chNext = styler.SafeGetCharAt(i + 1);
\r
254 styler.ColourTo(i, state);
\r
256 state = SCE_MSSQL_DEFAULT;
\r
260 //chNext = styler.SafeGetCharAt(i + 1);
\r
262 } else if (state == SCE_MSSQL_COLUMN_NAME) {
\r
264 if (chNext == '"') {
\r
267 chNext = styler.SafeGetCharAt(i + 1);
\r
269 styler.ColourTo(i, state);
\r
271 state = SCE_MSSQL_DEFAULT_PREF_DATATYPE;
\r
275 } else if (state == SCE_MSSQL_COLUMN_NAME_2) {
\r
277 styler.ColourTo(i, state);
\r
279 state = SCE_MSSQL_DEFAULT_PREF_DATATYPE;
\r
286 styler.ColourTo(lengthDoc - 1, state);
\r
289 static void FoldMSSQLDoc(unsigned int startPos, int length, int, WordList *[], Accessor &styler) {
\r
290 bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
\r
291 bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
\r
292 unsigned int endPos = startPos + length;
\r
293 int visibleChars = 0;
\r
294 int lineCurrent = styler.GetLine(startPos);
\r
295 int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
\r
296 int levelCurrent = levelPrev;
\r
297 char chNext = styler[startPos];
\r
298 bool inComment = (styler.StyleAt(startPos-1) == SCE_MSSQL_COMMENT);
\r
300 for (unsigned int i = startPos; i < endPos; i++) {
\r
302 chNext = styler.SafeGetCharAt(i + 1);
\r
303 int style = styler.StyleAt(i);
\r
304 bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
\r
307 if (!inComment && (style == SCE_MSSQL_COMMENT))
\r
309 else if (inComment && (style != SCE_MSSQL_COMMENT))
\r
311 inComment = (style == SCE_MSSQL_COMMENT);
\r
313 if (style == SCE_MSSQL_STATEMENT) {
\r
314 // Folding between begin or case and end
\r
315 if (ch == 'b' || ch == 'B' || ch == 'c' || ch == 'C' || ch == 'e' || ch == 'E') {
\r
316 for (unsigned int j = 0; j < 5; j++) {
\r
317 if (!iswordchar(styler[i + j])) {
\r
320 s[j] = static_cast<char>(tolower(styler[i + j]));
\r
323 if ((strcmp(s, "begin") == 0) || (strcmp(s, "case") == 0)) {
\r
326 if (strcmp(s, "end") == 0) {
\r
332 int lev = levelPrev;
\r
333 if (visibleChars == 0 && foldCompact)
\r
334 lev |= SC_FOLDLEVELWHITEFLAG;
\r
335 if ((levelCurrent > levelPrev) && (visibleChars > 0))
\r
336 lev |= SC_FOLDLEVELHEADERFLAG;
\r
337 if (lev != styler.LevelAt(lineCurrent)) {
\r
338 styler.SetLevel(lineCurrent, lev);
\r
341 levelPrev = levelCurrent;
\r
344 if (!isspacechar(ch))
\r
347 // Fill in the real level of the next line, keeping the current flags as they will be filled in later
\r
348 int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK;
\r
349 styler.SetLevel(lineCurrent, levelPrev | flagsNext);
\r
352 static const char * const sqlWordListDesc[] = {
\r
356 "Global variables",
\r
358 "System Stored Procedures",
\r
363 LexerModule lmMSSQL(SCLEX_MSSQL, ColouriseMSSQLDoc, "mssql", FoldMSSQLDoc, sqlWordListDesc);
\r