1 | // Scintilla source code edit control |
2 | |
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[] |
8 | |
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. |
11 | |
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). |
14 | |
15 | #include <stdlib.h> |
16 | #include <string.h> |
17 | #include <stdio.h> |
18 | #include <stdarg.h> |
19 | #include <assert.h> |
20 | #include <ctype.h> |
21 | |
22 | #include <string> |
23 | #include <string_view> |
24 | |
25 | #include "ILexer.h" |
26 | #include "Scintilla.h" |
27 | #include "SciLexer.h" |
28 | |
29 | #include "WordList.h" |
30 | #include "LexAccessor.h" |
31 | #include "Accessor.h" |
32 | #include "StyleContext.h" |
33 | #include "CharacterSet.h" |
34 | #include "LexerModule.h" |
35 | |
36 | using namespace Lexilla; |
37 | |
38 | // val SCE_METAPOST_DEFAULT = 0 |
39 | // val SCE_METAPOST_SPECIAL = 1 |
40 | // val SCE_METAPOST_GROUP = 2 |
41 | // val SCE_METAPOST_SYMBOL = 3 |
42 | // val SCE_METAPOST_COMMAND = 4 |
43 | // val SCE_METAPOST_TEXT = 5 |
44 | |
45 | // Definitions in SciTEGlobal.properties: |
46 | // |
47 | // Metapost Highlighting |
48 | // |
49 | // # Default |
50 | // style.metapost.0=fore:#7F7F00 |
51 | // # Special |
52 | // style.metapost.1=fore:#007F7F |
53 | // # Group |
54 | // style.metapost.2=fore:#880000 |
55 | // # Symbol |
56 | // style.metapost.3=fore:#7F7F00 |
57 | // # Command |
58 | // style.metapost.4=fore:#008800 |
59 | // # Text |
60 | // style.metapost.5=fore:#000000 |
61 | |
62 | // lexer.tex.comment.process=0 |
63 | |
64 | // Auxiliary functions: |
65 | |
66 | static inline bool endOfLine(Accessor &styler, Sci_PositionU i) { |
67 | return |
68 | (styler[i] == '\n') || ((styler[i] == '\r') && (styler.SafeGetCharAt(i + 1) != '\n')) ; |
69 | } |
70 | |
71 | static inline bool (int ch) { |
72 | return |
73 | (ch == '%') ; |
74 | } |
75 | |
76 | static inline bool isMETAPOSTone(int ch) { |
77 | return |
78 | (ch == '[') || (ch == ']') || (ch == '(') || (ch == ')') || |
79 | (ch == ':') || (ch == '=') || (ch == '<') || (ch == '>') || |
80 | (ch == '{') || (ch == '}') || (ch == '\'') || (ch == '\"') ; |
81 | } |
82 | |
83 | static inline bool isMETAPOSTtwo(int ch) { |
84 | return |
85 | (ch == ';') || (ch == '$') || (ch == '@') || (ch == '#'); |
86 | } |
87 | |
88 | static inline bool isMETAPOSTthree(int ch) { |
89 | return |
90 | (ch == '.') || (ch == '-') || (ch == '+') || (ch == '/') || |
91 | (ch == '*') || (ch == ',') || (ch == '|') || (ch == '`') || |
92 | (ch == '!') || (ch == '?') || (ch == '^') || (ch == '&') || |
93 | (ch == '%') ; |
94 | } |
95 | |
96 | static inline bool isMETAPOSTidentifier(int ch) { |
97 | return |
98 | ((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z')) || |
99 | (ch == '_') ; |
100 | } |
101 | |
102 | static inline bool isMETAPOSTnumber(int ch) { |
103 | return |
104 | (ch >= '0') && (ch <= '9') ; |
105 | } |
106 | |
107 | static inline bool isMETAPOSTstring(int ch) { |
108 | return |
109 | (ch == '\"') ; |
110 | } |
111 | |
112 | static inline bool isMETAPOSTcolon(int ch) { |
113 | return |
114 | (ch == ':') ; |
115 | } |
116 | |
117 | static inline bool isMETAPOSTequal(int ch) { |
118 | return |
119 | (ch == '=') ; |
120 | } |
121 | |
122 | static int CheckMETAPOSTInterface( |
123 | Sci_PositionU startPos, |
124 | Sci_Position length, |
125 | Accessor &styler, |
126 | int defaultInterface) { |
127 | |
128 | char lineBuffer[1024] ; |
129 | Sci_PositionU linePos = 0 ; |
130 | |
131 | // some day we can make something lexer.metapost.mapping=(none,0)(metapost,1)(mp,1)(metafun,2)... |
132 | |
133 | if (styler.SafeGetCharAt(0) == '%') { |
134 | for (Sci_PositionU i = 0; i < startPos + length; i++) { |
135 | lineBuffer[linePos++] = styler.SafeGetCharAt(i) ; |
136 | if (endOfLine(styler, i) || (linePos >= sizeof(lineBuffer) - 1)) { |
137 | lineBuffer[linePos] = '\0'; |
138 | if (strstr(lineBuffer, "interface=none" )) { |
139 | return 0 ; |
140 | } else if (strstr(lineBuffer, "interface=metapost" ) || strstr(lineBuffer, "interface=mp" )) { |
141 | return 1 ; |
142 | } else if (strstr(lineBuffer, "interface=metafun" )) { |
143 | return 2 ; |
144 | } else if (styler.SafeGetCharAt(1) == 'D' && strstr(lineBuffer, "%D \\module" )) { |
145 | // better would be to limit the search to just one line |
146 | return 2 ; |
147 | } else { |
148 | return defaultInterface ; |
149 | } |
150 | } |
151 | } |
152 | } |
153 | |
154 | return defaultInterface ; |
155 | } |
156 | |
157 | static void ColouriseMETAPOSTDoc( |
158 | Sci_PositionU startPos, |
159 | Sci_Position length, |
160 | int, |
161 | WordList *keywordlists[], |
162 | Accessor &styler) { |
163 | |
164 | styler.StartAt(startPos) ; |
165 | styler.StartSegment(startPos) ; |
166 | |
167 | bool = styler.GetPropertyInt("lexer.metapost.comment.process" , 0) == 1 ; |
168 | int defaultInterface = styler.GetPropertyInt("lexer.metapost.interface.default" , 1) ; |
169 | |
170 | int currentInterface = CheckMETAPOSTInterface(startPos,length,styler,defaultInterface) ; |
171 | |
172 | // 0 no keyword highlighting |
173 | // 1 metapost keyword hightlighting |
174 | // 2+ metafun keyword hightlighting |
175 | |
176 | int = 0 ; |
177 | |
178 | if (currentInterface != 0) { |
179 | extraInterface = currentInterface ; |
180 | } |
181 | |
182 | WordList &keywords = *keywordlists[0] ; |
183 | WordList kwEmpty; |
184 | WordList &keywords2 = (extraInterface > 0) ? *keywordlists[extraInterface - 1] : kwEmpty; |
185 | |
186 | StyleContext sc(startPos, length, SCE_METAPOST_TEXT, styler) ; |
187 | |
188 | char key[100] ; |
189 | |
190 | bool inTeX = false ; |
191 | bool = false ; |
192 | bool inString = false ; |
193 | bool inClause = false ; |
194 | |
195 | bool going = sc.More() ; // needed because of a fuzzy end of file state |
196 | |
197 | for (; going; sc.Forward()) { |
198 | |
199 | if (! sc.More()) { going = false ; } // we need to go one behind the end of text |
200 | |
201 | if (inClause) { |
202 | sc.SetState(SCE_METAPOST_TEXT) ; |
203 | inClause = false ; |
204 | } |
205 | |
206 | if (inComment) { |
207 | if (sc.atLineEnd) { |
208 | sc.SetState(SCE_METAPOST_TEXT) ; |
209 | inTeX = false ; |
210 | inComment = false ; |
211 | inClause = false ; |
212 | inString = false ; // not correct but we want to stimulate one-lines |
213 | } |
214 | } else if (inString) { |
215 | if (isMETAPOSTstring(sc.ch)) { |
216 | sc.SetState(SCE_METAPOST_SPECIAL) ; |
217 | sc.ForwardSetState(SCE_METAPOST_TEXT) ; |
218 | inString = false ; |
219 | } else if (sc.atLineEnd) { |
220 | sc.SetState(SCE_METAPOST_TEXT) ; |
221 | inTeX = false ; |
222 | inComment = false ; |
223 | inClause = false ; |
224 | inString = false ; // not correct but we want to stimulate one-lines |
225 | } |
226 | } else { |
227 | if ((! isMETAPOSTidentifier(sc.ch)) && (sc.LengthCurrent() > 0)) { |
228 | if (sc.state == SCE_METAPOST_COMMAND) { |
229 | sc.GetCurrent(key, sizeof(key)) ; |
230 | if ((strcmp(key,"btex" ) == 0) || (strcmp(key,"verbatimtex" ) == 0)) { |
231 | sc.ChangeState(SCE_METAPOST_GROUP) ; |
232 | inTeX = true ; |
233 | } else if (inTeX) { |
234 | if (strcmp(key,"etex" ) == 0) { |
235 | sc.ChangeState(SCE_METAPOST_GROUP) ; |
236 | inTeX = false ; |
237 | } else { |
238 | sc.ChangeState(SCE_METAPOST_TEXT) ; |
239 | } |
240 | } else { |
241 | if (keywords && keywords.InList(key)) { |
242 | sc.ChangeState(SCE_METAPOST_COMMAND) ; |
243 | } else if (keywords2 && keywords2.InList(key)) { |
244 | sc.ChangeState(SCE_METAPOST_EXTRA) ; |
245 | } else { |
246 | sc.ChangeState(SCE_METAPOST_TEXT) ; |
247 | } |
248 | } |
249 | } |
250 | } |
251 | if (isMETAPOSTcomment(sc.ch)) { |
252 | if (! inTeX) { |
253 | sc.SetState(SCE_METAPOST_SYMBOL) ; |
254 | sc.ForwardSetState(SCE_METAPOST_DEFAULT) ; |
255 | inComment = ! processComment ; |
256 | } else { |
257 | sc.SetState(SCE_METAPOST_TEXT) ; |
258 | } |
259 | } else if (isMETAPOSTstring(sc.ch)) { |
260 | if (! inTeX) { |
261 | sc.SetState(SCE_METAPOST_SPECIAL) ; |
262 | if (! isMETAPOSTstring(sc.chNext)) { |
263 | sc.ForwardSetState(SCE_METAPOST_TEXT) ; |
264 | } |
265 | inString = true ; |
266 | } else { |
267 | sc.SetState(SCE_METAPOST_TEXT) ; |
268 | } |
269 | } else if (isMETAPOSTcolon(sc.ch)) { |
270 | if (! inTeX) { |
271 | if (! isMETAPOSTequal(sc.chNext)) { |
272 | sc.SetState(SCE_METAPOST_COMMAND) ; |
273 | inClause = true ; |
274 | } else { |
275 | sc.SetState(SCE_METAPOST_SPECIAL) ; |
276 | } |
277 | } else { |
278 | sc.SetState(SCE_METAPOST_TEXT) ; |
279 | } |
280 | } else if (isMETAPOSTone(sc.ch)) { |
281 | if (! inTeX) { |
282 | sc.SetState(SCE_METAPOST_SPECIAL) ; |
283 | } else { |
284 | sc.SetState(SCE_METAPOST_TEXT) ; |
285 | } |
286 | } else if (isMETAPOSTtwo(sc.ch)) { |
287 | if (! inTeX) { |
288 | sc.SetState(SCE_METAPOST_GROUP) ; |
289 | } else { |
290 | sc.SetState(SCE_METAPOST_TEXT) ; |
291 | } |
292 | } else if (isMETAPOSTthree(sc.ch)) { |
293 | if (! inTeX) { |
294 | sc.SetState(SCE_METAPOST_SYMBOL) ; |
295 | } else { |
296 | sc.SetState(SCE_METAPOST_TEXT) ; |
297 | } |
298 | } else if (isMETAPOSTidentifier(sc.ch)) { |
299 | if (sc.state != SCE_METAPOST_COMMAND) { |
300 | sc.SetState(SCE_METAPOST_TEXT) ; |
301 | sc.ChangeState(SCE_METAPOST_COMMAND) ; |
302 | } |
303 | } else if (isMETAPOSTnumber(sc.ch)) { |
304 | // rather redundant since for the moment we don't handle numbers |
305 | sc.SetState(SCE_METAPOST_TEXT) ; |
306 | } else if (sc.atLineEnd) { |
307 | sc.SetState(SCE_METAPOST_TEXT) ; |
308 | inTeX = false ; |
309 | inComment = false ; |
310 | inClause = false ; |
311 | inString = false ; |
312 | } else { |
313 | sc.SetState(SCE_METAPOST_TEXT) ; |
314 | } |
315 | } |
316 | |
317 | } |
318 | |
319 | sc.Complete(); |
320 | |
321 | } |
322 | |
323 | // Hooks info the system: |
324 | |
325 | static const char * const metapostWordListDesc[] = { |
326 | "MetaPost" , |
327 | "MetaFun" , |
328 | 0 |
329 | } ; |
330 | |
331 | static int classifyFoldPointMetapost(const char* s,WordList *keywordlists[]) { |
332 | WordList& keywordsStart=*keywordlists[3]; |
333 | WordList& keywordsStop1=*keywordlists[4]; |
334 | |
335 | if (keywordsStart.InList(s)) {return 1;} |
336 | else if (keywordsStop1.InList(s)) {return -1;} |
337 | return 0; |
338 | |
339 | } |
340 | |
341 | static int ParseMetapostWord(Sci_PositionU pos, Accessor &styler, char *word) |
342 | { |
343 | int length=0; |
344 | char ch=styler.SafeGetCharAt(pos); |
345 | *word=0; |
346 | |
347 | while(isMETAPOSTidentifier(ch) && isalpha(ch) && length<100){ |
348 | word[length]=ch; |
349 | length++; |
350 | ch=styler.SafeGetCharAt(pos+length); |
351 | } |
352 | word[length]=0; |
353 | return length; |
354 | } |
355 | |
356 | static void FoldMetapostDoc(Sci_PositionU startPos, Sci_Position length, int, WordList *keywordlists[], Accessor &styler) |
357 | { |
358 | bool foldCompact = styler.GetPropertyInt("fold.compact" , 1) != 0; |
359 | Sci_PositionU endPos = startPos+length; |
360 | int visibleChars=0; |
361 | Sci_Position lineCurrent=styler.GetLine(startPos); |
362 | int levelPrev=styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK; |
363 | int levelCurrent=levelPrev; |
364 | char chNext=styler[startPos]; |
365 | |
366 | char buffer[100]="" ; |
367 | |
368 | for (Sci_PositionU i=startPos; i < endPos; i++) { |
369 | char ch=chNext; |
370 | chNext=styler.SafeGetCharAt(i+1); |
371 | char chPrev=styler.SafeGetCharAt(i-1); |
372 | bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n'); |
373 | |
374 | if(i==0 || chPrev == '\r' || chPrev=='\n'|| chPrev==' '|| chPrev=='(' || chPrev=='$') |
375 | { |
376 | ParseMetapostWord(i, styler, buffer); |
377 | levelCurrent += classifyFoldPointMetapost(buffer,keywordlists); |
378 | } |
379 | |
380 | if (atEOL) { |
381 | int lev = levelPrev; |
382 | if (visibleChars == 0 && foldCompact) |
383 | lev |= SC_FOLDLEVELWHITEFLAG; |
384 | if ((levelCurrent > levelPrev) && (visibleChars > 0)) |
385 | lev |= SC_FOLDLEVELHEADERFLAG; |
386 | if (lev != styler.LevelAt(lineCurrent)) { |
387 | styler.SetLevel(lineCurrent, lev); |
388 | } |
389 | lineCurrent++; |
390 | levelPrev = levelCurrent; |
391 | visibleChars = 0; |
392 | } |
393 | |
394 | if (!isspacechar(ch)) |
395 | visibleChars++; |
396 | } |
397 | // Fill in the real level of the next line, keeping the current flags as they will be filled in later |
398 | int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK; |
399 | styler.SetLevel(lineCurrent, levelPrev | flagsNext); |
400 | |
401 | } |
402 | |
403 | |
404 | LexerModule lmMETAPOST(SCLEX_METAPOST, ColouriseMETAPOSTDoc, "metapost" , FoldMetapostDoc, metapostWordListDesc); |
405 | |