1 // Scintilla source code edit control
3 // File: LexMetapost.cxx - general context conformant metapost coloring scheme
4 // Author: Hans Hagen - PRAGMA ADE - Hasselt NL - www.pragma-ade.com
5 // Version: September 28, 2003
6 // Modified by instanton: July 10, 2007
7 // Folding based on keywordlists[]
9 // Copyright: 1998-2003 by Neil Hodgson <neilh@scintilla.org>
10 // The License.txt file describes the conditions under which this software may be distributed.
12 // This lexer is derived from the one written for the texwork environment (1999++) which in
13 // turn is inspired on texedit (1991++) which finds its roots in wdt (1986).
23 #include "Scintilla.h"
27 #include "LexAccessor.h"
29 #include "StyleContext.h"
30 #include "CharacterSet.h"
31 #include "LexerModule.h"
33 using namespace Scintilla
;
35 // val SCE_METAPOST_DEFAULT = 0
36 // val SCE_METAPOST_SPECIAL = 1
37 // val SCE_METAPOST_GROUP = 2
38 // val SCE_METAPOST_SYMBOL = 3
39 // val SCE_METAPOST_COMMAND = 4
40 // val SCE_METAPOST_TEXT = 5
42 // Definitions in SciTEGlobal.properties:
44 // Metapost Highlighting
47 // style.metapost.0=fore:#7F7F00
49 // style.metapost.1=fore:#007F7F
51 // style.metapost.2=fore:#880000
53 // style.metapost.3=fore:#7F7F00
55 // style.metapost.4=fore:#008800
57 // style.metapost.5=fore:#000000
59 // lexer.tex.comment.process=0
61 // Auxiliary functions:
63 static inline bool endOfLine(Accessor
&styler
, Sci_PositionU i
) {
65 (styler
[i
] == '\n') || ((styler
[i
] == '\r') && (styler
.SafeGetCharAt(i
+ 1) != '\n')) ;
68 static inline bool isMETAPOSTcomment(int ch
) {
73 static inline bool isMETAPOSTone(int ch
) {
75 (ch
== '[') || (ch
== ']') || (ch
== '(') || (ch
== ')') ||
76 (ch
== ':') || (ch
== '=') || (ch
== '<') || (ch
== '>') ||
77 (ch
== '{') || (ch
== '}') || (ch
== '\'') || (ch
== '\"') ;
80 static inline bool isMETAPOSTtwo(int ch
) {
82 (ch
== ';') || (ch
== '$') || (ch
== '@') || (ch
== '#');
85 static inline bool isMETAPOSTthree(int ch
) {
87 (ch
== '.') || (ch
== '-') || (ch
== '+') || (ch
== '/') ||
88 (ch
== '*') || (ch
== ',') || (ch
== '|') || (ch
== '`') ||
89 (ch
== '!') || (ch
== '?') || (ch
== '^') || (ch
== '&') ||
93 static inline bool isMETAPOSTidentifier(int ch
) {
95 ((ch
>= 'a') && (ch
<= 'z')) || ((ch
>= 'A') && (ch
<= 'Z')) ||
99 static inline bool isMETAPOSTnumber(int ch
) {
101 (ch
>= '0') && (ch
<= '9') ;
104 static inline bool isMETAPOSTstring(int ch
) {
109 static inline bool isMETAPOSTcolon(int ch
) {
114 static inline bool isMETAPOSTequal(int ch
) {
119 static int CheckMETAPOSTInterface(
120 Sci_PositionU startPos
,
123 int defaultInterface
) {
125 char lineBuffer
[1024] ;
126 Sci_PositionU linePos
= 0 ;
128 // some day we can make something lexer.metapost.mapping=(none,0)(metapost,1)(mp,1)(metafun,2)...
130 if (styler
.SafeGetCharAt(0) == '%') {
131 for (Sci_PositionU i
= 0; i
< startPos
+ length
; i
++) {
132 lineBuffer
[linePos
++] = styler
.SafeGetCharAt(i
) ;
133 if (endOfLine(styler
, i
) || (linePos
>= sizeof(lineBuffer
) - 1)) {
134 lineBuffer
[linePos
] = '\0';
135 if (strstr(lineBuffer
, "interface=none")) {
137 } else if (strstr(lineBuffer
, "interface=metapost") || strstr(lineBuffer
, "interface=mp")) {
139 } else if (strstr(lineBuffer
, "interface=metafun")) {
141 } else if (styler
.SafeGetCharAt(1) == 'D' && strstr(lineBuffer
, "%D \\module")) {
142 // better would be to limit the search to just one line
145 return defaultInterface
;
151 return defaultInterface
;
154 static void ColouriseMETAPOSTDoc(
155 Sci_PositionU startPos
,
158 WordList
*keywordlists
[],
161 styler
.StartAt(startPos
) ;
162 styler
.StartSegment(startPos
) ;
164 bool processComment
= styler
.GetPropertyInt("lexer.metapost.comment.process", 0) == 1 ;
165 int defaultInterface
= styler
.GetPropertyInt("lexer.metapost.interface.default", 1) ;
167 int currentInterface
= CheckMETAPOSTInterface(startPos
,length
,styler
,defaultInterface
) ;
169 // 0 no keyword highlighting
170 // 1 metapost keyword hightlighting
171 // 2+ metafun keyword hightlighting
173 int extraInterface
= 0 ;
175 if (currentInterface
!= 0) {
176 extraInterface
= currentInterface
;
179 WordList
&keywords
= *keywordlists
[0] ;
180 WordList
&keywords2
= *keywordlists
[extraInterface
-1] ;
182 StyleContext
sc(startPos
, length
, SCE_METAPOST_TEXT
, styler
) ;
187 bool inComment
= false ;
188 bool inString
= false ;
189 bool inClause
= false ;
191 bool going
= sc
.More() ; // needed because of a fuzzy end of file state
193 for (; going
; sc
.Forward()) {
195 if (! sc
.More()) { going
= false ; } // we need to go one behind the end of text
198 sc
.SetState(SCE_METAPOST_TEXT
) ;
204 sc
.SetState(SCE_METAPOST_TEXT
) ;
208 inString
= false ; // not correct but we want to stimulate one-lines
210 } else if (inString
) {
211 if (isMETAPOSTstring(sc
.ch
)) {
212 sc
.SetState(SCE_METAPOST_SPECIAL
) ;
213 sc
.ForwardSetState(SCE_METAPOST_TEXT
) ;
215 } else if (sc
.atLineEnd
) {
216 sc
.SetState(SCE_METAPOST_TEXT
) ;
220 inString
= false ; // not correct but we want to stimulate one-lines
223 if ((! isMETAPOSTidentifier(sc
.ch
)) && (sc
.LengthCurrent() > 0)) {
224 if (sc
.state
== SCE_METAPOST_COMMAND
) {
225 sc
.GetCurrent(key
, sizeof(key
)) ;
226 if ((strcmp(key
,"btex") == 0) || (strcmp(key
,"verbatimtex") == 0)) {
227 sc
.ChangeState(SCE_METAPOST_GROUP
) ;
230 if (strcmp(key
,"etex") == 0) {
231 sc
.ChangeState(SCE_METAPOST_GROUP
) ;
234 sc
.ChangeState(SCE_METAPOST_TEXT
) ;
237 if (keywords
&& keywords
.InList(key
)) {
238 sc
.ChangeState(SCE_METAPOST_COMMAND
) ;
239 } else if (keywords2
&& keywords2
.InList(key
)) {
240 sc
.ChangeState(SCE_METAPOST_EXTRA
) ;
242 sc
.ChangeState(SCE_METAPOST_TEXT
) ;
247 if (isMETAPOSTcomment(sc
.ch
)) {
249 sc
.SetState(SCE_METAPOST_SYMBOL
) ;
250 sc
.ForwardSetState(SCE_METAPOST_DEFAULT
) ;
251 inComment
= ! processComment
;
253 sc
.SetState(SCE_METAPOST_TEXT
) ;
255 } else if (isMETAPOSTstring(sc
.ch
)) {
257 sc
.SetState(SCE_METAPOST_SPECIAL
) ;
258 if (! isMETAPOSTstring(sc
.chNext
)) {
259 sc
.ForwardSetState(SCE_METAPOST_TEXT
) ;
263 sc
.SetState(SCE_METAPOST_TEXT
) ;
265 } else if (isMETAPOSTcolon(sc
.ch
)) {
267 if (! isMETAPOSTequal(sc
.chNext
)) {
268 sc
.SetState(SCE_METAPOST_COMMAND
) ;
271 sc
.SetState(SCE_METAPOST_SPECIAL
) ;
274 sc
.SetState(SCE_METAPOST_TEXT
) ;
276 } else if (isMETAPOSTone(sc
.ch
)) {
278 sc
.SetState(SCE_METAPOST_SPECIAL
) ;
280 sc
.SetState(SCE_METAPOST_TEXT
) ;
282 } else if (isMETAPOSTtwo(sc
.ch
)) {
284 sc
.SetState(SCE_METAPOST_GROUP
) ;
286 sc
.SetState(SCE_METAPOST_TEXT
) ;
288 } else if (isMETAPOSTthree(sc
.ch
)) {
290 sc
.SetState(SCE_METAPOST_SYMBOL
) ;
292 sc
.SetState(SCE_METAPOST_TEXT
) ;
294 } else if (isMETAPOSTidentifier(sc
.ch
)) {
295 if (sc
.state
!= SCE_METAPOST_COMMAND
) {
296 sc
.SetState(SCE_METAPOST_TEXT
) ;
297 sc
.ChangeState(SCE_METAPOST_COMMAND
) ;
299 } else if (isMETAPOSTnumber(sc
.ch
)) {
300 // rather redundant since for the moment we don't handle numbers
301 sc
.SetState(SCE_METAPOST_TEXT
) ;
302 } else if (sc
.atLineEnd
) {
303 sc
.SetState(SCE_METAPOST_TEXT
) ;
309 sc
.SetState(SCE_METAPOST_TEXT
) ;
319 // Hooks info the system:
321 static const char * const metapostWordListDesc
[] = {
327 static int classifyFoldPointMetapost(const char* s
,WordList
*keywordlists
[]) {
328 WordList
& keywordsStart
=*keywordlists
[3];
329 WordList
& keywordsStop1
=*keywordlists
[4];
331 if (keywordsStart
.InList(s
)) {return 1;}
332 else if (keywordsStop1
.InList(s
)) {return -1;}
337 static int ParseMetapostWord(Sci_PositionU pos
, Accessor
&styler
, char *word
)
340 char ch
=styler
.SafeGetCharAt(pos
);
343 while(isMETAPOSTidentifier(ch
) && isalpha(ch
) && length
<100){
346 ch
=styler
.SafeGetCharAt(pos
+length
);
352 static void FoldMetapostDoc(Sci_PositionU startPos
, Sci_Position length
, int, WordList
*keywordlists
[], Accessor
&styler
)
354 bool foldCompact
= styler
.GetPropertyInt("fold.compact", 1) != 0;
355 Sci_PositionU endPos
= startPos
+length
;
357 Sci_Position lineCurrent
=styler
.GetLine(startPos
);
358 int levelPrev
=styler
.LevelAt(lineCurrent
) & SC_FOLDLEVELNUMBERMASK
;
359 int levelCurrent
=levelPrev
;
360 char chNext
=styler
[startPos
];
364 for (Sci_PositionU i
=startPos
; i
< endPos
; i
++) {
366 chNext
=styler
.SafeGetCharAt(i
+1);
367 char chPrev
=styler
.SafeGetCharAt(i
-1);
368 bool atEOL
= (ch
== '\r' && chNext
!= '\n') || (ch
== '\n');
370 if(i
==0 || chPrev
== '\r' || chPrev
=='\n'|| chPrev
==' '|| chPrev
=='(' || chPrev
=='$')
372 ParseMetapostWord(i
, styler
, buffer
);
373 levelCurrent
+= classifyFoldPointMetapost(buffer
,keywordlists
);
378 if (visibleChars
== 0 && foldCompact
)
379 lev
|= SC_FOLDLEVELWHITEFLAG
;
380 if ((levelCurrent
> levelPrev
) && (visibleChars
> 0))
381 lev
|= SC_FOLDLEVELHEADERFLAG
;
382 if (lev
!= styler
.LevelAt(lineCurrent
)) {
383 styler
.SetLevel(lineCurrent
, lev
);
386 levelPrev
= levelCurrent
;
390 if (!isspacechar(ch
))
393 // Fill in the real level of the next line, keeping the current flags as they will be filled in later
394 int flagsNext
= styler
.LevelAt(lineCurrent
) & ~SC_FOLDLEVELNUMBERMASK
;
395 styler
.SetLevel(lineCurrent
, levelPrev
| flagsNext
);
400 LexerModule
lmMETAPOST(SCLEX_METAPOST
, ColouriseMETAPOSTDoc
, "metapost", FoldMetapostDoc
, metapostWordListDesc
);