1// Scintilla source code edit control
2// Copyright 1998-2002 by Neil Hodgson <neilh@scintilla.org>
3// @file LexGui4Cli.cxx
4/*
5This is the Lexer for Gui4Cli, included in SciLexer.dll
6- by d. Keletsekis, 2/10/2003
7
8To add to SciLexer.dll:
91. Add the values below to INCLUDE\Scintilla.iface
102. Run the scripts/HFacer.py script
113. Run the scripts/LexGen.py script
12
13val SCE_GC_DEFAULT=0
14val SCE_GC_COMMENTLINE=1
15val SCE_GC_COMMENTBLOCK=2
16val SCE_GC_GLOBAL=3
17val SCE_GC_EVENT=4
18val SCE_GC_ATTRIBUTE=5
19val SCE_GC_CONTROL=6
20val SCE_GC_COMMAND=7
21val SCE_GC_STRING=8
22val 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
46using namespace Lexilla;
47
48#define debug Platform::DebugPrintf
49
50static inline bool IsAWordChar(const int ch) {
51 return (ch < 0x80) && (isalnum(ch) || ch == '.' || ch == '_' || ch =='\\');
52}
53
54inline 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
72static 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
123static void
124ColouriseGui4CliDoc(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)
249static 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 headerPoint = 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.
306static const char * const gui4cliWordListDesc[] = {
307 "Globals", "Events", "Attributes", "Control", "Commands",
308 0
309};
310
311// Declare language & pass our function pointers to Scintilla
312LexerModule lmGui4Cli(SCLEX_GUI4CLI, ColouriseGui4CliDoc, "gui4cli", FoldGui4Cli, gui4cliWordListDesc);
313
314#undef debug
315
316