]> git.saurik.com Git - wxWidgets.git/blob - src/stc/scintilla/lexers/LexVisualProlog.cxx
wxRTC text box layout fixes
[wxWidgets.git] / src / stc / scintilla / lexers / LexVisualProlog.cxx
1 // Scintilla source code edit control
2 /** @file LexVisualProlog.cxx
3 ** Lexer for Visual Prolog.
4 **/
5 // Author Thomas Linder Puls, Prolog Development Denter A/S, http://www.visual-prolog.com
6 // Based on Lexer for C++, C, Java, and JavaScript.
7 // Copyright 1998-2005 by Neil Hodgson <neilh@scintilla.org>
8 // The License.txt file describes the conditions under which this software may be distributed.
9
10 #include <stdlib.h>
11 #include <string.h>
12 #include <ctype.h>
13 #include <stdio.h>
14 #include <stdarg.h>
15 #include <assert.h>
16
17 #ifdef _MSC_VER
18 #pragma warning(disable: 4786)
19 #endif
20
21 #include <string>
22 #include <vector>
23 #include <map>
24 #include <algorithm>
25
26 #include "ILexer.h"
27 #include "Scintilla.h"
28 #include "SciLexer.h"
29
30 #include "WordList.h"
31 #include "LexAccessor.h"
32 #include "Accessor.h"
33 #include "StyleContext.h"
34 #include "CharacterSet.h"
35 #include "LexerModule.h"
36 #include "OptionSet.h"
37
38 #ifdef SCI_NAMESPACE
39 using namespace Scintilla;
40 #endif
41
42 // Options used for LexerVisualProlog
43 struct OptionsVisualProlog {
44 OptionsVisualProlog() {
45 }
46 };
47
48 static const char *const visualPrologWordLists[] = {
49 "Major keywords (class, predicates, ...)",
50 "Minor keywords (if, then, try, ...)",
51 "Directive keywords without the '#' (include, requires, ...)",
52 "Documentation keywords without the '@' (short, detail, ...)",
53 0,
54 };
55
56 struct OptionSetVisualProlog : public OptionSet<OptionsVisualProlog> {
57 OptionSetVisualProlog() {
58 DefineWordListSets(visualPrologWordLists);
59 }
60 };
61
62 class LexerVisualProlog : public ILexer {
63 WordList majorKeywords;
64 WordList minorKeywords;
65 WordList directiveKeywords;
66 WordList docKeywords;
67 OptionsVisualProlog options;
68 OptionSetVisualProlog osVisualProlog;
69 public:
70 LexerVisualProlog() {
71 }
72 virtual ~LexerVisualProlog() {
73 }
74 void SCI_METHOD Release() {
75 delete this;
76 }
77 int SCI_METHOD Version() const {
78 return lvOriginal;
79 }
80 const char * SCI_METHOD PropertyNames() {
81 return osVisualProlog.PropertyNames();
82 }
83 int SCI_METHOD PropertyType(const char *name) {
84 return osVisualProlog.PropertyType(name);
85 }
86 const char * SCI_METHOD DescribeProperty(const char *name) {
87 return osVisualProlog.DescribeProperty(name);
88 }
89 int SCI_METHOD PropertySet(const char *key, const char *val);
90 const char * SCI_METHOD DescribeWordListSets() {
91 return osVisualProlog.DescribeWordListSets();
92 }
93 int SCI_METHOD WordListSet(int n, const char *wl);
94 void SCI_METHOD Lex(unsigned int startPos, int length, int initStyle, IDocument *pAccess);
95 void SCI_METHOD Fold(unsigned int startPos, int length, int initStyle, IDocument *pAccess);
96
97 void * SCI_METHOD PrivateCall(int, void *) {
98 return 0;
99 }
100
101 static ILexer *LexerFactoryVisualProlog() {
102 return new LexerVisualProlog();
103 }
104 };
105
106 int SCI_METHOD LexerVisualProlog::PropertySet(const char *key, const char *val) {
107 if (osVisualProlog.PropertySet(&options, key, val)) {
108 return 0;
109 }
110 return -1;
111 }
112
113 int SCI_METHOD LexerVisualProlog::WordListSet(int n, const char *wl) {
114 WordList *wordListN = 0;
115 switch (n) {
116 case 0:
117 wordListN = &majorKeywords;
118 break;
119 case 1:
120 wordListN = &minorKeywords;
121 break;
122 case 2:
123 wordListN = &directiveKeywords;
124 break;
125 case 3:
126 wordListN = &docKeywords;
127 break;
128 }
129 int firstModification = -1;
130 if (wordListN) {
131 WordList wlNew;
132 wlNew.Set(wl);
133 if (*wordListN != wlNew) {
134 wordListN->Set(wl);
135 firstModification = 0;
136 }
137 }
138 return firstModification;
139 }
140
141 // Functor used to truncate history
142 struct After {
143 int line;
144 After(int line_) : line(line_) {}
145 };
146
147 // Look ahead to see which colour "end" should have (takes colour after the following keyword)
148 static void endLookAhead(char s[], LexAccessor &styler, int start, CharacterSet &setIdentifier) {
149 char ch = styler.SafeGetCharAt(start, '\n');
150 while (' ' == ch) {
151 start++;
152 ch = styler.SafeGetCharAt(start, '\n');
153 }
154 int i = 0;
155 while (i < 100 && setIdentifier.Contains(ch)){
156 s[i] = ch;
157 i++;
158 ch = styler.SafeGetCharAt(start + i, '\n');
159 }
160 s[i] = '\0';
161 }
162
163 static void forwardEscapeLiteral(StyleContext &sc, int OwnChar, int EscapeState) {
164 sc.Forward();
165 if (sc.ch == OwnChar || sc.ch == '\\' || sc.ch == 'n' || sc.ch == 'l' || sc.ch == 'r' || sc.ch == 't') {
166 sc.ChangeState(EscapeState);
167 } else if (sc.ch == 'u') {
168 if (IsADigit(sc.chNext, 16)) {
169 sc.Forward();
170 if (IsADigit(sc.chNext, 16)) {
171 sc.Forward();
172 if (IsADigit(sc.chNext, 16)) {
173 sc.Forward();
174 if (IsADigit(sc.chNext, 16)) {
175 sc.Forward();
176 sc.ChangeState(EscapeState);
177 }
178 }
179 }
180 }
181 }
182 }
183
184 void SCI_METHOD LexerVisualProlog::Lex(unsigned int startPos, int length, int initStyle, IDocument *pAccess) {
185 LexAccessor styler(pAccess);
186
187 CharacterSet setDoxygen(CharacterSet::setAlpha, "$@\\&<>#{}[]");
188
189 CharacterSet setLowerStart(CharacterSet::setLower);
190 CharacterSet setVariableStart(CharacterSet::setUpper);
191 CharacterSet setIdentifier(CharacterSet::setAlphaNum, "_", 0x80, true);
192
193 int styleBeforeDocKeyword = SCE_VISUALPROLOG_DEFAULT;
194
195 int currentLine = styler.GetLine(startPos);
196
197 int nestLevel = 0;
198 if (currentLine >= 1)
199 {
200 nestLevel = styler.GetLineState(currentLine - 1);
201 }
202
203 StyleContext sc(startPos, length, initStyle, styler, 0x7f);
204
205 // Truncate ppDefineHistory before current line
206
207 for (; sc.More(); sc.Forward()) {
208
209 if (sc.atLineEnd) {
210 // Update the line state, so it can be seen by next line
211 styler.SetLineState(currentLine, nestLevel);
212 currentLine++;
213 }
214
215 if (sc.atLineStart) {
216 if ((sc.state == SCE_VISUALPROLOG_STRING) || (sc.state == SCE_VISUALPROLOG_CHARACTER)) {
217 // Prevent SCE_VISUALPROLOG_STRING_EOL from leaking back to previous line which
218 // ends with a line continuation by locking in the state upto this position.
219 sc.SetState(sc.state);
220 }
221 }
222
223 const bool atLineEndBeforeSwitch = sc.atLineEnd;
224
225 // Determine if the current state should terminate.
226 switch (sc.state) {
227 case SCE_VISUALPROLOG_OPERATOR:
228 sc.SetState(SCE_VISUALPROLOG_DEFAULT);
229 break;
230 case SCE_VISUALPROLOG_NUMBER:
231 // We accept almost anything because of hex. and number suffixes
232 if (!(setIdentifier.Contains(sc.ch) || (sc.ch == '.') || ((sc.ch == '+' || sc.ch == '-') && (sc.chPrev == 'e' || sc.chPrev == 'E')))) {
233 sc.SetState(SCE_VISUALPROLOG_DEFAULT);
234 }
235 break;
236 case SCE_VISUALPROLOG_IDENTIFIER:
237 if (!setIdentifier.Contains(sc.ch)) {
238 char s[1000];
239 sc.GetCurrent(s, sizeof(s));
240 if (0 == strcmp(s, "end")) {
241 endLookAhead(s, styler, sc.currentPos, setIdentifier);
242 }
243 if (majorKeywords.InList(s)) {
244 sc.ChangeState(SCE_VISUALPROLOG_KEY_MAJOR);
245 } else if (minorKeywords.InList(s)) {
246 sc.ChangeState(SCE_VISUALPROLOG_KEY_MINOR);
247 }
248 sc.SetState(SCE_VISUALPROLOG_DEFAULT);
249 }
250 break;
251 case SCE_VISUALPROLOG_VARIABLE:
252 case SCE_VISUALPROLOG_ANONYMOUS:
253 if (!setIdentifier.Contains(sc.ch)) {
254 sc.SetState(SCE_VISUALPROLOG_DEFAULT);
255 }
256 break;
257 case SCE_VISUALPROLOG_KEY_DIRECTIVE:
258 if (!setLowerStart.Contains(sc.ch)) {
259 char s[1000];
260 sc.GetCurrent(s, sizeof(s));
261 if (!directiveKeywords.InList(s+1)) {
262 sc.ChangeState(SCE_VISUALPROLOG_IDENTIFIER);
263 }
264 sc.SetState(SCE_VISUALPROLOG_DEFAULT);
265 }
266 break;
267 case SCE_VISUALPROLOG_COMMENT_BLOCK:
268 if (sc.Match('*', '/')) {
269 sc.Forward();
270 nestLevel--;
271 int nextState = (nestLevel == 0) ? SCE_VISUALPROLOG_DEFAULT : SCE_VISUALPROLOG_COMMENT_BLOCK;
272 sc.ForwardSetState(nextState);
273 } else if (sc.Match('/', '*')) {
274 sc.Forward();
275 nestLevel++;
276 } else if (sc.ch == '%') {
277 sc.SetState(SCE_VISUALPROLOG_COMMENT_LINE);
278 } else if (sc.ch == '@') {
279 styleBeforeDocKeyword = sc.state;
280 sc.SetState(SCE_VISUALPROLOG_COMMENT_KEY_ERROR);
281 }
282 break;
283 case SCE_VISUALPROLOG_COMMENT_LINE:
284 if (sc.atLineEnd) {
285 int nextState = (nestLevel == 0) ? SCE_VISUALPROLOG_DEFAULT : SCE_VISUALPROLOG_COMMENT_BLOCK;
286 sc.SetState(nextState);
287 } else if (sc.ch == '@') {
288 styleBeforeDocKeyword = sc.state;
289 sc.SetState(SCE_VISUALPROLOG_COMMENT_KEY_ERROR);
290 }
291 break;
292 case SCE_VISUALPROLOG_COMMENT_KEY_ERROR:
293 if (!setDoxygen.Contains(sc.ch)) {
294 char s[1000];
295 sc.GetCurrent(s, sizeof(s));
296 if (docKeywords.InList(s+1)) {
297 sc.ChangeState(SCE_VISUALPROLOG_COMMENT_KEY);
298 }
299 sc.SetState(styleBeforeDocKeyword);
300 }
301 if (SCE_VISUALPROLOG_COMMENT_LINE == styleBeforeDocKeyword && sc.atLineStart) {
302 sc.SetState(SCE_VISUALPROLOG_DEFAULT);
303 } else if (SCE_VISUALPROLOG_COMMENT_BLOCK == styleBeforeDocKeyword && sc.atLineStart) {
304 sc.SetState(SCE_VISUALPROLOG_COMMENT_BLOCK);
305 }
306 break;
307 case SCE_VISUALPROLOG_STRING_ESCAPE:
308 case SCE_VISUALPROLOG_STRING_ESCAPE_ERROR:
309 // return to SCE_VISUALPROLOG_STRING and treat as such (fall-through)
310 sc.SetState(SCE_VISUALPROLOG_STRING);
311 case SCE_VISUALPROLOG_STRING:
312 if (sc.atLineEnd) {
313 sc.SetState(SCE_VISUALPROLOG_STRING_EOL_OPEN);
314 } else if (sc.ch == '"') {
315 sc.ForwardSetState(SCE_VISUALPROLOG_DEFAULT);
316 } else if (sc.ch == '\\') {
317 sc.SetState(SCE_VISUALPROLOG_STRING_ESCAPE_ERROR);
318 forwardEscapeLiteral(sc, '"', SCE_VISUALPROLOG_STRING_ESCAPE);
319 }
320 break;
321 case SCE_VISUALPROLOG_CHARACTER_TOO_MANY:
322 if (sc.atLineStart) {
323 sc.SetState(SCE_VISUALPROLOG_DEFAULT);
324 } else if (sc.ch == '\'') {
325 sc.SetState(SCE_VISUALPROLOG_CHARACTER);
326 sc.ForwardSetState(SCE_VISUALPROLOG_DEFAULT);
327 }
328 break;
329 case SCE_VISUALPROLOG_CHARACTER:
330 if (sc.atLineEnd) {
331 sc.SetState(SCE_VISUALPROLOG_STRING_EOL_OPEN); // reuse STRING_EOL_OPEN for this
332 } else if (sc.ch == '\'') {
333 sc.SetState(SCE_VISUALPROLOG_CHARACTER_ESCAPE_ERROR);
334 sc.ForwardSetState(SCE_VISUALPROLOG_DEFAULT);
335 } else {
336 if (sc.ch == '\\') {
337 sc.SetState(SCE_VISUALPROLOG_CHARACTER_ESCAPE_ERROR);
338 forwardEscapeLiteral(sc, '\'', SCE_VISUALPROLOG_CHARACTER);
339 }
340 sc.ForwardSetState(SCE_VISUALPROLOG_CHARACTER);
341 if (sc.ch == '\'') {
342 sc.ForwardSetState(SCE_VISUALPROLOG_DEFAULT);
343 } else {
344 sc.SetState(SCE_VISUALPROLOG_CHARACTER_TOO_MANY);
345 }
346 }
347 break;
348 case SCE_VISUALPROLOG_STRING_EOL_OPEN:
349 if (sc.atLineStart) {
350 sc.SetState(SCE_VISUALPROLOG_DEFAULT);
351 }
352 break;
353 case SCE_VISUALPROLOG_STRING_VERBATIM_SPECIAL:
354 case SCE_VISUALPROLOG_STRING_VERBATIM_EOL:
355 // return to SCE_VISUALPROLOG_STRING_VERBATIM and treat as such (fall-through)
356 sc.SetState(SCE_VISUALPROLOG_STRING_VERBATIM);
357 case SCE_VISUALPROLOG_STRING_VERBATIM:
358 if (sc.atLineEnd) {
359 sc.SetState(SCE_VISUALPROLOG_STRING_VERBATIM_EOL);
360 } else if (sc.ch == '\"') {
361 if (sc.chNext == '\"') {
362 sc.SetState(SCE_VISUALPROLOG_STRING_VERBATIM_SPECIAL);
363 sc.Forward();
364 } else {
365 sc.ForwardSetState(SCE_VISUALPROLOG_DEFAULT);
366 }
367 }
368 break;
369 }
370
371 if (sc.atLineEnd && !atLineEndBeforeSwitch) {
372 // State exit processing consumed characters up to end of line.
373 currentLine++;
374 }
375
376 // Determine if a new state should be entered.
377 if (sc.state == SCE_VISUALPROLOG_DEFAULT) {
378 if (sc.Match('@', '\"')) {
379 sc.SetState(SCE_VISUALPROLOG_STRING_VERBATIM);
380 sc.Forward();
381 } else if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
382 sc.SetState(SCE_VISUALPROLOG_NUMBER);
383 } else if (setLowerStart.Contains(sc.ch)) {
384 sc.SetState(SCE_VISUALPROLOG_IDENTIFIER);
385 } else if (setVariableStart.Contains(sc.ch)) {
386 sc.SetState(SCE_VISUALPROLOG_VARIABLE);
387 } else if (sc.ch == '_') {
388 sc.SetState(SCE_VISUALPROLOG_ANONYMOUS);
389 } else if (sc.Match('/', '*')) {
390 sc.SetState(SCE_VISUALPROLOG_COMMENT_BLOCK);
391 nestLevel = 1;
392 sc.Forward(); // Eat the * so it isn't used for the end of the comment
393 } else if (sc.ch == '%') {
394 sc.SetState(SCE_VISUALPROLOG_COMMENT_LINE);
395 } else if (sc.ch == '\"') {
396 sc.SetState(SCE_VISUALPROLOG_STRING);
397 } else if (sc.ch == '\'') {
398 sc.SetState(SCE_VISUALPROLOG_CHARACTER);
399 } else if (sc.ch == '#') {
400 sc.SetState(SCE_VISUALPROLOG_KEY_DIRECTIVE);
401 } else if (isoperator(static_cast<char>(sc.ch)) || sc.ch == '\\') {
402 sc.SetState(SCE_VISUALPROLOG_OPERATOR);
403 }
404 }
405
406 }
407 sc.Complete();
408 styler.Flush();
409 }
410
411 // Store both the current line's fold level and the next lines in the
412 // level store to make it easy to pick up with each increment
413 // and to make it possible to fiddle the current level for "} else {".
414
415 void SCI_METHOD LexerVisualProlog::Fold(unsigned int startPos, int length, int initStyle, IDocument *pAccess) {
416
417 LexAccessor styler(pAccess);
418
419 unsigned int endPos = startPos + length;
420 int visibleChars = 0;
421 int currentLine = styler.GetLine(startPos);
422 int levelCurrent = SC_FOLDLEVELBASE;
423 if (currentLine > 0)
424 levelCurrent = styler.LevelAt(currentLine-1) >> 16;
425 int levelMinCurrent = levelCurrent;
426 int levelNext = levelCurrent;
427 char chNext = styler[startPos];
428 int styleNext = styler.StyleAt(startPos);
429 int style = initStyle;
430 for (unsigned int i = startPos; i < endPos; i++) {
431 char ch = chNext;
432 chNext = styler.SafeGetCharAt(i + 1);
433 style = styleNext;
434 styleNext = styler.StyleAt(i + 1);
435 bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
436 if (style == SCE_VISUALPROLOG_OPERATOR) {
437 if (ch == '{') {
438 // Measure the minimum before a '{' to allow
439 // folding on "} else {"
440 if (levelMinCurrent > levelNext) {
441 levelMinCurrent = levelNext;
442 }
443 levelNext++;
444 } else if (ch == '}') {
445 levelNext--;
446 }
447 }
448 if (!IsASpace(ch))
449 visibleChars++;
450 if (atEOL || (i == endPos-1)) {
451 int levelUse = levelCurrent;
452 int lev = levelUse | levelNext << 16;
453 if (levelUse < levelNext)
454 lev |= SC_FOLDLEVELHEADERFLAG;
455 if (lev != styler.LevelAt(currentLine)) {
456 styler.SetLevel(currentLine, lev);
457 }
458 currentLine++;
459 levelCurrent = levelNext;
460 levelMinCurrent = levelCurrent;
461 if (atEOL && (i == static_cast<unsigned int>(styler.Length()-1))) {
462 // There is an empty line at end of file so give it same level and empty
463 styler.SetLevel(currentLine, (levelCurrent | levelCurrent << 16) | SC_FOLDLEVELWHITEFLAG);
464 }
465 visibleChars = 0;
466 }
467 }
468 }
469
470 LexerModule lmVisualProlog(SCLEX_VISUALPROLOG, LexerVisualProlog::LexerFactoryVisualProlog, "visualprolog", visualPrologWordLists);