Update Scintilla to version 3.4.4
[TortoiseGit.git] / ext / scintilla / lexers / LexVisualProlog.cxx
blob66d997a531c24d360a0a69032fec505df650f231
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 // The line state contains:
11 // In SCE_VISUALPROLOG_STRING_VERBATIM_EOL (i.e. multiline string literal): The closingQuote.
12 // else (for SCE_VISUALPROLOG_COMMENT_BLOCK): The comment nesting level
14 #include <stdlib.h>
15 #include <string.h>
16 #include <stdio.h>
17 #include <stdarg.h>
18 #include <assert.h>
19 #include <ctype.h>
21 #ifdef _MSC_VER
22 #pragma warning(disable: 4786)
23 #endif
25 #include <string>
26 #include <vector>
27 #include <map>
28 #include <algorithm>
30 #include "ILexer.h"
31 #include "Scintilla.h"
32 #include "SciLexer.h"
34 #include "WordList.h"
35 #include "LexAccessor.h"
36 #include "Accessor.h"
37 #include "StyleContext.h"
38 #include "CharacterSet.h"
39 #include "CharacterCategory.h"
40 #include "LexerModule.h"
41 #include "OptionSet.h"
43 #ifdef SCI_NAMESPACE
44 using namespace Scintilla;
45 #endif
47 // Options used for LexerVisualProlog
48 struct OptionsVisualProlog {
49 OptionsVisualProlog() {
53 static const char *const visualPrologWordLists[] = {
54 "Major keywords (class, predicates, ...)",
55 "Minor keywords (if, then, try, ...)",
56 "Directive keywords without the '#' (include, requires, ...)",
57 "Documentation keywords without the '@' (short, detail, ...)",
61 struct OptionSetVisualProlog : public OptionSet<OptionsVisualProlog> {
62 OptionSetVisualProlog() {
63 DefineWordListSets(visualPrologWordLists);
67 class LexerVisualProlog : public ILexer {
68 WordList majorKeywords;
69 WordList minorKeywords;
70 WordList directiveKeywords;
71 WordList docKeywords;
72 OptionsVisualProlog options;
73 OptionSetVisualProlog osVisualProlog;
74 public:
75 LexerVisualProlog() {
77 virtual ~LexerVisualProlog() {
79 void SCI_METHOD Release() {
80 delete this;
82 int SCI_METHOD Version() const {
83 return lvOriginal;
85 const char * SCI_METHOD PropertyNames() {
86 return osVisualProlog.PropertyNames();
88 int SCI_METHOD PropertyType(const char *name) {
89 return osVisualProlog.PropertyType(name);
91 const char * SCI_METHOD DescribeProperty(const char *name) {
92 return osVisualProlog.DescribeProperty(name);
94 int SCI_METHOD PropertySet(const char *key, const char *val);
95 const char * SCI_METHOD DescribeWordListSets() {
96 return osVisualProlog.DescribeWordListSets();
98 int SCI_METHOD WordListSet(int n, const char *wl);
99 void SCI_METHOD Lex(unsigned int startPos, int length, int initStyle, IDocument *pAccess);
100 void SCI_METHOD Fold(unsigned int startPos, int length, int initStyle, IDocument *pAccess);
102 void * SCI_METHOD PrivateCall(int, void *) {
103 return 0;
106 static ILexer *LexerFactoryVisualProlog() {
107 return new LexerVisualProlog();
111 int SCI_METHOD LexerVisualProlog::PropertySet(const char *key, const char *val) {
112 if (osVisualProlog.PropertySet(&options, key, val)) {
113 return 0;
115 return -1;
118 int SCI_METHOD LexerVisualProlog::WordListSet(int n, const char *wl) {
119 WordList *wordListN = 0;
120 switch (n) {
121 case 0:
122 wordListN = &majorKeywords;
123 break;
124 case 1:
125 wordListN = &minorKeywords;
126 break;
127 case 2:
128 wordListN = &directiveKeywords;
129 break;
130 case 3:
131 wordListN = &docKeywords;
132 break;
134 int firstModification = -1;
135 if (wordListN) {
136 WordList wlNew;
137 wlNew.Set(wl);
138 if (*wordListN != wlNew) {
139 wordListN->Set(wl);
140 firstModification = 0;
143 return firstModification;
146 // Functor used to truncate history
147 struct After {
148 int line;
149 After(int line_) : line(line_) {}
152 static bool isLowerLetter(int ch){
153 return ccLl == CategoriseCharacter(ch);
156 static bool isUpperLetter(int ch){
157 return ccLu == CategoriseCharacter(ch);
160 static bool isAlphaNum(int ch){
161 CharacterCategory cc = CategoriseCharacter(ch);
162 return (ccLu == cc || ccLl == cc || ccLt == cc || ccLm == cc || ccLo == cc || ccNd == cc || ccNl == cc || ccNo == cc);
165 static bool isIdChar(int ch){
166 return ('_') == ch || isAlphaNum(ch);
169 static bool isOpenStringVerbatim(int next, int &closingQuote){
170 switch (next) {
171 case L'<':
172 closingQuote = L'>';
173 return true;
174 case L'>':
175 closingQuote = L'<';
176 return true;
177 case L'(':
178 closingQuote = L')';
179 return true;
180 case L')':
181 closingQuote = L'(';
182 return true;
183 case L'[':
184 closingQuote = L']';
185 return true;
186 case L']':
187 closingQuote = L'[';
188 return true;
189 case L'{':
190 closingQuote = L'}';
191 return true;
192 case L'}':
193 closingQuote = L'{';
194 return true;
195 case L'_':
196 case L'.':
197 case L',':
198 case L';':
199 return false;
200 default:
201 if (isAlphaNum(next)) {
202 return false;
203 } else {
204 closingQuote = next;
205 return true;
210 // Look ahead to see which colour "end" should have (takes colour after the following keyword)
211 static void endLookAhead(char s[], LexAccessor &styler, int start) {
212 char ch = styler.SafeGetCharAt(start, '\n');
213 while (' ' == ch) {
214 start++;
215 ch = styler.SafeGetCharAt(start, '\n');
217 int i = 0;
218 while (i < 100 && isLowerLetter(ch)){
219 s[i] = ch;
220 i++;
221 ch = styler.SafeGetCharAt(start + i, '\n');
223 s[i] = '\0';
226 static void forwardEscapeLiteral(StyleContext &sc, int EscapeState) {
227 sc.Forward();
228 if (sc.Match('"') || sc.Match('\'') || sc.Match('\\') || sc.Match('n') || sc.Match('l') || sc.Match('r') || sc.Match('t')) {
229 sc.ChangeState(EscapeState);
230 } else if (sc.Match('u')) {
231 if (IsADigit(sc.chNext, 16)) {
232 sc.Forward();
233 if (IsADigit(sc.chNext, 16)) {
234 sc.Forward();
235 if (IsADigit(sc.chNext, 16)) {
236 sc.Forward();
237 if (IsADigit(sc.chNext, 16)) {
238 sc.Forward();
239 sc.ChangeState(EscapeState);
247 void SCI_METHOD LexerVisualProlog::Lex(unsigned int startPos, int length, int initStyle, IDocument *pAccess) {
248 LexAccessor styler(pAccess);
249 CharacterSet setDoxygen(CharacterSet::setAlpha, "");
250 CharacterSet setNumber(CharacterSet::setNone, "+-.0123456789abcdefABCDEFxoXO");
252 StyleContext sc(startPos, length, initStyle, styler, 0x7f);
254 int styleBeforeDocKeyword = SCE_VISUALPROLOG_DEFAULT;
255 int currentLine = styler.GetLine(startPos);
257 int closingQuote = '"';
258 int nestLevel = 0;
259 if (currentLine >= 1)
261 nestLevel = styler.GetLineState(currentLine - 1);
262 closingQuote = nestLevel;
265 // Truncate ppDefineHistory before current line
267 for (; sc.More(); sc.Forward()) {
269 // Determine if the current state should terminate.
270 switch (sc.state) {
271 case SCE_VISUALPROLOG_OPERATOR:
272 sc.SetState(SCE_VISUALPROLOG_DEFAULT);
273 break;
274 case SCE_VISUALPROLOG_NUMBER:
275 // We accept almost anything because of hex. and number suffixes
276 if (!(setNumber.Contains(sc.ch))) {
277 sc.SetState(SCE_VISUALPROLOG_DEFAULT);
279 break;
280 case SCE_VISUALPROLOG_IDENTIFIER:
281 if (!isIdChar(sc.ch)) {
282 char s[1000];
283 sc.GetCurrent(s, sizeof(s));
284 if (0 == strcmp(s, "end")) {
285 endLookAhead(s, styler, sc.currentPos);
287 if (majorKeywords.InList(s)) {
288 sc.ChangeState(SCE_VISUALPROLOG_KEY_MAJOR);
289 } else if (minorKeywords.InList(s)) {
290 sc.ChangeState(SCE_VISUALPROLOG_KEY_MINOR);
292 sc.SetState(SCE_VISUALPROLOG_DEFAULT);
294 break;
295 case SCE_VISUALPROLOG_VARIABLE:
296 case SCE_VISUALPROLOG_ANONYMOUS:
297 if (!isIdChar(sc.ch)) {
298 sc.SetState(SCE_VISUALPROLOG_DEFAULT);
300 break;
301 case SCE_VISUALPROLOG_KEY_DIRECTIVE:
302 if (!isLowerLetter(sc.ch)) {
303 char s[1000];
304 sc.GetCurrent(s, sizeof(s));
305 if (!directiveKeywords.InList(s+1)) {
306 sc.ChangeState(SCE_VISUALPROLOG_IDENTIFIER);
308 sc.SetState(SCE_VISUALPROLOG_DEFAULT);
310 break;
311 case SCE_VISUALPROLOG_COMMENT_BLOCK:
312 if (sc.Match('*', '/')) {
313 sc.Forward();
314 nestLevel--;
315 int nextState = (nestLevel == 0) ? SCE_VISUALPROLOG_DEFAULT : SCE_VISUALPROLOG_COMMENT_BLOCK;
316 sc.ForwardSetState(nextState);
317 } else if (sc.Match('/', '*')) {
318 sc.Forward();
319 nestLevel++;
320 } else if (sc.Match('@')) {
321 styleBeforeDocKeyword = sc.state;
322 sc.SetState(SCE_VISUALPROLOG_COMMENT_KEY_ERROR);
324 break;
325 case SCE_VISUALPROLOG_COMMENT_LINE:
326 if (sc.atLineEnd) {
327 int nextState = (nestLevel == 0) ? SCE_VISUALPROLOG_DEFAULT : SCE_VISUALPROLOG_COMMENT_BLOCK;
328 sc.SetState(nextState);
329 } else if (sc.Match('@')) {
330 styleBeforeDocKeyword = sc.state;
331 sc.SetState(SCE_VISUALPROLOG_COMMENT_KEY_ERROR);
333 break;
334 case SCE_VISUALPROLOG_COMMENT_KEY_ERROR:
335 if (!setDoxygen.Contains(sc.ch) || sc.atLineEnd) {
336 char s[1000];
337 sc.GetCurrent(s, sizeof(s));
338 if (docKeywords.InList(s+1)) {
339 sc.ChangeState(SCE_VISUALPROLOG_COMMENT_KEY);
341 if (SCE_VISUALPROLOG_COMMENT_LINE == styleBeforeDocKeyword && sc.atLineEnd) {
342 // end line comment
343 int nextState = (nestLevel == 0) ? SCE_VISUALPROLOG_DEFAULT : SCE_VISUALPROLOG_COMMENT_BLOCK;
344 sc.SetState(nextState);
345 } else {
346 sc.SetState(styleBeforeDocKeyword);
347 if (SCE_VISUALPROLOG_COMMENT_BLOCK == styleBeforeDocKeyword && sc.Match('*', '/')) {
348 // we have consumed the '*' if it comes immediately after the docKeyword
349 sc.Forward();
350 sc.Forward();
351 nestLevel--;
352 if (0 == nestLevel) {
353 sc.SetState(SCE_VISUALPROLOG_DEFAULT);
358 break;
359 case SCE_VISUALPROLOG_STRING_ESCAPE:
360 case SCE_VISUALPROLOG_STRING_ESCAPE_ERROR:
361 // return to SCE_VISUALPROLOG_STRING and treat as such (fall-through)
362 sc.SetState(SCE_VISUALPROLOG_STRING);
363 case SCE_VISUALPROLOG_STRING:
364 if (sc.atLineEnd) {
365 sc.SetState(SCE_VISUALPROLOG_STRING_EOL_OPEN);
366 } else if (sc.Match(closingQuote)) {
367 sc.ForwardSetState(SCE_VISUALPROLOG_DEFAULT);
368 } else if (sc.Match('\\')) {
369 sc.SetState(SCE_VISUALPROLOG_STRING_ESCAPE_ERROR);
370 forwardEscapeLiteral(sc, SCE_VISUALPROLOG_STRING_ESCAPE);
372 break;
373 case SCE_VISUALPROLOG_STRING_EOL_OPEN:
374 if (sc.atLineStart) {
375 sc.SetState(SCE_VISUALPROLOG_DEFAULT);
377 break;
378 case SCE_VISUALPROLOG_STRING_VERBATIM_SPECIAL:
379 case SCE_VISUALPROLOG_STRING_VERBATIM_EOL:
380 // return to SCE_VISUALPROLOG_STRING_VERBATIM and treat as such (fall-through)
381 sc.SetState(SCE_VISUALPROLOG_STRING_VERBATIM);
382 case SCE_VISUALPROLOG_STRING_VERBATIM:
383 if (sc.atLineEnd) {
384 sc.SetState(SCE_VISUALPROLOG_STRING_VERBATIM_EOL);
385 } else if (sc.Match(closingQuote)) {
386 if (closingQuote == sc.chNext) {
387 sc.SetState(SCE_VISUALPROLOG_STRING_VERBATIM_SPECIAL);
388 sc.Forward();
389 } else {
390 sc.ForwardSetState(SCE_VISUALPROLOG_DEFAULT);
393 break;
396 if (sc.atLineEnd) {
397 // Update the line state, so it can be seen by next line
398 int lineState = 0;
399 if (SCE_VISUALPROLOG_STRING_VERBATIM_EOL == sc.state) {
400 lineState = closingQuote;
401 } else if (SCE_VISUALPROLOG_COMMENT_BLOCK == sc.state) {
402 lineState = nestLevel;
404 styler.SetLineState(currentLine, lineState);
405 currentLine++;
408 // Determine if a new state should be entered.
409 if (sc.state == SCE_VISUALPROLOG_DEFAULT) {
410 if (sc.Match('@') && isOpenStringVerbatim(sc.chNext, closingQuote)) {
411 sc.SetState(SCE_VISUALPROLOG_STRING_VERBATIM);
412 sc.Forward();
413 } else if (IsADigit(sc.ch) || (sc.Match('.') && IsADigit(sc.chNext))) {
414 sc.SetState(SCE_VISUALPROLOG_NUMBER);
415 } else if (isLowerLetter(sc.ch)) {
416 sc.SetState(SCE_VISUALPROLOG_IDENTIFIER);
417 } else if (isUpperLetter(sc.ch)) {
418 sc.SetState(SCE_VISUALPROLOG_VARIABLE);
419 } else if (sc.Match('_')) {
420 sc.SetState(SCE_VISUALPROLOG_ANONYMOUS);
421 } else if (sc.Match('/', '*')) {
422 sc.SetState(SCE_VISUALPROLOG_COMMENT_BLOCK);
423 nestLevel = 1;
424 sc.Forward(); // Eat the * so it isn't used for the end of the comment
425 } else if (sc.Match('%')) {
426 sc.SetState(SCE_VISUALPROLOG_COMMENT_LINE);
427 } else if (sc.Match('\'')) {
428 closingQuote = '\'';
429 sc.SetState(SCE_VISUALPROLOG_STRING);
430 } else if (sc.Match('"')) {
431 closingQuote = '"';
432 sc.SetState(SCE_VISUALPROLOG_STRING);
433 } else if (sc.Match('#')) {
434 sc.SetState(SCE_VISUALPROLOG_KEY_DIRECTIVE);
435 } else if (isoperator(static_cast<char>(sc.ch)) || sc.Match('\\')) {
436 sc.SetState(SCE_VISUALPROLOG_OPERATOR);
441 sc.Complete();
442 styler.Flush();
445 // Store both the current line's fold level and the next lines in the
446 // level store to make it easy to pick up with each increment
447 // and to make it possible to fiddle the current level for "} else {".
449 void SCI_METHOD LexerVisualProlog::Fold(unsigned int startPos, int length, int initStyle, IDocument *pAccess) {
451 LexAccessor styler(pAccess);
453 unsigned int endPos = startPos + length;
454 int visibleChars = 0;
455 int currentLine = styler.GetLine(startPos);
456 int levelCurrent = SC_FOLDLEVELBASE;
457 if (currentLine > 0)
458 levelCurrent = styler.LevelAt(currentLine-1) >> 16;
459 int levelMinCurrent = levelCurrent;
460 int levelNext = levelCurrent;
461 char chNext = styler[startPos];
462 int styleNext = styler.StyleAt(startPos);
463 int style = initStyle;
464 for (unsigned int i = startPos; i < endPos; i++) {
465 char ch = chNext;
466 chNext = styler.SafeGetCharAt(i + 1);
467 style = styleNext;
468 styleNext = styler.StyleAt(i + 1);
469 bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
470 if (style == SCE_VISUALPROLOG_OPERATOR) {
471 if (ch == '{') {
472 // Measure the minimum before a '{' to allow
473 // folding on "} else {"
474 if (levelMinCurrent > levelNext) {
475 levelMinCurrent = levelNext;
477 levelNext++;
478 } else if (ch == '}') {
479 levelNext--;
482 if (!IsASpace(ch))
483 visibleChars++;
484 if (atEOL || (i == endPos-1)) {
485 int levelUse = levelCurrent;
486 int lev = levelUse | levelNext << 16;
487 if (levelUse < levelNext)
488 lev |= SC_FOLDLEVELHEADERFLAG;
489 if (lev != styler.LevelAt(currentLine)) {
490 styler.SetLevel(currentLine, lev);
492 currentLine++;
493 levelCurrent = levelNext;
494 levelMinCurrent = levelCurrent;
495 if (atEOL && (i == static_cast<unsigned int>(styler.Length()-1))) {
496 // There is an empty line at end of file so give it same level and empty
497 styler.SetLevel(currentLine, (levelCurrent | levelCurrent << 16) | SC_FOLDLEVELWHITEFLAG);
499 visibleChars = 0;
504 LexerModule lmVisualProlog(SCLEX_VISUALPROLOG, LexerVisualProlog::LexerFactoryVisualProlog, "visualprolog", visualPrologWordLists);