upgraded to scintilla 3.2.0
[TortoiseGit.git] / ext / scintilla / lexers / LexVisualProlog.cxx
blob634d155034e765c3f3215919e485be0362cbf56c
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.
10 #include <stdlib.h>
11 #include <string.h>
12 #include <ctype.h>
13 #include <stdio.h>
14 #include <stdarg.h>
15 #include <assert.h>
17 #ifdef _MSC_VER
18 #pragma warning(disable: 4786)
19 #endif
21 #include <string>
22 #include <vector>
23 #include <map>
24 #include <algorithm>
26 #include "ILexer.h"
27 #include "Scintilla.h"
28 #include "SciLexer.h"
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"
38 #ifdef SCI_NAMESPACE
39 using namespace Scintilla;
40 #endif
42 // Options used for LexerVisualProlog
43 struct OptionsVisualProlog {
44 OptionsVisualProlog() {
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, ...)",
56 struct OptionSetVisualProlog : public OptionSet<OptionsVisualProlog> {
57 OptionSetVisualProlog() {
58 DefineWordListSets(visualPrologWordLists);
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() {
72 virtual ~LexerVisualProlog() {
74 void SCI_METHOD Release() {
75 delete this;
77 int SCI_METHOD Version() const {
78 return lvOriginal;
80 const char * SCI_METHOD PropertyNames() {
81 return osVisualProlog.PropertyNames();
83 int SCI_METHOD PropertyType(const char *name) {
84 return osVisualProlog.PropertyType(name);
86 const char * SCI_METHOD DescribeProperty(const char *name) {
87 return osVisualProlog.DescribeProperty(name);
89 int SCI_METHOD PropertySet(const char *key, const char *val);
90 const char * SCI_METHOD DescribeWordListSets() {
91 return osVisualProlog.DescribeWordListSets();
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);
97 void * SCI_METHOD PrivateCall(int, void *) {
98 return 0;
101 static ILexer *LexerFactoryVisualProlog() {
102 return new LexerVisualProlog();
106 int SCI_METHOD LexerVisualProlog::PropertySet(const char *key, const char *val) {
107 if (osVisualProlog.PropertySet(&options, key, val)) {
108 return 0;
110 return -1;
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;
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;
138 return firstModification;
141 // Functor used to truncate history
142 struct After {
143 int line;
144 After(int line_) : line(line_) {}
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');
154 int i = 0;
155 while (i < 100 && setIdentifier.Contains(ch)){
156 s[i] = ch;
157 i++;
158 ch = styler.SafeGetCharAt(start + i, '\n');
160 s[i] = '\0';
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);
184 void SCI_METHOD LexerVisualProlog::Lex(unsigned int startPos, int length, int initStyle, IDocument *pAccess) {
185 LexAccessor styler(pAccess);
187 CharacterSet setDoxygen(CharacterSet::setAlpha, "$@\\&<>#{}[]");
189 CharacterSet setLowerStart(CharacterSet::setLower);
190 CharacterSet setVariableStart(CharacterSet::setUpper);
191 CharacterSet setIdentifier(CharacterSet::setAlphaNum, "_", 0x80, true);
193 int styleBeforeDocKeyword = SCE_VISUALPROLOG_DEFAULT;
195 int currentLine = styler.GetLine(startPos);
197 int nestLevel = 0;
198 if (currentLine >= 1)
200 nestLevel = styler.GetLineState(currentLine - 1);
203 StyleContext sc(startPos, length, initStyle, styler, 0x7f);
205 // Truncate ppDefineHistory before current line
207 for (; sc.More(); sc.Forward()) {
209 if (sc.atLineEnd) {
210 // Update the line state, so it can be seen by next line
211 styler.SetLineState(currentLine, nestLevel);
212 currentLine++;
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);
223 const bool atLineEndBeforeSwitch = sc.atLineEnd;
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);
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);
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);
248 sc.SetState(SCE_VISUALPROLOG_DEFAULT);
250 break;
251 case SCE_VISUALPROLOG_VARIABLE:
252 case SCE_VISUALPROLOG_ANONYMOUS:
253 if (!setIdentifier.Contains(sc.ch)) {
254 sc.SetState(SCE_VISUALPROLOG_DEFAULT);
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);
264 sc.SetState(SCE_VISUALPROLOG_DEFAULT);
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);
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);
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);
299 sc.SetState(styleBeforeDocKeyword);
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);
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);
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);
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);
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);
347 break;
348 case SCE_VISUALPROLOG_STRING_EOL_OPEN:
349 if (sc.atLineStart) {
350 sc.SetState(SCE_VISUALPROLOG_DEFAULT);
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);
368 break;
371 if (sc.atLineEnd && !atLineEndBeforeSwitch) {
372 // State exit processing consumed characters up to end of line.
373 currentLine++;
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);
407 sc.Complete();
408 styler.Flush();
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 {".
415 void SCI_METHOD LexerVisualProlog::Fold(unsigned int startPos, int length, int initStyle, IDocument *pAccess) {
417 LexAccessor styler(pAccess);
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;
443 levelNext++;
444 } else if (ch == '}') {
445 levelNext--;
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);
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);
465 visibleChars = 0;
470 LexerModule lmVisualProlog(SCLEX_VISUALPROLOG, LexerVisualProlog::LexerFactoryVisualProlog, "visualprolog", visualPrologWordLists);