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.
18 #pragma warning(disable: 4786)
27 #include "Scintilla.h"
31 #include "LexAccessor.h"
33 #include "StyleContext.h"
34 #include "CharacterSet.h"
35 #include "LexerModule.h"
36 #include "OptionSet.h"
39 using namespace Scintilla
;
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
;
67 OptionsVisualProlog options
;
68 OptionSetVisualProlog osVisualProlog
;
72 virtual ~LexerVisualProlog() {
74 void SCI_METHOD
Release() {
77 int SCI_METHOD
Version() const {
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 *) {
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
)) {
113 int SCI_METHOD
LexerVisualProlog::WordListSet(int n
, const char *wl
) {
114 WordList
*wordListN
= 0;
117 wordListN
= &majorKeywords
;
120 wordListN
= &minorKeywords
;
123 wordListN
= &directiveKeywords
;
126 wordListN
= &docKeywords
;
129 int firstModification
= -1;
133 if (*wordListN
!= wlNew
) {
135 firstModification
= 0;
138 return firstModification
;
141 // Functor used to truncate history
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');
152 ch
= styler
.SafeGetCharAt(start
, '\n');
155 while (i
< 100 && setIdentifier
.Contains(ch
)){
158 ch
= styler
.SafeGetCharAt(start
+ i
, '\n');
163 static void forwardEscapeLiteral(StyleContext
&sc
, int OwnChar
, int EscapeState
) {
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)) {
170 if (IsADigit(sc
.chNext
, 16)) {
172 if (IsADigit(sc
.chNext
, 16)) {
174 if (IsADigit(sc
.chNext
, 16)) {
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
);
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()) {
210 // Update the line state, so it can be seen by next line
211 styler
.SetLineState(currentLine
, nestLevel
);
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.
227 case SCE_VISUALPROLOG_OPERATOR
:
228 sc
.SetState(SCE_VISUALPROLOG_DEFAULT
);
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
);
236 case SCE_VISUALPROLOG_IDENTIFIER
:
237 if (!setIdentifier
.Contains(sc
.ch
)) {
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
);
251 case SCE_VISUALPROLOG_VARIABLE
:
252 case SCE_VISUALPROLOG_ANONYMOUS
:
253 if (!setIdentifier
.Contains(sc
.ch
)) {
254 sc
.SetState(SCE_VISUALPROLOG_DEFAULT
);
257 case SCE_VISUALPROLOG_KEY_DIRECTIVE
:
258 if (!setLowerStart
.Contains(sc
.ch
)) {
260 sc
.GetCurrent(s
, sizeof(s
));
261 if (!directiveKeywords
.InList(s
+1)) {
262 sc
.ChangeState(SCE_VISUALPROLOG_IDENTIFIER
);
264 sc
.SetState(SCE_VISUALPROLOG_DEFAULT
);
267 case SCE_VISUALPROLOG_COMMENT_BLOCK
:
268 if (sc
.Match('*', '/')) {
271 int nextState
= (nestLevel
== 0) ? SCE_VISUALPROLOG_DEFAULT
: SCE_VISUALPROLOG_COMMENT_BLOCK
;
272 sc
.ForwardSetState(nextState
);
273 } else if (sc
.Match('/', '*')) {
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
);
283 case SCE_VISUALPROLOG_COMMENT_LINE
:
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
);
292 case SCE_VISUALPROLOG_COMMENT_KEY_ERROR
:
293 if (!setDoxygen
.Contains(sc
.ch
)) {
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
);
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
:
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
);
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
);
329 case SCE_VISUALPROLOG_CHARACTER
:
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
);
337 sc
.SetState(SCE_VISUALPROLOG_CHARACTER_ESCAPE_ERROR
);
338 forwardEscapeLiteral(sc
, '\'', SCE_VISUALPROLOG_CHARACTER
);
340 sc
.ForwardSetState(SCE_VISUALPROLOG_CHARACTER
);
342 sc
.ForwardSetState(SCE_VISUALPROLOG_DEFAULT
);
344 sc
.SetState(SCE_VISUALPROLOG_CHARACTER_TOO_MANY
);
348 case SCE_VISUALPROLOG_STRING_EOL_OPEN
:
349 if (sc
.atLineStart
) {
350 sc
.SetState(SCE_VISUALPROLOG_DEFAULT
);
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
:
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
);
365 sc
.ForwardSetState(SCE_VISUALPROLOG_DEFAULT
);
371 if (sc
.atLineEnd
&& !atLineEndBeforeSwitch
) {
372 // State exit processing consumed characters up to end of line.
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
);
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
);
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
);
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
;
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
++) {
432 chNext
= styler
.SafeGetCharAt(i
+ 1);
434 styleNext
= styler
.StyleAt(i
+ 1);
435 bool atEOL
= (ch
== '\r' && chNext
!= '\n') || (ch
== '\n');
436 if (style
== SCE_VISUALPROLOG_OPERATOR
) {
438 // Measure the minimum before a '{' to allow
439 // folding on "} else {"
440 if (levelMinCurrent
> levelNext
) {
441 levelMinCurrent
= levelNext
;
444 } else if (ch
== '}') {
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
);
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
);
470 LexerModule
lmVisualProlog(SCLEX_VISUALPROLOG
, LexerVisualProlog::LexerFactoryVisualProlog
, "visualprolog", visualPrologWordLists
);