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"
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
;
71 OptionsVisualProlog options
;
72 OptionSetVisualProlog osVisualProlog
;
76 virtual ~LexerVisualProlog() {
78 void SCI_METHOD
Release() override
{
81 int SCI_METHOD
Version() const override
{
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
{
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
)) {
117 Sci_Position SCI_METHOD
LexerVisualProlog::WordListSet(int n
, const char *wl
) {
118 WordList
*wordListN
= 0;
121 wordListN
= &majorKeywords
;
124 wordListN
= &minorKeywords
;
127 wordListN
= &directiveKeywords
;
130 wordListN
= &docKeywords
;
133 Sci_Position firstModification
= -1;
137 if (*wordListN
!= wlNew
) {
139 firstModification
= 0;
142 return firstModification
;
145 // Functor used to truncate history
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
){
205 if (isStringVerbatimOpenClose(next
)) {
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');
219 ch
= styler
.SafeGetCharAt(start
, '\n');
222 while (i
< 100 && isLowerLetter(ch
)){
225 ch
= styler
.SafeGetCharAt(start
+ i
, '\n');
230 static void forwardEscapeLiteral(StyleContext
&sc
, int EscapeState
) {
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)) {
237 if (IsADigit(sc
.chNext
, 16)) {
239 if (IsADigit(sc
.chNext
, 16)) {
241 if (IsADigit(sc
.chNext
, 16)) {
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
= '"';
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.
275 case SCE_VISUALPROLOG_OPERATOR
:
276 sc
.SetState(SCE_VISUALPROLOG_DEFAULT
);
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
);
284 case SCE_VISUALPROLOG_IDENTIFIER
:
285 if (!isIdChar(sc
.ch
)) {
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
);
299 case SCE_VISUALPROLOG_VARIABLE
:
300 case SCE_VISUALPROLOG_ANONYMOUS
:
301 if (!isIdChar(sc
.ch
)) {
302 sc
.SetState(SCE_VISUALPROLOG_DEFAULT
);
305 case SCE_VISUALPROLOG_KEY_DIRECTIVE
:
306 if (!isLowerLetter(sc
.ch
)) {
308 sc
.GetCurrent(s
, sizeof(s
));
309 if (!directiveKeywords
.InList(s
+1)) {
310 sc
.ChangeState(SCE_VISUALPROLOG_IDENTIFIER
);
312 sc
.SetState(SCE_VISUALPROLOG_DEFAULT
);
315 case SCE_VISUALPROLOG_COMMENT_BLOCK
:
316 if (sc
.Match('*', '/')) {
319 int nextState
= (nestLevel
== 0) ? SCE_VISUALPROLOG_DEFAULT
: SCE_VISUALPROLOG_COMMENT_BLOCK
;
320 sc
.ForwardSetState(nextState
);
321 } else if (sc
.Match('/', '*')) {
324 } else if (sc
.Match('@')) {
325 styleBeforeDocKeyword
= sc
.state
;
326 sc
.SetState(SCE_VISUALPROLOG_COMMENT_KEY_ERROR
);
329 case SCE_VISUALPROLOG_COMMENT_LINE
:
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
);
338 case SCE_VISUALPROLOG_COMMENT_KEY_ERROR
:
339 if (!setDoxygen
.Contains(sc
.ch
) || sc
.atLineEnd
) {
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
) {
347 int nextState
= (nestLevel
== 0) ? SCE_VISUALPROLOG_DEFAULT
: SCE_VISUALPROLOG_COMMENT_BLOCK
;
348 sc
.SetState(nextState
);
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
356 if (0 == nestLevel
) {
357 sc
.SetState(SCE_VISUALPROLOG_DEFAULT
);
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
);
368 case SCE_VISUALPROLOG_STRING
:
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
);
378 case SCE_VISUALPROLOG_STRING_EOL_OPEN
:
379 if (sc
.atLineStart
) {
380 sc
.SetState(SCE_VISUALPROLOG_DEFAULT
);
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
);
388 case SCE_VISUALPROLOG_STRING_VERBATIM
:
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
);
396 sc
.ForwardSetState(SCE_VISUALPROLOG_DEFAULT
);
403 // Update the line state, so it can be seen by next line
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
);
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
);
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
);
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('\'')) {
435 sc
.SetState(SCE_VISUALPROLOG_STRING
);
436 } else if (sc
.Match('"')) {
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
);
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
;
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
++) {
472 chNext
= styler
.SafeGetCharAt(i
+ 1);
474 styleNext
= styler
.StyleAt(i
+ 1);
475 bool atEOL
= (ch
== '\r' && chNext
!= '\n') || (ch
== '\n');
476 if (style
== SCE_VISUALPROLOG_OPERATOR
) {
478 // Measure the minimum before a '{' to allow
479 // folding on "} else {"
480 if (levelMinCurrent
> levelNext
) {
481 levelMinCurrent
= levelNext
;
484 } else if (ch
== '}') {
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
);
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
);
510 LexerModule
lmVisualProlog(SCLEX_VISUALPROLOG
, LexerVisualProlog::LexerFactoryVisualProlog
, "visualprolog", visualPrologWordLists
);