1 | // Scintilla source code edit control |
2 | // Copyright 1998-2002 by Neil Hodgson <neilh@scintilla.org> |
3 | // @file LexGui4Cli.cxx |
4 | /* |
5 | This is the Lexer for Gui4Cli, included in SciLexer.dll |
6 | - by d. Keletsekis, 2/10/2003 |
7 | |
8 | To add to SciLexer.dll: |
9 | 1. Add the values below to INCLUDE\Scintilla.iface |
10 | 2. Run the scripts/HFacer.py script |
11 | 3. Run the scripts/LexGen.py script |
12 | |
13 | val SCE_GC_DEFAULT=0 |
14 | val SCE_GC_COMMENTLINE=1 |
15 | val SCE_GC_COMMENTBLOCK=2 |
16 | val SCE_GC_GLOBAL=3 |
17 | val SCE_GC_EVENT=4 |
18 | val SCE_GC_ATTRIBUTE=5 |
19 | val SCE_GC_CONTROL=6 |
20 | val SCE_GC_COMMAND=7 |
21 | val SCE_GC_STRING=8 |
22 | val SCE_GC_OPERATOR=9 |
23 | */ |
24 | |
25 | #include <stdlib.h> |
26 | #include <string.h> |
27 | #include <stdio.h> |
28 | #include <stdarg.h> |
29 | #include <assert.h> |
30 | #include <ctype.h> |
31 | |
32 | #include <string> |
33 | #include <string_view> |
34 | |
35 | #include "ILexer.h" |
36 | #include "Scintilla.h" |
37 | #include "SciLexer.h" |
38 | |
39 | #include "WordList.h" |
40 | #include "LexAccessor.h" |
41 | #include "Accessor.h" |
42 | #include "StyleContext.h" |
43 | #include "CharacterSet.h" |
44 | #include "LexerModule.h" |
45 | |
46 | using namespace Lexilla; |
47 | |
48 | #define debug Platform::DebugPrintf |
49 | |
50 | static inline bool IsAWordChar(const int ch) { |
51 | return (ch < 0x80) && (isalnum(ch) || ch == '.' || ch == '_' || ch =='\\'); |
52 | } |
53 | |
54 | inline bool isGCOperator(int ch) |
55 | { if (isalnum(ch)) |
56 | return false; |
57 | // '.' left out as it is used to make up numbers |
58 | if (ch == '*' || ch == '/' || ch == '-' || ch == '+' || |
59 | ch == '(' || ch == ')' || ch == '=' || ch == '%' || |
60 | ch == '[' || ch == ']' || ch == '<' || ch == '>' || |
61 | ch == ',' || ch == ';' || ch == ':') |
62 | return true; |
63 | return false; |
64 | } |
65 | |
66 | #define isSpace(x) ((x)==' ' || (x)=='\t') |
67 | #define isNL(x) ((x)=='\n' || (x)=='\r') |
68 | #define isSpaceOrNL(x) (isSpace(x) || isNL(x)) |
69 | #define BUFFSIZE 500 |
70 | #define isFoldPoint(x) ((styler.LevelAt(x) & SC_FOLDLEVELNUMBERMASK) == 1024) |
71 | |
72 | static void colorFirstWord(WordList *keywordlists[], Accessor &styler, |
73 | StyleContext *sc, char *buff, Sci_Position length, Sci_Position) |
74 | { |
75 | Sci_Position c = 0; |
76 | while (sc->More() && isSpaceOrNL(sc->ch)) |
77 | { sc->Forward(); |
78 | } |
79 | styler.ColourTo(sc->currentPos - 1, sc->state); |
80 | |
81 | if (!IsAWordChar(sc->ch)) // comment, marker, etc.. |
82 | return; |
83 | |
84 | while (sc->More() && !isSpaceOrNL(sc->ch) && (c < length-1) && !isGCOperator(sc->ch)) |
85 | { buff[c] = static_cast<char>(sc->ch); |
86 | ++c; sc->Forward(); |
87 | } |
88 | buff[c] = '\0'; |
89 | char *p = buff; |
90 | while (*p) // capitalize.. |
91 | { if (islower(*p)) *p = static_cast<char>(toupper(*p)); |
92 | ++p; |
93 | } |
94 | |
95 | WordList &kGlobal = *keywordlists[0]; // keyword lists set by the user |
96 | WordList &kEvent = *keywordlists[1]; |
97 | WordList &kAttribute = *keywordlists[2]; |
98 | WordList &kControl = *keywordlists[3]; |
99 | WordList &kCommand = *keywordlists[4]; |
100 | |
101 | int state = 0; |
102 | // int level = styler.LevelAt(line) & SC_FOLDLEVELNUMBERMASK; |
103 | // debug ("line = %d, level = %d", line, level); |
104 | |
105 | if (kGlobal.InList(buff)) state = SCE_GC_GLOBAL; |
106 | else if (kAttribute.InList(buff)) state = SCE_GC_ATTRIBUTE; |
107 | else if (kControl.InList(buff)) state = SCE_GC_CONTROL; |
108 | else if (kCommand.InList(buff)) state = SCE_GC_COMMAND; |
109 | else if (kEvent.InList(buff)) state = SCE_GC_EVENT; |
110 | |
111 | if (state) |
112 | { sc->ChangeState(state); |
113 | styler.ColourTo(sc->currentPos - 1, sc->state); |
114 | sc->ChangeState(SCE_GC_DEFAULT); |
115 | } |
116 | else |
117 | { sc->ChangeState(SCE_GC_DEFAULT); |
118 | styler.ColourTo(sc->currentPos - 1, sc->state); |
119 | } |
120 | } |
121 | |
122 | // Main colorizing function called by Scintilla |
123 | static void |
124 | ColouriseGui4CliDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, |
125 | WordList *keywordlists[], Accessor &styler) |
126 | { |
127 | styler.StartAt(startPos); |
128 | |
129 | Sci_Position currentline = styler.GetLine(startPos); |
130 | int quotestart = 0, oldstate; |
131 | styler.StartSegment(startPos); |
132 | bool noforward; |
133 | char buff[BUFFSIZE+1]; // buffer for command name |
134 | |
135 | StyleContext sc(startPos, length, initStyle, styler); |
136 | buff[0] = '\0'; // cbuff = 0; |
137 | |
138 | if (sc.state != SCE_GC_COMMENTBLOCK) // colorize 1st word.. |
139 | colorFirstWord(keywordlists, styler, &sc, buff, BUFFSIZE, currentline); |
140 | |
141 | while (sc.More()) |
142 | { noforward = 0; |
143 | |
144 | switch (sc.ch) |
145 | { |
146 | case '/': |
147 | if (sc.state == SCE_GC_COMMENTBLOCK || sc.state == SCE_GC_STRING) |
148 | break; |
149 | if (sc.chNext == '/') // line comment |
150 | { sc.SetState (SCE_GC_COMMENTLINE); |
151 | sc.Forward(); |
152 | styler.ColourTo(sc.currentPos, sc.state); |
153 | } |
154 | else if (sc.chNext == '*') // block comment |
155 | { sc.SetState(SCE_GC_COMMENTBLOCK); |
156 | sc.Forward(); |
157 | styler.ColourTo(sc.currentPos, sc.state); |
158 | } |
159 | else |
160 | styler.ColourTo(sc.currentPos, sc.state); |
161 | break; |
162 | |
163 | case '*': // end of comment block, or operator.. |
164 | if (sc.state == SCE_GC_STRING) |
165 | break; |
166 | if (sc.state == SCE_GC_COMMENTBLOCK && sc.chNext == '/') |
167 | { sc.Forward(); |
168 | styler.ColourTo(sc.currentPos, sc.state); |
169 | sc.ChangeState (SCE_GC_DEFAULT); |
170 | } |
171 | else |
172 | styler.ColourTo(sc.currentPos, sc.state); |
173 | break; |
174 | |
175 | case '\'': case '\"': // strings.. |
176 | if (sc.state == SCE_GC_COMMENTBLOCK || sc.state == SCE_GC_COMMENTLINE) |
177 | break; |
178 | if (sc.state == SCE_GC_STRING) |
179 | { if (sc.ch == quotestart) // match same quote char.. |
180 | { styler.ColourTo(sc.currentPos, sc.state); |
181 | sc.ChangeState(SCE_GC_DEFAULT); |
182 | quotestart = 0; |
183 | } } |
184 | else |
185 | { styler.ColourTo(sc.currentPos - 1, sc.state); |
186 | sc.ChangeState(SCE_GC_STRING); |
187 | quotestart = sc.ch; |
188 | } |
189 | break; |
190 | |
191 | case ';': // end of commandline character |
192 | if (sc.state != SCE_GC_COMMENTBLOCK && sc.state != SCE_GC_COMMENTLINE && |
193 | sc.state != SCE_GC_STRING) |
194 | { |
195 | styler.ColourTo(sc.currentPos - 1, sc.state); |
196 | styler.ColourTo(sc.currentPos, SCE_GC_OPERATOR); |
197 | sc.ChangeState(SCE_GC_DEFAULT); |
198 | sc.Forward(); |
199 | colorFirstWord(keywordlists, styler, &sc, buff, BUFFSIZE, currentline); |
200 | noforward = 1; // don't move forward - already positioned at next char.. |
201 | } |
202 | break; |
203 | |
204 | case '+': case '-': case '=': case '!': // operators.. |
205 | case '<': case '>': case '&': case '|': case '$': |
206 | if (sc.state != SCE_GC_COMMENTBLOCK && sc.state != SCE_GC_COMMENTLINE && |
207 | sc.state != SCE_GC_STRING) |
208 | { |
209 | styler.ColourTo(sc.currentPos - 1, sc.state); |
210 | styler.ColourTo(sc.currentPos, SCE_GC_OPERATOR); |
211 | sc.ChangeState(SCE_GC_DEFAULT); |
212 | } |
213 | break; |
214 | |
215 | case '\\': // escape - same as operator, but also mark in strings.. |
216 | if (sc.state != SCE_GC_COMMENTBLOCK && sc.state != SCE_GC_COMMENTLINE) |
217 | { |
218 | oldstate = sc.state; |
219 | styler.ColourTo(sc.currentPos - 1, sc.state); |
220 | sc.Forward(); // mark also the next char.. |
221 | styler.ColourTo(sc.currentPos, SCE_GC_OPERATOR); |
222 | sc.ChangeState(oldstate); |
223 | } |
224 | break; |
225 | |
226 | case '\n': case '\r': |
227 | ++currentline; |
228 | if (sc.state == SCE_GC_COMMENTLINE) |
229 | { styler.ColourTo(sc.currentPos, sc.state); |
230 | sc.ChangeState (SCE_GC_DEFAULT); |
231 | } |
232 | else if (sc.state != SCE_GC_COMMENTBLOCK) |
233 | { colorFirstWord(keywordlists, styler, &sc, buff, BUFFSIZE, currentline); |
234 | noforward = 1; // don't move forward - already positioned at next char.. |
235 | } |
236 | break; |
237 | |
238 | // case ' ': case '\t': |
239 | // default : |
240 | } |
241 | |
242 | if (!noforward) sc.Forward(); |
243 | |
244 | } |
245 | sc.Complete(); |
246 | } |
247 | |
248 | // Main folding function called by Scintilla - (based on props (.ini) files function) |
249 | static void FoldGui4Cli(Sci_PositionU startPos, Sci_Position length, int, |
250 | WordList *[], Accessor &styler) |
251 | { |
252 | bool foldCompact = styler.GetPropertyInt("fold.compact" , 1) != 0; |
253 | |
254 | Sci_PositionU endPos = startPos + length; |
255 | int visibleChars = 0; |
256 | Sci_Position lineCurrent = styler.GetLine(startPos); |
257 | |
258 | char chNext = styler[startPos]; |
259 | int styleNext = styler.StyleAt(startPos); |
260 | bool = false; |
261 | |
262 | for (Sci_PositionU i = startPos; i < endPos; i++) |
263 | { |
264 | char ch = chNext; |
265 | chNext = styler[i+1]; |
266 | |
267 | int style = styleNext; |
268 | styleNext = styler.StyleAt(i + 1); |
269 | bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n'); |
270 | |
271 | if (style == SCE_GC_EVENT || style == SCE_GC_GLOBAL) |
272 | { headerPoint = true; // fold at events and globals |
273 | } |
274 | |
275 | if (atEOL) |
276 | { int lev = SC_FOLDLEVELBASE+1; |
277 | |
278 | if (headerPoint) |
279 | lev = SC_FOLDLEVELBASE; |
280 | |
281 | if (visibleChars == 0 && foldCompact) |
282 | lev |= SC_FOLDLEVELWHITEFLAG; |
283 | |
284 | if (headerPoint) |
285 | lev |= SC_FOLDLEVELHEADERFLAG; |
286 | |
287 | if (lev != styler.LevelAt(lineCurrent)) // set level, if not already correct |
288 | { styler.SetLevel(lineCurrent, lev); |
289 | } |
290 | |
291 | lineCurrent++; // re-initialize our flags |
292 | visibleChars = 0; |
293 | headerPoint = false; |
294 | } |
295 | |
296 | if (!(isspacechar(ch))) // || (style == SCE_GC_COMMENTLINE) || (style != SCE_GC_COMMENTBLOCK))) |
297 | visibleChars++; |
298 | } |
299 | |
300 | int lev = headerPoint ? SC_FOLDLEVELBASE : SC_FOLDLEVELBASE+1; |
301 | int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK; |
302 | styler.SetLevel(lineCurrent, lev | flagsNext); |
303 | } |
304 | |
305 | // I have no idea what these are for.. probably accessible by some message. |
306 | static const char * const gui4cliWordListDesc[] = { |
307 | "Globals" , "Events" , "Attributes" , "Control" , "Commands" , |
308 | 0 |
309 | }; |
310 | |
311 | // Declare language & pass our function pointers to Scintilla |
312 | LexerModule lmGui4Cli(SCLEX_GUI4CLI, ColouriseGui4CliDoc, "gui4cli" , FoldGui4Cli, gui4cliWordListDesc); |
313 | |
314 | #undef debug |
315 | |
316 | |