Scintilla 4.0.3
[TortoiseGit.git] / ext / scintilla / lexers / LexVisualProlog.cxx
bloba2d50916e88232bdc9d68be71af38057bbcc9b9f
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"
42 #include "DefaultLexer.h"
44 using namespace Scintilla;
46 // Options used for LexerVisualProlog
47 struct OptionsVisualProlog {
48 OptionsVisualProlog() {
52 static const char *const visualPrologWordLists[] = {
53 "Major keywords (class, predicates, ...)",
54 "Minor keywords (if, then, try, ...)",
55 "Directive keywords without the '#' (include, requires, ...)",
56 "Documentation keywords without the '@' (short, detail, ...)",
60 struct OptionSetVisualProlog : public OptionSet<OptionsVisualProlog> {
61 OptionSetVisualProlog() {
62 DefineWordListSets(visualPrologWordLists);
66 class LexerVisualProlog : public DefaultLexer {
67 WordList majorKeywords;
68 WordList minorKeywords;
69 WordList directiveKeywords;
70 WordList docKeywords;
71 OptionsVisualProlog options;
72 OptionSetVisualProlog osVisualProlog;
73 public:
74 LexerVisualProlog() {
76 virtual ~LexerVisualProlog() {
78 void SCI_METHOD Release() override {
79 delete this;
81 int SCI_METHOD Version() const override {
82 return lvRelease4;
84 const char * SCI_METHOD PropertyNames() override {
85 return osVisualProlog.PropertyNames();
87 int SCI_METHOD PropertyType(const char *name) override {
88 return osVisualProlog.PropertyType(name);
90 const char * SCI_METHOD DescribeProperty(const char *name) override {
91 return osVisualProlog.DescribeProperty(name);
93 Sci_Position SCI_METHOD PropertySet(const char *key, const char *val) override;
94 const char * SCI_METHOD DescribeWordListSets() override {
95 return osVisualProlog.DescribeWordListSets();
97 Sci_Position SCI_METHOD WordListSet(int n, const char *wl) override;
98 void SCI_METHOD Lex(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess) override;
99 void SCI_METHOD Fold(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess) override;
101 void * SCI_METHOD PrivateCall(int, void *) override {
102 return 0;
105 static ILexer4 *LexerFactoryVisualProlog() {
106 return new LexerVisualProlog();
110 Sci_Position SCI_METHOD LexerVisualProlog::PropertySet(const char *key, const char *val) {
111 if (osVisualProlog.PropertySet(&options, key, val)) {
112 return 0;
114 return -1;
117 Sci_Position SCI_METHOD LexerVisualProlog::WordListSet(int n, const char *wl) {
118 WordList *wordListN = 0;
119 switch (n) {
120 case 0:
121 wordListN = &majorKeywords;
122 break;
123 case 1:
124 wordListN = &minorKeywords;
125 break;
126 case 2:
127 wordListN = &directiveKeywords;
128 break;
129 case 3:
130 wordListN = &docKeywords;
131 break;
133 Sci_Position firstModification = -1;
134 if (wordListN) {
135 WordList wlNew;
136 wlNew.Set(wl);
137 if (*wordListN != wlNew) {
138 wordListN->Set(wl);
139 firstModification = 0;
142 return firstModification;
145 // Functor used to truncate history
146 struct After {
147 Sci_Position line;
148 After(Sci_Position line_) : line(line_) {}
151 static bool isLowerLetter(int ch){
152 return ccLl == CategoriseCharacter(ch);
155 static bool isUpperLetter(int ch){
156 return ccLu == CategoriseCharacter(ch);
159 static bool isAlphaNum(int ch){
160 CharacterCategory cc = CategoriseCharacter(ch);
161 return (ccLu == cc || ccLl == cc || ccLt == cc || ccLm == cc || ccLo == cc || ccNd == cc || ccNl == cc || ccNo == cc);
164 static bool isStringVerbatimOpenClose(int ch){
165 CharacterCategory cc = CategoriseCharacter(ch);
166 return (ccPc <= cc && cc <= ccSo);
169 static bool isIdChar(int ch){
170 return ('_') == ch || isAlphaNum(ch);
173 static bool isOpenStringVerbatim(int next, int &closingQuote){
174 switch (next) {
175 case L'<':
176 closingQuote = L'>';
177 return true;
178 case L'>':
179 closingQuote = L'<';
180 return true;
181 case L'(':
182 closingQuote = L')';
183 return true;
184 case L')':
185 closingQuote = L'(';
186 return true;
187 case L'[':
188 closingQuote = L']';
189 return true;
190 case L']':
191 closingQuote = L'[';
192 return true;
193 case L'{':
194 closingQuote = L'}';
195 return true;
196 case L'}':
197 closingQuote = L'{';
198 return true;
199 case L'_':
200 case L'.':
201 case L',':
202 case L';':
203 return false;
204 default:
205 if (isStringVerbatimOpenClose(next)) {
206 closingQuote = next;
207 return true;
208 } else {
209 return false;
214 // Look ahead to see which colour "end" should have (takes colour after the following keyword)
215 static void endLookAhead(char s[], LexAccessor &styler, Sci_Position start) {
216 char ch = styler.SafeGetCharAt(start, '\n');
217 while (' ' == ch) {
218 start++;
219 ch = styler.SafeGetCharAt(start, '\n');
221 Sci_Position i = 0;
222 while (i < 100 && isLowerLetter(ch)){
223 s[i] = ch;
224 i++;
225 ch = styler.SafeGetCharAt(start + i, '\n');
227 s[i] = '\0';
230 static void forwardEscapeLiteral(StyleContext &sc, int EscapeState) {
231 sc.Forward();
232 if (sc.Match('"') || sc.Match('\'') || sc.Match('\\') || sc.Match('n') || sc.Match('l') || sc.Match('r') || sc.Match('t')) {
233 sc.ChangeState(EscapeState);
234 } else if (sc.Match('u')) {
235 if (IsADigit(sc.chNext, 16)) {
236 sc.Forward();
237 if (IsADigit(sc.chNext, 16)) {
238 sc.Forward();
239 if (IsADigit(sc.chNext, 16)) {
240 sc.Forward();
241 if (IsADigit(sc.chNext, 16)) {
242 sc.Forward();
243 sc.ChangeState(EscapeState);
251 void SCI_METHOD LexerVisualProlog::Lex(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess) {
252 LexAccessor styler(pAccess);
253 CharacterSet setDoxygen(CharacterSet::setAlpha, "");
254 CharacterSet setNumber(CharacterSet::setNone, "0123456789abcdefABCDEFxoXO");
256 StyleContext sc(startPos, length, initStyle, styler, 0x7f);
258 int styleBeforeDocKeyword = SCE_VISUALPROLOG_DEFAULT;
259 Sci_Position currentLine = styler.GetLine(startPos);
261 int closingQuote = '"';
262 int nestLevel = 0;
263 if (currentLine >= 1)
265 nestLevel = styler.GetLineState(currentLine - 1);
266 closingQuote = nestLevel;
269 // Truncate ppDefineHistory before current line
271 for (; sc.More(); sc.Forward()) {
273 // Determine if the current state should terminate.
274 switch (sc.state) {
275 case SCE_VISUALPROLOG_OPERATOR:
276 sc.SetState(SCE_VISUALPROLOG_DEFAULT);
277 break;
278 case SCE_VISUALPROLOG_NUMBER:
279 // We accept almost anything because of hex. and number suffixes
280 if (!(setNumber.Contains(sc.ch)) || (sc.Match('.') && IsADigit(sc.chNext))) {
281 sc.SetState(SCE_VISUALPROLOG_DEFAULT);
283 break;
284 case SCE_VISUALPROLOG_IDENTIFIER:
285 if (!isIdChar(sc.ch)) {
286 char s[1000];
287 sc.GetCurrent(s, sizeof(s));
288 if (0 == strcmp(s, "end")) {
289 endLookAhead(s, styler, sc.currentPos);
291 if (majorKeywords.InList(s)) {
292 sc.ChangeState(SCE_VISUALPROLOG_KEY_MAJOR);
293 } else if (minorKeywords.InList(s)) {
294 sc.ChangeState(SCE_VISUALPROLOG_KEY_MINOR);
296 sc.SetState(SCE_VISUALPROLOG_DEFAULT);
298 break;
299 case SCE_VISUALPROLOG_VARIABLE:
300 case SCE_VISUALPROLOG_ANONYMOUS:
301 if (!isIdChar(sc.ch)) {
302 sc.SetState(SCE_VISUALPROLOG_DEFAULT);
304 break;
305 case SCE_VISUALPROLOG_KEY_DIRECTIVE:
306 if (!isLowerLetter(sc.ch)) {
307 char s[1000];
308 sc.GetCurrent(s, sizeof(s));
309 if (!directiveKeywords.InList(s+1)) {
310 sc.ChangeState(SCE_VISUALPROLOG_IDENTIFIER);
312 sc.SetState(SCE_VISUALPROLOG_DEFAULT);
314 break;
315 case SCE_VISUALPROLOG_COMMENT_BLOCK:
316 if (sc.Match('*', '/')) {
317 sc.Forward();
318 nestLevel--;
319 int nextState = (nestLevel == 0) ? SCE_VISUALPROLOG_DEFAULT : SCE_VISUALPROLOG_COMMENT_BLOCK;
320 sc.ForwardSetState(nextState);
321 } else if (sc.Match('/', '*')) {
322 sc.Forward();
323 nestLevel++;
324 } else if (sc.Match('@')) {
325 styleBeforeDocKeyword = sc.state;
326 sc.SetState(SCE_VISUALPROLOG_COMMENT_KEY_ERROR);
328 break;
329 case SCE_VISUALPROLOG_COMMENT_LINE:
330 if (sc.atLineEnd) {
331 int nextState = (nestLevel == 0) ? SCE_VISUALPROLOG_DEFAULT : SCE_VISUALPROLOG_COMMENT_BLOCK;
332 sc.SetState(nextState);
333 } else if (sc.Match('@')) {
334 styleBeforeDocKeyword = sc.state;
335 sc.SetState(SCE_VISUALPROLOG_COMMENT_KEY_ERROR);
337 break;
338 case SCE_VISUALPROLOG_COMMENT_KEY_ERROR:
339 if (!setDoxygen.Contains(sc.ch) || sc.atLineEnd) {
340 char s[1000];
341 sc.GetCurrent(s, sizeof(s));
342 if (docKeywords.InList(s+1)) {
343 sc.ChangeState(SCE_VISUALPROLOG_COMMENT_KEY);
345 if (SCE_VISUALPROLOG_COMMENT_LINE == styleBeforeDocKeyword && sc.atLineEnd) {
346 // end line comment
347 int nextState = (nestLevel == 0) ? SCE_VISUALPROLOG_DEFAULT : SCE_VISUALPROLOG_COMMENT_BLOCK;
348 sc.SetState(nextState);
349 } else {
350 sc.SetState(styleBeforeDocKeyword);
351 if (SCE_VISUALPROLOG_COMMENT_BLOCK == styleBeforeDocKeyword && sc.Match('*', '/')) {
352 // we have consumed the '*' if it comes immediately after the docKeyword
353 sc.Forward();
354 sc.Forward();
355 nestLevel--;
356 if (0 == nestLevel) {
357 sc.SetState(SCE_VISUALPROLOG_DEFAULT);
362 break;
363 case SCE_VISUALPROLOG_STRING_ESCAPE:
364 case SCE_VISUALPROLOG_STRING_ESCAPE_ERROR:
365 // return to SCE_VISUALPROLOG_STRING and treat as such (fall-through)
366 sc.SetState(SCE_VISUALPROLOG_STRING);
367 // Falls through.
368 case SCE_VISUALPROLOG_STRING:
369 if (sc.atLineEnd) {
370 sc.SetState(SCE_VISUALPROLOG_STRING_EOL_OPEN);
371 } else if (sc.Match(closingQuote)) {
372 sc.ForwardSetState(SCE_VISUALPROLOG_DEFAULT);
373 } else if (sc.Match('\\')) {
374 sc.SetState(SCE_VISUALPROLOG_STRING_ESCAPE_ERROR);
375 forwardEscapeLiteral(sc, SCE_VISUALPROLOG_STRING_ESCAPE);
377 break;
378 case SCE_VISUALPROLOG_STRING_EOL_OPEN:
379 if (sc.atLineStart) {
380 sc.SetState(SCE_VISUALPROLOG_DEFAULT);
382 break;
383 case SCE_VISUALPROLOG_STRING_VERBATIM_SPECIAL:
384 case SCE_VISUALPROLOG_STRING_VERBATIM_EOL:
385 // return to SCE_VISUALPROLOG_STRING_VERBATIM and treat as such (fall-through)
386 sc.SetState(SCE_VISUALPROLOG_STRING_VERBATIM);
387 // Falls through.
388 case SCE_VISUALPROLOG_STRING_VERBATIM:
389 if (sc.atLineEnd) {
390 sc.SetState(SCE_VISUALPROLOG_STRING_VERBATIM_EOL);
391 } else if (sc.Match(closingQuote)) {
392 if (closingQuote == sc.chNext) {
393 sc.SetState(SCE_VISUALPROLOG_STRING_VERBATIM_SPECIAL);
394 sc.Forward();
395 } else {
396 sc.ForwardSetState(SCE_VISUALPROLOG_DEFAULT);
399 break;
402 if (sc.atLineEnd) {
403 // Update the line state, so it can be seen by next line
404 int lineState = 0;
405 if (SCE_VISUALPROLOG_STRING_VERBATIM_EOL == sc.state) {
406 lineState = closingQuote;
407 } else if (SCE_VISUALPROLOG_COMMENT_BLOCK == sc.state) {
408 lineState = nestLevel;
410 styler.SetLineState(currentLine, lineState);
411 currentLine++;
414 // Determine if a new state should be entered.
415 if (sc.state == SCE_VISUALPROLOG_DEFAULT) {
416 if (sc.Match('@') && isOpenStringVerbatim(sc.chNext, closingQuote)) {
417 sc.SetState(SCE_VISUALPROLOG_STRING_VERBATIM);
418 sc.Forward();
419 } else if (IsADigit(sc.ch) || (sc.Match('.') && IsADigit(sc.chNext))) {
420 sc.SetState(SCE_VISUALPROLOG_NUMBER);
421 } else if (isLowerLetter(sc.ch)) {
422 sc.SetState(SCE_VISUALPROLOG_IDENTIFIER);
423 } else if (isUpperLetter(sc.ch)) {
424 sc.SetState(SCE_VISUALPROLOG_VARIABLE);
425 } else if (sc.Match('_')) {
426 sc.SetState(SCE_VISUALPROLOG_ANONYMOUS);
427 } else if (sc.Match('/', '*')) {
428 sc.SetState(SCE_VISUALPROLOG_COMMENT_BLOCK);
429 nestLevel = 1;
430 sc.Forward(); // Eat the * so it isn't used for the end of the comment
431 } else if (sc.Match('%')) {
432 sc.SetState(SCE_VISUALPROLOG_COMMENT_LINE);
433 } else if (sc.Match('\'')) {
434 closingQuote = '\'';
435 sc.SetState(SCE_VISUALPROLOG_STRING);
436 } else if (sc.Match('"')) {
437 closingQuote = '"';
438 sc.SetState(SCE_VISUALPROLOG_STRING);
439 } else if (sc.Match('#')) {
440 sc.SetState(SCE_VISUALPROLOG_KEY_DIRECTIVE);
441 } else if (isoperator(static_cast<char>(sc.ch)) || sc.Match('\\')) {
442 sc.SetState(SCE_VISUALPROLOG_OPERATOR);
447 sc.Complete();
448 styler.Flush();
451 // Store both the current line's fold level and the next lines in the
452 // level store to make it easy to pick up with each increment
453 // and to make it possible to fiddle the current level for "} else {".
455 void SCI_METHOD LexerVisualProlog::Fold(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess) {
457 LexAccessor styler(pAccess);
459 Sci_PositionU endPos = startPos + length;
460 int visibleChars = 0;
461 Sci_Position currentLine = styler.GetLine(startPos);
462 int levelCurrent = SC_FOLDLEVELBASE;
463 if (currentLine > 0)
464 levelCurrent = styler.LevelAt(currentLine-1) >> 16;
465 int levelMinCurrent = levelCurrent;
466 int levelNext = levelCurrent;
467 char chNext = styler[startPos];
468 int styleNext = styler.StyleAt(startPos);
469 int style = initStyle;
470 for (Sci_PositionU i = startPos; i < endPos; i++) {
471 char ch = chNext;
472 chNext = styler.SafeGetCharAt(i + 1);
473 style = styleNext;
474 styleNext = styler.StyleAt(i + 1);
475 bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
476 if (style == SCE_VISUALPROLOG_OPERATOR) {
477 if (ch == '{') {
478 // Measure the minimum before a '{' to allow
479 // folding on "} else {"
480 if (levelMinCurrent > levelNext) {
481 levelMinCurrent = levelNext;
483 levelNext++;
484 } else if (ch == '}') {
485 levelNext--;
488 if (!IsASpace(ch))
489 visibleChars++;
490 if (atEOL || (i == endPos-1)) {
491 int levelUse = levelCurrent;
492 int lev = levelUse | levelNext << 16;
493 if (levelUse < levelNext)
494 lev |= SC_FOLDLEVELHEADERFLAG;
495 if (lev != styler.LevelAt(currentLine)) {
496 styler.SetLevel(currentLine, lev);
498 currentLine++;
499 levelCurrent = levelNext;
500 levelMinCurrent = levelCurrent;
501 if (atEOL && (i == static_cast<Sci_PositionU>(styler.Length()-1))) {
502 // There is an empty line at end of file so give it same level and empty
503 styler.SetLevel(currentLine, (levelCurrent | levelCurrent << 16) | SC_FOLDLEVELWHITEFLAG);
505 visibleChars = 0;
510 LexerModule lmVisualProlog(SCLEX_VISUALPROLOG, LexerVisualProlog::LexerFactoryVisualProlog, "visualprolog", visualPrologWordLists);