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