]>
Commit | Line | Data |
---|---|---|
1dcf666d RD |
1 | // Scintilla source code edit control |
2 | /** @file LexFlagShip.cxx | |
3 | ** Lexer for Harbour and FlagShip. | |
4 | ** (Syntactically compatible to other xBase dialects, like Clipper, dBase, Clip, FoxPro etc.) | |
5 | **/ | |
6 | // Copyright 2005 by Randy Butler | |
7 | // Copyright 2010 by Xavi <jarabal/at/gmail.com> (Harbour) | |
8 | // Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org> | |
9 | // The License.txt file describes the conditions under which this software may be distributed. | |
10 | ||
11 | #include <stdlib.h> | |
12 | #include <string.h> | |
13 | #include <stdio.h> | |
14 | #include <stdarg.h> | |
15 | #include <assert.h> | |
16 | #include <ctype.h> | |
17 | ||
18 | #include "ILexer.h" | |
19 | #include "Scintilla.h" | |
20 | #include "SciLexer.h" | |
21 | ||
22 | #include "WordList.h" | |
23 | #include "LexAccessor.h" | |
24 | #include "Accessor.h" | |
25 | #include "StyleContext.h" | |
26 | #include "CharacterSet.h" | |
27 | #include "LexerModule.h" | |
28 | ||
29 | #ifdef SCI_NAMESPACE | |
30 | using namespace Scintilla; | |
31 | #endif | |
32 | ||
33 | // Extended to accept accented characters | |
34 | static inline bool IsAWordChar(int ch) | |
35 | { | |
36 | return ch >= 0x80 || | |
37 | (isalnum(ch) || ch == '_'); | |
38 | } | |
39 | ||
40 | static void ColouriseFlagShipDoc(unsigned int startPos, int length, int initStyle, | |
41 | WordList *keywordlists[], Accessor &styler) | |
42 | { | |
43 | ||
44 | WordList &keywords = *keywordlists[0]; | |
45 | WordList &keywords2 = *keywordlists[1]; | |
46 | WordList &keywords3 = *keywordlists[2]; | |
47 | WordList &keywords4 = *keywordlists[3]; | |
48 | WordList &keywords5 = *keywordlists[4]; | |
49 | ||
50 | // property lexer.flagship.styling.within.preprocessor | |
51 | // For Harbour code, determines whether all preprocessor code is styled in the preprocessor style (0) or only from the | |
52 | // initial # to the end of the command word(1, the default). It also determines how to present text, dump, and disabled code. | |
53 | bool stylingWithinPreprocessor = styler.GetPropertyInt("lexer.flagship.styling.within.preprocessor", 1) != 0; | |
54 | ||
55 | CharacterSet setDoxygen(CharacterSet::setAlpha, "$@\\&<>#{}[]"); | |
56 | ||
57 | int visibleChars = 0; | |
58 | int closeStringChar = 0; | |
59 | int styleBeforeDCKeyword = SCE_FS_DEFAULT; | |
60 | bool bEnableCode = initStyle < SCE_FS_DISABLEDCODE; | |
61 | ||
62 | StyleContext sc(startPos, length, initStyle, styler); | |
63 | ||
64 | for (; sc.More(); sc.Forward()) { | |
65 | ||
66 | // Determine if the current state should terminate. | |
67 | switch (sc.state) { | |
68 | case SCE_FS_OPERATOR: | |
69 | case SCE_FS_OPERATOR_C: | |
70 | case SCE_FS_WORDOPERATOR: | |
71 | sc.SetState(bEnableCode ? SCE_FS_DEFAULT : SCE_FS_DEFAULT_C); | |
72 | break; | |
73 | case SCE_FS_IDENTIFIER: | |
74 | case SCE_FS_IDENTIFIER_C: | |
75 | if (!IsAWordChar(sc.ch)) { | |
76 | char s[64]; | |
77 | sc.GetCurrentLowered(s, sizeof(s)); | |
78 | if (keywords.InList(s)) { | |
79 | sc.ChangeState(bEnableCode ? SCE_FS_KEYWORD : SCE_FS_KEYWORD_C); | |
80 | } else if (keywords2.InList(s)) { | |
81 | sc.ChangeState(bEnableCode ? SCE_FS_KEYWORD2 : SCE_FS_KEYWORD2_C); | |
82 | } else if (bEnableCode && keywords3.InList(s)) { | |
83 | sc.ChangeState(SCE_FS_KEYWORD3); | |
84 | } else if (bEnableCode && keywords4.InList(s)) { | |
85 | sc.ChangeState(SCE_FS_KEYWORD4); | |
86 | }// Else, it is really an identifier... | |
87 | sc.SetState(bEnableCode ? SCE_FS_DEFAULT : SCE_FS_DEFAULT_C); | |
88 | } | |
89 | break; | |
90 | case SCE_FS_NUMBER: | |
91 | if (!IsAWordChar(sc.ch) && !(sc.ch == '.' && IsADigit(sc.chNext))) { | |
92 | sc.SetState(SCE_FS_DEFAULT); | |
93 | } | |
94 | break; | |
95 | case SCE_FS_NUMBER_C: | |
96 | if (!IsAWordChar(sc.ch) && sc.ch != '.') { | |
97 | sc.SetState(SCE_FS_DEFAULT_C); | |
98 | } | |
99 | break; | |
100 | case SCE_FS_CONSTANT: | |
101 | if (!IsAWordChar(sc.ch)) { | |
102 | sc.SetState(SCE_FS_DEFAULT); | |
103 | } | |
104 | break; | |
105 | case SCE_FS_STRING: | |
106 | case SCE_FS_STRING_C: | |
107 | if (sc.ch == closeStringChar) { | |
108 | sc.ForwardSetState(bEnableCode ? SCE_FS_DEFAULT : SCE_FS_DEFAULT_C); | |
109 | } else if (sc.atLineEnd) { | |
110 | sc.ChangeState(bEnableCode ? SCE_FS_STRINGEOL : SCE_FS_STRINGEOL_C); | |
111 | } | |
112 | break; | |
113 | case SCE_FS_STRINGEOL: | |
114 | case SCE_FS_STRINGEOL_C: | |
115 | if (sc.atLineStart) { | |
116 | sc.SetState(bEnableCode ? SCE_FS_DEFAULT : SCE_FS_DEFAULT_C); | |
117 | } | |
118 | break; | |
119 | case SCE_FS_COMMENTDOC: | |
120 | case SCE_FS_COMMENTDOC_C: | |
121 | if (sc.Match('*', '/')) { | |
122 | sc.Forward(); | |
123 | sc.ForwardSetState(bEnableCode ? SCE_FS_DEFAULT : SCE_FS_DEFAULT_C); | |
124 | } else if (sc.ch == '@' || sc.ch == '\\') { // JavaDoc and Doxygen support | |
125 | // Verify that we have the conditions to mark a comment-doc-keyword | |
126 | if ((IsASpace(sc.chPrev) || sc.chPrev == '*') && (!IsASpace(sc.chNext))) { | |
127 | styleBeforeDCKeyword = bEnableCode ? SCE_FS_COMMENTDOC : SCE_FS_COMMENTDOC_C; | |
128 | sc.SetState(SCE_FS_COMMENTDOCKEYWORD); | |
129 | } | |
130 | } | |
131 | break; | |
132 | case SCE_FS_COMMENT: | |
133 | case SCE_FS_COMMENTLINE: | |
134 | if (sc.atLineStart) { | |
135 | sc.SetState(SCE_FS_DEFAULT); | |
136 | } | |
137 | break; | |
138 | case SCE_FS_COMMENTLINEDOC: | |
139 | case SCE_FS_COMMENTLINEDOC_C: | |
140 | if (sc.atLineStart) { | |
141 | sc.SetState(bEnableCode ? SCE_FS_DEFAULT : SCE_FS_DEFAULT_C); | |
142 | } else if (sc.ch == '@' || sc.ch == '\\') { // JavaDoc and Doxygen support | |
143 | // Verify that we have the conditions to mark a comment-doc-keyword | |
144 | if ((IsASpace(sc.chPrev) || sc.chPrev == '/' || sc.chPrev == '!') && (!IsASpace(sc.chNext))) { | |
145 | styleBeforeDCKeyword = bEnableCode ? SCE_FS_COMMENTLINEDOC : SCE_FS_COMMENTLINEDOC_C; | |
146 | sc.SetState(SCE_FS_COMMENTDOCKEYWORD); | |
147 | } | |
148 | } | |
149 | break; | |
150 | case SCE_FS_COMMENTDOCKEYWORD: | |
151 | if ((styleBeforeDCKeyword == SCE_FS_COMMENTDOC || styleBeforeDCKeyword == SCE_FS_COMMENTDOC_C) && | |
152 | sc.Match('*', '/')) { | |
153 | sc.ChangeState(SCE_FS_COMMENTDOCKEYWORDERROR); | |
154 | sc.Forward(); | |
155 | sc.ForwardSetState(bEnableCode ? SCE_FS_DEFAULT : SCE_FS_DEFAULT_C); | |
156 | } else if (!setDoxygen.Contains(sc.ch)) { | |
157 | char s[64]; | |
158 | sc.GetCurrentLowered(s, sizeof(s)); | |
159 | if (!IsASpace(sc.ch) || !keywords5.InList(s + 1)) { | |
160 | sc.ChangeState(SCE_FS_COMMENTDOCKEYWORDERROR); | |
161 | } | |
162 | sc.SetState(styleBeforeDCKeyword); | |
163 | } | |
164 | break; | |
165 | case SCE_FS_PREPROCESSOR: | |
166 | case SCE_FS_PREPROCESSOR_C: | |
167 | if (sc.atLineEnd) { | |
168 | if (!(sc.chPrev == ';' || sc.GetRelative(-2) == ';')) { | |
169 | sc.SetState(bEnableCode ? SCE_FS_DEFAULT : SCE_FS_DEFAULT_C); | |
170 | } | |
171 | } else if (stylingWithinPreprocessor) { | |
172 | if (IsASpaceOrTab(sc.ch)) { | |
173 | sc.SetState(bEnableCode ? SCE_FS_DEFAULT : SCE_FS_DEFAULT_C); | |
174 | } | |
175 | } else if (sc.Match('/', '*') || sc.Match('/', '/') || sc.Match('&', '&')) { | |
176 | sc.SetState(bEnableCode ? SCE_FS_DEFAULT : SCE_FS_DEFAULT_C); | |
177 | } | |
178 | break; | |
179 | case SCE_FS_DISABLEDCODE: | |
180 | if (sc.ch == '#' && visibleChars == 0) { | |
181 | sc.SetState(bEnableCode ? SCE_FS_PREPROCESSOR : SCE_FS_PREPROCESSOR_C); | |
182 | do { // Skip whitespace between # and preprocessor word | |
183 | sc.Forward(); | |
184 | } while (IsASpaceOrTab(sc.ch) && sc.More()); | |
185 | if (sc.MatchIgnoreCase("pragma")) { | |
186 | sc.Forward(6); | |
187 | do { // Skip more whitespace until keyword | |
188 | sc.Forward(); | |
189 | } while (IsASpaceOrTab(sc.ch) && sc.More()); | |
190 | if (sc.MatchIgnoreCase("enddump") || sc.MatchIgnoreCase("__endtext")) { | |
191 | bEnableCode = true; | |
192 | sc.SetState(SCE_FS_DISABLEDCODE); | |
193 | sc.Forward(sc.ch == '_' ? 8 : 6); | |
194 | sc.ForwardSetState(SCE_FS_DEFAULT); | |
195 | } else { | |
196 | sc.ChangeState(SCE_FS_DISABLEDCODE); | |
197 | } | |
198 | } else { | |
199 | sc.ChangeState(SCE_FS_DISABLEDCODE); | |
200 | } | |
201 | } | |
202 | break; | |
203 | case SCE_FS_DATE: | |
204 | if (sc.ch == '}') { | |
205 | sc.ForwardSetState(SCE_FS_DEFAULT); | |
206 | } else if (sc.atLineEnd) { | |
207 | sc.ChangeState(SCE_FS_STRINGEOL); | |
208 | } | |
209 | } | |
210 | ||
211 | // Determine if a new state should be entered. | |
212 | if (sc.state == SCE_FS_DEFAULT || sc.state == SCE_FS_DEFAULT_C) { | |
213 | if (bEnableCode && | |
214 | (sc.MatchIgnoreCase(".and.") || sc.MatchIgnoreCase(".not."))) { | |
215 | sc.SetState(SCE_FS_WORDOPERATOR); | |
216 | sc.Forward(4); | |
217 | } else if (bEnableCode && sc.MatchIgnoreCase(".or.")) { | |
218 | sc.SetState(SCE_FS_WORDOPERATOR); | |
219 | sc.Forward(3); | |
220 | } else if (bEnableCode && | |
221 | (sc.MatchIgnoreCase(".t.") || sc.MatchIgnoreCase(".f.") || | |
222 | (!IsAWordChar(sc.GetRelative(3)) && sc.MatchIgnoreCase("nil")))) { | |
223 | sc.SetState(SCE_FS_CONSTANT); | |
224 | sc.Forward(2); | |
225 | } else if (sc.Match('/', '*')) { | |
226 | sc.SetState(bEnableCode ? SCE_FS_COMMENTDOC : SCE_FS_COMMENTDOC_C); | |
227 | sc.Forward(); | |
228 | } else if (bEnableCode && sc.Match('&', '&')) { | |
229 | sc.SetState(SCE_FS_COMMENTLINE); | |
230 | sc.Forward(); | |
231 | } else if (sc.Match('/', '/')) { | |
232 | sc.SetState(bEnableCode ? SCE_FS_COMMENTLINEDOC : SCE_FS_COMMENTLINEDOC_C); | |
233 | sc.Forward(); | |
234 | } else if (bEnableCode && sc.ch == '*' && visibleChars == 0) { | |
235 | sc.SetState(SCE_FS_COMMENT); | |
236 | } else if (sc.ch == '\"' || sc.ch == '\'') { | |
237 | sc.SetState(bEnableCode ? SCE_FS_STRING : SCE_FS_STRING_C); | |
238 | closeStringChar = sc.ch; | |
239 | } else if (closeStringChar == '>' && sc.ch == '<') { | |
240 | sc.SetState(bEnableCode ? SCE_FS_STRING : SCE_FS_STRING_C); | |
241 | } else if (sc.ch == '#' && visibleChars == 0) { | |
242 | sc.SetState(bEnableCode ? SCE_FS_PREPROCESSOR : SCE_FS_PREPROCESSOR_C); | |
243 | do { // Skip whitespace between # and preprocessor word | |
244 | sc.Forward(); | |
245 | } while (IsASpaceOrTab(sc.ch) && sc.More()); | |
246 | if (sc.atLineEnd) { | |
247 | sc.SetState(bEnableCode ? SCE_FS_DEFAULT : SCE_FS_DEFAULT_C); | |
248 | } else if (sc.MatchIgnoreCase("include")) { | |
249 | if (stylingWithinPreprocessor) { | |
250 | closeStringChar = '>'; | |
251 | } | |
252 | } else if (sc.MatchIgnoreCase("pragma")) { | |
253 | sc.Forward(6); | |
254 | do { // Skip more whitespace until keyword | |
255 | sc.Forward(); | |
256 | } while (IsASpaceOrTab(sc.ch) && sc.More()); | |
257 | if (sc.MatchIgnoreCase("begindump") || sc.MatchIgnoreCase("__cstream")) { | |
258 | bEnableCode = false; | |
259 | if (stylingWithinPreprocessor) { | |
260 | sc.SetState(SCE_FS_DISABLEDCODE); | |
261 | sc.Forward(8); | |
262 | sc.ForwardSetState(SCE_FS_DEFAULT_C); | |
263 | } else { | |
264 | sc.SetState(SCE_FS_DISABLEDCODE); | |
265 | } | |
266 | } else if (sc.MatchIgnoreCase("enddump") || sc.MatchIgnoreCase("__endtext")) { | |
267 | bEnableCode = true; | |
268 | sc.SetState(SCE_FS_DISABLEDCODE); | |
269 | sc.Forward(sc.ch == '_' ? 8 : 6); | |
270 | sc.ForwardSetState(SCE_FS_DEFAULT); | |
271 | } | |
272 | } | |
273 | } else if (bEnableCode && sc.ch == '{') { | |
274 | int p = 0; | |
275 | int chSeek; | |
276 | unsigned int endPos(startPos + length); | |
277 | do { // Skip whitespace | |
278 | chSeek = sc.GetRelative(++p); | |
279 | } while (IsASpaceOrTab(chSeek) && (sc.currentPos + p < endPos)); | |
280 | if (chSeek == '^') { | |
281 | sc.SetState(SCE_FS_DATE); | |
282 | } else { | |
283 | sc.SetState(SCE_FS_OPERATOR); | |
284 | } | |
285 | } else if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) { | |
286 | sc.SetState(bEnableCode ? SCE_FS_NUMBER : SCE_FS_NUMBER_C); | |
287 | } else if (IsAWordChar(sc.ch)) { | |
288 | sc.SetState(bEnableCode ? SCE_FS_IDENTIFIER : SCE_FS_IDENTIFIER_C); | |
289 | } else if (isoperator(static_cast<char>(sc.ch)) || (bEnableCode && sc.ch == '@')) { | |
290 | sc.SetState(bEnableCode ? SCE_FS_OPERATOR : SCE_FS_OPERATOR_C); | |
291 | } | |
292 | } | |
293 | ||
294 | if (sc.atLineEnd) { | |
295 | visibleChars = 0; | |
296 | closeStringChar = 0; | |
297 | } | |
298 | if (!IsASpace(sc.ch)) { | |
299 | visibleChars++; | |
300 | } | |
301 | } | |
302 | sc.Complete(); | |
303 | } | |
304 | ||
305 | static void FoldFlagShipDoc(unsigned int startPos, int length, int, | |
306 | WordList *[], Accessor &styler) | |
307 | { | |
308 | ||
309 | int endPos = startPos + length; | |
310 | ||
311 | // Backtrack to previous line in case need to fix its fold status | |
312 | int lineCurrent = styler.GetLine(startPos); | |
313 | if (startPos > 0 && lineCurrent > 0) { | |
314 | lineCurrent--; | |
315 | startPos = styler.LineStart(lineCurrent); | |
316 | } | |
317 | int spaceFlags = 0; | |
318 | int indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags); | |
319 | char chNext = styler[startPos]; | |
320 | for (int i = startPos; i < endPos; i++) { | |
321 | char ch = chNext; | |
322 | chNext = styler.SafeGetCharAt(i + 1); | |
323 | ||
324 | if ((ch == '\r' && chNext != '\n') || (ch == '\n') || (i == endPos-1)) { | |
325 | int lev = indentCurrent; | |
326 | int indentNext = styler.IndentAmount(lineCurrent + 1, &spaceFlags); | |
327 | if (!(indentCurrent & SC_FOLDLEVELWHITEFLAG)) { | |
328 | if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext & SC_FOLDLEVELNUMBERMASK)) { | |
329 | lev |= SC_FOLDLEVELHEADERFLAG; | |
330 | } else if (indentNext & SC_FOLDLEVELWHITEFLAG) { | |
331 | int spaceFlags2 = 0; | |
332 | int indentNext2 = styler.IndentAmount(lineCurrent + 2, &spaceFlags2); | |
333 | if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext2 & SC_FOLDLEVELNUMBERMASK)) { | |
334 | lev |= SC_FOLDLEVELHEADERFLAG; | |
335 | } | |
336 | } | |
337 | } | |
338 | indentCurrent = indentNext; | |
339 | styler.SetLevel(lineCurrent, lev); | |
340 | lineCurrent++; | |
341 | } | |
342 | } | |
343 | } | |
344 | ||
345 | static const char * const FSWordListDesc[] = { | |
346 | "Keywords Commands", | |
347 | "Std Library Functions", | |
348 | "Procedure, return, exit", | |
349 | "Class (oop)", | |
350 | "Doxygen keywords", | |
351 | 0 | |
352 | }; | |
353 | ||
354 | LexerModule lmFlagShip(SCLEX_FLAGSHIP, ColouriseFlagShipDoc, "flagship", FoldFlagShipDoc, FSWordListDesc); |