1 // Scintilla source code edit control
5 // By Filip Yaghob <fyaghob@gmail.com>
6 // The License.txt file describes the conditions under which this software may be distributed.
16 #include "Scintilla.h"
20 #include "LexAccessor.h"
22 #include "StyleContext.h"
23 #include "CharacterSet.h"
24 #include "LexerModule.h"
27 using namespace Scintilla
;
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
38 static bool isMSSQLOperator(char ch
) {
39 if (isascii(ch
) && isalnum(ch
))
41 // '.' left out as it is used to make up numbers
42 if (ch
== '%' || ch
== '^' || ch
== '&' || ch
== '*' ||
43 ch
== '-' || ch
== '+' || ch
== '=' || ch
== '|' ||
44 ch
== '<' || ch
== '>' || ch
== '/' ||
45 ch
== '!' || ch
== '~' || ch
== '(' || ch
== ')' ||
51 static char classifyWordSQL(unsigned int start
,
53 WordList
*keywordlists
[],
55 unsigned int actualState
,
56 unsigned int prevState
) {
58 bool wordIsNumber
= isdigit(styler
[start
]) || (styler
[start
] == '.');
60 WordList
&kwStatements
= *keywordlists
[KW_MSSQL_STATEMENTS
];
61 WordList
&kwDataTypes
= *keywordlists
[KW_MSSQL_DATA_TYPES
];
62 WordList
&kwSystemTables
= *keywordlists
[KW_MSSQL_SYSTEM_TABLES
];
63 WordList
&kwGlobalVariables
= *keywordlists
[KW_MSSQL_GLOBAL_VARIABLES
];
64 WordList
&kwFunctions
= *keywordlists
[KW_MSSQL_FUNCTIONS
];
65 WordList
&kwStoredProcedures
= *keywordlists
[KW_MSSQL_STORED_PROCEDURES
];
66 WordList
&kwOperators
= *keywordlists
[KW_MSSQL_OPERATORS
];
68 for (unsigned int i
= 0; i
< end
- start
+ 1 && i
< 128; i
++) {
69 s
[i
] = static_cast<char>(tolower(styler
[start
+ i
]));
72 char chAttr
= SCE_MSSQL_IDENTIFIER
;
74 if (actualState
== SCE_MSSQL_GLOBAL_VARIABLE
) {
76 if (kwGlobalVariables
.InList(&s
[2]))
77 chAttr
= SCE_MSSQL_GLOBAL_VARIABLE
;
79 } else if (wordIsNumber
) {
80 chAttr
= SCE_MSSQL_NUMBER
;
82 } else if (prevState
== SCE_MSSQL_DEFAULT_PREF_DATATYPE
) {
83 // Look first in datatypes
84 if (kwDataTypes
.InList(s
))
85 chAttr
= SCE_MSSQL_DATATYPE
;
86 else 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
;
98 if (kwOperators
.InList(s
))
99 chAttr
= SCE_MSSQL_OPERATOR
;
100 else if (kwStatements
.InList(s
))
101 chAttr
= SCE_MSSQL_STATEMENT
;
102 else if (kwSystemTables
.InList(s
))
103 chAttr
= SCE_MSSQL_SYSTABLE
;
104 else if (kwFunctions
.InList(s
))
105 chAttr
= SCE_MSSQL_FUNCTION
;
106 else if (kwStoredProcedures
.InList(s
))
107 chAttr
= SCE_MSSQL_STORED_PROCEDURE
;
108 else if (kwDataTypes
.InList(s
))
109 chAttr
= SCE_MSSQL_DATATYPE
;
112 styler
.ColourTo(end
, chAttr
);
117 static void ColouriseMSSQLDoc(unsigned int startPos
, int length
,
118 int initStyle
, WordList
*keywordlists
[], Accessor
&styler
) {
121 styler
.StartAt(startPos
);
123 bool fold
= styler
.GetPropertyInt("fold") != 0;
124 int lineCurrent
= styler
.GetLine(startPos
);
127 int state
= initStyle
;
128 int prevState
= initStyle
;
130 char chNext
= styler
[startPos
];
131 styler
.StartSegment(startPos
);
132 unsigned int lengthDoc
= startPos
+ length
;
133 for (unsigned int i
= startPos
; i
< lengthDoc
; i
++) {
135 chNext
= styler
.SafeGetCharAt(i
+ 1);
137 if ((ch
== '\r' && chNext
!= '\n') || (ch
== '\n')) {
138 int indentCurrent
= styler
.IndentAmount(lineCurrent
, &spaceFlags
);
139 int lev
= indentCurrent
;
140 if (!(indentCurrent
& SC_FOLDLEVELWHITEFLAG
)) {
141 // Only non whitespace lines can be headers
142 int indentNext
= styler
.IndentAmount(lineCurrent
+ 1, &spaceFlags
);
143 if (indentCurrent
< (indentNext
& ~SC_FOLDLEVELWHITEFLAG
)) {
144 lev
|= SC_FOLDLEVELHEADERFLAG
;
148 styler
.SetLevel(lineCurrent
, lev
);
152 if (styler
.IsLeadByte(ch
)) {
153 chNext
= styler
.SafeGetCharAt(i
+ 2);
159 // When the last char isn't part of the state (have to deal with it too)...
160 if ( (state
== SCE_MSSQL_IDENTIFIER
) ||
161 (state
== SCE_MSSQL_STORED_PROCEDURE
) ||
162 (state
== SCE_MSSQL_DATATYPE
) ||
163 //~ (state == SCE_MSSQL_COLUMN_NAME) ||
164 (state
== SCE_MSSQL_FUNCTION
) ||
165 //~ (state == SCE_MSSQL_GLOBAL_VARIABLE) ||
166 (state
== SCE_MSSQL_VARIABLE
)) {
167 if (!iswordchar(ch
)) {
170 if ((state
== SCE_MSSQL_VARIABLE
) || (state
== SCE_MSSQL_COLUMN_NAME
)) {
171 styler
.ColourTo(i
- 1, state
);
174 stateTmp
= classifyWordSQL(styler
.GetStartSegment(), i
- 1, keywordlists
, styler
, state
, prevState
);
178 if (stateTmp
== SCE_MSSQL_IDENTIFIER
|| stateTmp
== SCE_MSSQL_VARIABLE
)
179 state
= SCE_MSSQL_DEFAULT_PREF_DATATYPE
;
181 state
= SCE_MSSQL_DEFAULT
;
183 } else if (state
== SCE_MSSQL_LINE_COMMENT
) {
184 if (ch
== '\r' || ch
== '\n') {
185 styler
.ColourTo(i
- 1, state
);
187 state
= SCE_MSSQL_DEFAULT
;
189 } else if (state
== SCE_MSSQL_GLOBAL_VARIABLE
) {
190 if ((ch
!= '@') && !iswordchar(ch
)) {
191 classifyWordSQL(styler
.GetStartSegment(), i
- 1, keywordlists
, styler
, state
, prevState
);
193 state
= SCE_MSSQL_DEFAULT
;
197 // If is the default or one of the above succeeded
198 if (state
== SCE_MSSQL_DEFAULT
|| state
== SCE_MSSQL_DEFAULT_PREF_DATATYPE
) {
199 if (iswordstart(ch
)) {
200 styler
.ColourTo(i
- 1, SCE_MSSQL_DEFAULT
);
202 state
= SCE_MSSQL_IDENTIFIER
;
203 } else if (ch
== '/' && chNext
== '*') {
204 styler
.ColourTo(i
- 1, SCE_MSSQL_DEFAULT
);
206 state
= SCE_MSSQL_COMMENT
;
207 } else if (ch
== '-' && chNext
== '-') {
208 styler
.ColourTo(i
- 1, SCE_MSSQL_DEFAULT
);
210 state
= SCE_MSSQL_LINE_COMMENT
;
211 } else if (ch
== '\'') {
212 styler
.ColourTo(i
- 1, SCE_MSSQL_DEFAULT
);
214 state
= SCE_MSSQL_STRING
;
215 } else if (ch
== '"') {
216 styler
.ColourTo(i
- 1, SCE_MSSQL_DEFAULT
);
218 state
= SCE_MSSQL_COLUMN_NAME
;
219 } else if (ch
== '[') {
220 styler
.ColourTo(i
- 1, SCE_MSSQL_DEFAULT
);
222 state
= SCE_MSSQL_COLUMN_NAME_2
;
223 } else if (isMSSQLOperator(ch
)) {
224 styler
.ColourTo(i
- 1, SCE_MSSQL_DEFAULT
);
225 styler
.ColourTo(i
, SCE_MSSQL_OPERATOR
);
226 //~ style = SCE_MSSQL_DEFAULT;
228 state
= SCE_MSSQL_DEFAULT
;
229 } else if (ch
== '@') {
230 styler
.ColourTo(i
- 1, SCE_MSSQL_DEFAULT
);
233 state
= SCE_MSSQL_GLOBAL_VARIABLE
;
236 state
= SCE_MSSQL_VARIABLE
;
240 // When the last char is part of the state...
241 } else if (state
== SCE_MSSQL_COMMENT
) {
242 if (ch
== '/' && chPrev
== '*') {
243 if (((i
> (styler
.GetStartSegment() + 2)) || ((initStyle
== SCE_MSSQL_COMMENT
) &&
244 (styler
.GetStartSegment() == startPos
)))) {
245 styler
.ColourTo(i
, state
);
246 //~ state = SCE_MSSQL_COMMENT;
248 state
= SCE_MSSQL_DEFAULT
;
251 } else if (state
== SCE_MSSQL_STRING
) {
253 if ( chNext
== '\'' ) {
256 chNext
= styler
.SafeGetCharAt(i
+ 1);
258 styler
.ColourTo(i
, state
);
260 state
= SCE_MSSQL_DEFAULT
;
264 //chNext = styler.SafeGetCharAt(i + 1);
266 } else if (state
== SCE_MSSQL_COLUMN_NAME
) {
271 chNext
= styler
.SafeGetCharAt(i
+ 1);
273 styler
.ColourTo(i
, state
);
275 state
= SCE_MSSQL_DEFAULT_PREF_DATATYPE
;
279 } else if (state
== SCE_MSSQL_COLUMN_NAME_2
) {
281 styler
.ColourTo(i
, state
);
283 state
= SCE_MSSQL_DEFAULT_PREF_DATATYPE
;
290 styler
.ColourTo(lengthDoc
- 1, state
);
293 static void FoldMSSQLDoc(unsigned int startPos
, int length
, int, WordList
*[], Accessor
&styler
) {
294 bool foldComment
= styler
.GetPropertyInt("fold.comment") != 0;
295 bool foldCompact
= styler
.GetPropertyInt("fold.compact", 1) != 0;
296 unsigned int endPos
= startPos
+ length
;
297 int visibleChars
= 0;
298 int lineCurrent
= styler
.GetLine(startPos
);
299 int levelPrev
= styler
.LevelAt(lineCurrent
) & SC_FOLDLEVELNUMBERMASK
;
300 int levelCurrent
= levelPrev
;
301 char chNext
= styler
[startPos
];
302 bool inComment
= (styler
.StyleAt(startPos
-1) == SCE_MSSQL_COMMENT
);
304 for (unsigned int i
= startPos
; i
< endPos
; i
++) {
306 chNext
= styler
.SafeGetCharAt(i
+ 1);
307 int style
= styler
.StyleAt(i
);
308 bool atEOL
= (ch
== '\r' && chNext
!= '\n') || (ch
== '\n');
311 if (!inComment
&& (style
== SCE_MSSQL_COMMENT
))
313 else if (inComment
&& (style
!= SCE_MSSQL_COMMENT
))
315 inComment
= (style
== SCE_MSSQL_COMMENT
);
317 if (style
== SCE_MSSQL_STATEMENT
) {
318 // Folding between begin or case and end
319 if (ch
== 'b' || ch
== 'B' || ch
== 'c' || ch
== 'C' || ch
== 'e' || ch
== 'E') {
320 for (unsigned int j
= 0; j
< 5; j
++) {
321 if (!iswordchar(styler
[i
+ j
])) {
324 s
[j
] = static_cast<char>(tolower(styler
[i
+ j
]));
327 if ((strcmp(s
, "begin") == 0) || (strcmp(s
, "case") == 0)) {
330 if (strcmp(s
, "end") == 0) {
337 if (visibleChars
== 0 && foldCompact
)
338 lev
|= SC_FOLDLEVELWHITEFLAG
;
339 if ((levelCurrent
> levelPrev
) && (visibleChars
> 0))
340 lev
|= SC_FOLDLEVELHEADERFLAG
;
341 if (lev
!= styler
.LevelAt(lineCurrent
)) {
342 styler
.SetLevel(lineCurrent
, lev
);
345 levelPrev
= levelCurrent
;
348 if (!isspacechar(ch
))
351 // Fill in the real level of the next line, keeping the current flags as they will be filled in later
352 int flagsNext
= styler
.LevelAt(lineCurrent
) & ~SC_FOLDLEVELNUMBERMASK
;
353 styler
.SetLevel(lineCurrent
, levelPrev
| flagsNext
);
356 static const char * const sqlWordListDesc
[] = {
362 "System Stored Procedures",
367 LexerModule
lmMSSQL(SCLEX_MSSQL
, ColouriseMSSQLDoc
, "mssql", FoldMSSQLDoc
, sqlWordListDesc
);