1 // Scintilla source code edit control
2 /** @file LexVisualProlog.cxx
3 ** Lexer for Visual Prolog.
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
22 #pragma warning(disable: 4786)
31 #include "Scintilla.h"
35 #include "LexAccessor.h"
37 #include "StyleContext.h"
38 #include "CharacterSet.h"
39 #include "CharacterCategory.h"
40 #include "LexerModule.h"
41 #include "OptionSet.h"
44 using namespace Scintilla
;
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
;
72 OptionsVisualProlog options
;
73 OptionSetVisualProlog osVisualProlog
;
77 virtual ~LexerVisualProlog() {
79 void SCI_METHOD
Release() {
82 int SCI_METHOD
Version() const {
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 *) {
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
)) {
118 int SCI_METHOD
LexerVisualProlog::WordListSet(int n
, const char *wl
) {
119 WordList
*wordListN
= 0;
122 wordListN
= &majorKeywords
;
125 wordListN
= &minorKeywords
;
128 wordListN
= &directiveKeywords
;
131 wordListN
= &docKeywords
;
134 int firstModification
= -1;
138 if (*wordListN
!= wlNew
) {
140 firstModification
= 0;
143 return firstModification
;
146 // Functor used to truncate history
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
){
201 if (isAlphaNum(next
)) {
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');
215 ch
= styler
.SafeGetCharAt(start
, '\n');
218 while (i
< 100 && isLowerLetter(ch
)){
221 ch
= styler
.SafeGetCharAt(start
+ i
, '\n');
226 static void forwardEscapeLiteral(StyleContext
&sc
, int EscapeState
) {
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)) {
233 if (IsADigit(sc
.chNext
, 16)) {
235 if (IsADigit(sc
.chNext
, 16)) {
237 if (IsADigit(sc
.chNext
, 16)) {
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
= '"';
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.
271 case SCE_VISUALPROLOG_OPERATOR
:
272 sc
.SetState(SCE_VISUALPROLOG_DEFAULT
);
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
);
280 case SCE_VISUALPROLOG_IDENTIFIER
:
281 if (!isIdChar(sc
.ch
)) {
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
);
295 case SCE_VISUALPROLOG_VARIABLE
:
296 case SCE_VISUALPROLOG_ANONYMOUS
:
297 if (!isIdChar(sc
.ch
)) {
298 sc
.SetState(SCE_VISUALPROLOG_DEFAULT
);
301 case SCE_VISUALPROLOG_KEY_DIRECTIVE
:
302 if (!isLowerLetter(sc
.ch
)) {
304 sc
.GetCurrent(s
, sizeof(s
));
305 if (!directiveKeywords
.InList(s
+1)) {
306 sc
.ChangeState(SCE_VISUALPROLOG_IDENTIFIER
);
308 sc
.SetState(SCE_VISUALPROLOG_DEFAULT
);
311 case SCE_VISUALPROLOG_COMMENT_BLOCK
:
312 if (sc
.Match('*', '/')) {
315 int nextState
= (nestLevel
== 0) ? SCE_VISUALPROLOG_DEFAULT
: SCE_VISUALPROLOG_COMMENT_BLOCK
;
316 sc
.ForwardSetState(nextState
);
317 } else if (sc
.Match('/', '*')) {
320 } else if (sc
.Match('@')) {
321 styleBeforeDocKeyword
= sc
.state
;
322 sc
.SetState(SCE_VISUALPROLOG_COMMENT_KEY_ERROR
);
325 case SCE_VISUALPROLOG_COMMENT_LINE
:
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
);
334 case SCE_VISUALPROLOG_COMMENT_KEY_ERROR
:
335 if (!setDoxygen
.Contains(sc
.ch
) || sc
.atLineEnd
) {
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
) {
343 int nextState
= (nestLevel
== 0) ? SCE_VISUALPROLOG_DEFAULT
: SCE_VISUALPROLOG_COMMENT_BLOCK
;
344 sc
.SetState(nextState
);
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
352 if (0 == nestLevel
) {
353 sc
.SetState(SCE_VISUALPROLOG_DEFAULT
);
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
:
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
);
373 case SCE_VISUALPROLOG_STRING_EOL_OPEN
:
374 if (sc
.atLineStart
) {
375 sc
.SetState(SCE_VISUALPROLOG_DEFAULT
);
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
:
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
);
390 sc
.ForwardSetState(SCE_VISUALPROLOG_DEFAULT
);
397 // Update the line state, so it can be seen by next line
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
);
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
);
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
);
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('\'')) {
429 sc
.SetState(SCE_VISUALPROLOG_STRING
);
430 } else if (sc
.Match('"')) {
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
);
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
;
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
++) {
466 chNext
= styler
.SafeGetCharAt(i
+ 1);
468 styleNext
= styler
.StyleAt(i
+ 1);
469 bool atEOL
= (ch
== '\r' && chNext
!= '\n') || (ch
== '\n');
470 if (style
== SCE_VISUALPROLOG_OPERATOR
) {
472 // Measure the minimum before a '{' to allow
473 // folding on "} else {"
474 if (levelMinCurrent
> levelNext
) {
475 levelMinCurrent
= levelNext
;
478 } else if (ch
== '}') {
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
);
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
);
504 LexerModule
lmVisualProlog(SCLEX_VISUALPROLOG
, LexerVisualProlog::LexerFactoryVisualProlog
, "visualprolog", visualPrologWordLists
);