]>
Commit | Line | Data |
---|---|---|
9e96e16f RD |
1 | // Scintilla source code edit control |
2 | // @file LexPowerPro.cxx | |
3 | // PowerPro utility, written by Bruce Switzer, is available from http://powerpro.webeddie.com | |
4 | // PowerPro lexer is written by Christopher Bean (cbean@cb-software.net) | |
5 | // | |
6 | // Lexer code heavily borrowed from: | |
7 | // LexAU3.cxx by Jos van der Zande | |
8 | // LexCPP.cxx by Neil Hodgson | |
9 | // LexVB.cxx by Neil Hodgson | |
10 | // | |
11 | // Changes: | |
12 | // 2008-10-25 - Initial release | |
13 | // 2008-10-26 - Changed how <name> is hilighted in 'function <name>' so that | |
14 | // local isFunction = "" and local functions = "" don't get falsely highlighted | |
15 | // 2008-12-14 - Added bounds checking for szKeyword and szDo | |
16 | // - Replaced SetOfCharacters with CharacterSet | |
17 | // - Made sure that CharacterSet::Contains is passed only positive values | |
18 | // - Made sure that the return value of Accessor::SafeGetCharAt is positive before | |
19 | // passsing to functions that require positive values like isspacechar() | |
20 | // - Removed unused visibleChars processing from ColourisePowerProDoc() | |
21 | // - Fixed bug with folding logic where line continuations didn't end where | |
22 | // they were supposed to | |
23 | // - Moved all helper functions to the top of the file | |
24 | // | |
25 | // Copyright 1998-2005 by Neil Hodgson <neilh@scintilla.org> | |
26 | // The License.txt file describes the conditions under which this software may be distributed. | |
27 | ||
28 | #include <ctype.h> | |
29 | #include <stdarg.h> | |
30 | #include <stdio.h> | |
31 | #include <stdlib.h> | |
32 | #include <string.h> | |
33 | ||
34 | #include "Platform.h" | |
35 | #include "PropSet.h" | |
36 | #include "Accessor.h" | |
37 | #include "StyleContext.h" | |
38 | #include "KeyWords.h" | |
39 | #include "Scintilla.h" | |
40 | #include "SciLexer.h" | |
41 | #include "CharacterSet.h" | |
42 | ||
43 | #ifdef SCI_NAMESPACE | |
44 | using namespace Scintilla; | |
45 | #endif | |
46 | ||
47 | static inline bool IsStreamCommentStyle(int style) { | |
48 | return style == SCE_POWERPRO_COMMENTBLOCK; | |
49 | } | |
50 | ||
51 | static bool IsContinuationLine(unsigned int szLine, Accessor &styler) | |
52 | { | |
53 | int nsPos = styler.LineStart(szLine); | |
54 | int nePos = styler.LineStart(szLine + 1) - 2; | |
55 | while (nsPos < nePos) | |
56 | { | |
57 | int stylech = styler.StyleAt(nsPos); | |
58 | if (!(stylech == SCE_POWERPRO_COMMENTBLOCK)) { | |
59 | char ch = styler.SafeGetCharAt(nePos); | |
60 | char chPrev = styler.SafeGetCharAt(nePos-1); | |
61 | char chPrevPrev = styler.SafeGetCharAt(nePos-2); | |
62 | if (ch > 0 && chPrev > 0 && chPrevPrev > 0 && !isspacechar(ch) && !isspacechar(chPrev) && !isspacechar(chPrevPrev) ) { | |
63 | if (chPrevPrev == ';' && chPrev == ';' && ch == '+') | |
64 | return true; | |
65 | else | |
66 | return false; | |
67 | } | |
68 | } | |
69 | nePos--; // skip to next char | |
70 | } | |
71 | return false; | |
72 | } | |
73 | ||
74 | // Routine to find first none space on the current line and return its Style | |
75 | // needed for comment lines not starting on pos 1 | |
76 | static int GetStyleFirstWord(unsigned int szLine, Accessor &styler) | |
77 | { | |
78 | int nsPos = styler.LineStart(szLine); | |
79 | int nePos = styler.LineStart(szLine+1) - 1; | |
80 | char ch = styler.SafeGetCharAt(nsPos); | |
81 | ||
82 | while (ch > 0 && isspacechar(ch) && nsPos < nePos) | |
83 | { | |
84 | nsPos++; // skip to next char | |
85 | ch = styler.SafeGetCharAt(nsPos); | |
86 | ||
87 | } | |
88 | return styler.StyleAt(nsPos); | |
89 | } | |
90 | ||
91 | //returns true if there is a function to highlight | |
92 | //used to highlight <name> in 'function <name>' | |
93 | static bool HasFunction(Accessor &styler, unsigned int currentPos) { | |
94 | ||
95 | //check for presence of 'function ' | |
96 | return (styler.SafeGetCharAt(currentPos) == ' ' | |
97 | && tolower(styler.SafeGetCharAt(currentPos-1)) == 'n' | |
98 | && tolower(styler.SafeGetCharAt(currentPos-2)) == 'o' | |
99 | && tolower(styler.SafeGetCharAt(currentPos-3)) == 'i' | |
100 | && tolower(styler.SafeGetCharAt(currentPos-4)) == 't' | |
101 | && tolower(styler.SafeGetCharAt(currentPos-5)) == 'c' | |
102 | && tolower(styler.SafeGetCharAt(currentPos-6)) == 'n' | |
103 | && tolower(styler.SafeGetCharAt(currentPos-7)) == 'u' | |
104 | && tolower(styler.SafeGetCharAt(currentPos-8)) == 'f' | |
105 | //only allow 'function ' to appear at the beginning of a line | |
106 | && (styler.SafeGetCharAt(currentPos-9) == '\n' | |
107 | || styler.SafeGetCharAt(currentPos-9) == '\r' | |
108 | || (styler.SafeGetCharAt(currentPos -9, '\0')) == '\0') //is the first line | |
109 | ); | |
110 | } | |
111 | ||
112 | static void ColourisePowerProDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[], | |
113 | Accessor &styler, bool caseSensitive) { | |
114 | ||
115 | WordList &keywords = *keywordlists[0]; | |
116 | WordList &keywords2 = *keywordlists[1]; | |
117 | WordList &keywords3 = *keywordlists[2]; | |
118 | WordList &keywords4 = *keywordlists[3]; | |
119 | ||
120 | //define the character sets | |
121 | CharacterSet setWordStart(CharacterSet::setAlpha, "_@", 0x80, true); | |
122 | CharacterSet setWord(CharacterSet::setAlphaNum, "._", 0x80, true); | |
123 | ||
124 | StyleContext sc(startPos, length, initStyle, styler); | |
125 | char s_save[100]; //for last line highlighting | |
126 | ||
127 | for (; sc.More(); sc.Forward()) { | |
128 | ||
129 | // ********************************************** | |
130 | // save the total current word for eof processing | |
131 | char s[100]; | |
132 | sc.GetCurrentLowered(s, sizeof(s)); | |
133 | ||
134 | if ((sc.ch > 0) && setWord.Contains(sc.ch)) | |
135 | { | |
136 | strcpy(s_save,s); | |
137 | int tp = strlen(s_save); | |
138 | if (tp < 99) { | |
139 | s_save[tp] = static_cast<char>(tolower(sc.ch)); | |
140 | s_save[tp+1] = '\0'; | |
141 | } | |
142 | } | |
143 | // ********************************************** | |
144 | // | |
145 | ||
146 | if (sc.atLineStart) { | |
147 | if (sc.state == SCE_POWERPRO_DOUBLEQUOTEDSTRING) { | |
148 | // Prevent SCE_POWERPRO_STRINGEOL from leaking back to previous line which | |
149 | // ends with a line continuation by locking in the state upto this position. | |
150 | sc.SetState(SCE_POWERPRO_DOUBLEQUOTEDSTRING); | |
151 | } | |
152 | } | |
153 | ||
154 | // Determine if the current state should terminate. | |
155 | switch (sc.state) { | |
156 | case SCE_POWERPRO_OPERATOR: | |
157 | sc.SetState(SCE_POWERPRO_DEFAULT); | |
158 | break; | |
159 | ||
160 | case SCE_POWERPRO_NUMBER: | |
161 | ||
162 | if (!IsADigit(sc.ch)) | |
163 | sc.SetState(SCE_POWERPRO_DEFAULT); | |
164 | ||
165 | break; | |
166 | ||
167 | case SCE_POWERPRO_IDENTIFIER: | |
168 | //if ((sc.ch > 0) && !setWord.Contains(sc.ch) || (sc.ch == '.')) { // use this line if don't want to match keywords with . in them. ie: win.debug will match both win and debug so win debug will also be colorized | |
169 | if ((sc.ch > 0) && !setWord.Contains(sc.ch)){ // || (sc.ch == '.')) { // use this line if you want to match keywords with a . ie: win.debug will only match win.debug neither win nor debug will be colorized separately | |
170 | char s[1000]; | |
171 | if (caseSensitive) { | |
172 | sc.GetCurrent(s, sizeof(s)); | |
173 | } else { | |
174 | sc.GetCurrentLowered(s, sizeof(s)); | |
175 | } | |
176 | if (keywords.InList(s)) { | |
177 | sc.ChangeState(SCE_POWERPRO_WORD); | |
178 | } else if (keywords2.InList(s)) { | |
179 | sc.ChangeState(SCE_POWERPRO_WORD2); | |
180 | } else if (keywords3.InList(s)) { | |
181 | sc.ChangeState(SCE_POWERPRO_WORD3); | |
182 | } else if (keywords4.InList(s)) { | |
183 | sc.ChangeState(SCE_POWERPRO_WORD4); | |
184 | } | |
185 | sc.SetState(SCE_POWERPRO_DEFAULT); | |
186 | } | |
187 | break; | |
188 | ||
189 | case SCE_POWERPRO_LINECONTINUE: | |
190 | if (sc.atLineStart) { | |
191 | sc.SetState(SCE_POWERPRO_DEFAULT); | |
192 | } else if (sc.Match('/', '*') || sc.Match('/', '/')) { | |
193 | sc.SetState(SCE_POWERPRO_DEFAULT); | |
194 | } | |
195 | break; | |
196 | ||
197 | case SCE_POWERPRO_COMMENTBLOCK: | |
198 | if (sc.Match('*', '/')) { | |
199 | sc.Forward(); | |
200 | sc.ForwardSetState(SCE_POWERPRO_DEFAULT); | |
201 | } | |
202 | break; | |
203 | ||
204 | case SCE_POWERPRO_COMMENTLINE: | |
205 | if (sc.atLineStart) { | |
206 | sc.SetState(SCE_POWERPRO_DEFAULT); | |
207 | } | |
208 | break; | |
209 | ||
210 | case SCE_POWERPRO_DOUBLEQUOTEDSTRING: | |
211 | if (sc.atLineEnd) { | |
212 | sc.ChangeState(SCE_POWERPRO_STRINGEOL); | |
213 | } else if (sc.ch == '\\') { | |
214 | if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') { | |
215 | sc.Forward(); | |
216 | } | |
217 | } else if (sc.ch == '\"') { | |
218 | sc.ForwardSetState(SCE_POWERPRO_DEFAULT); | |
219 | } | |
220 | break; | |
221 | ||
222 | case SCE_POWERPRO_SINGLEQUOTEDSTRING: | |
223 | if (sc.atLineEnd) { | |
224 | sc.ChangeState(SCE_POWERPRO_STRINGEOL); | |
225 | } else if (sc.ch == '\\') { | |
226 | if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') { | |
227 | sc.Forward(); | |
228 | } | |
229 | } else if (sc.ch == '\'') { | |
230 | sc.ForwardSetState(SCE_POWERPRO_DEFAULT); | |
231 | } | |
232 | break; | |
233 | ||
234 | case SCE_POWERPRO_STRINGEOL: | |
235 | if (sc.atLineStart) { | |
236 | sc.SetState(SCE_POWERPRO_DEFAULT); | |
237 | } | |
238 | break; | |
239 | ||
240 | case SCE_POWERPRO_VERBATIM: | |
241 | if (sc.ch == '\"') { | |
242 | if (sc.chNext == '\"') { | |
243 | sc.Forward(); | |
244 | } else { | |
245 | sc.ForwardSetState(SCE_POWERPRO_DEFAULT); | |
246 | } | |
247 | } | |
248 | break; | |
249 | ||
250 | case SCE_POWERPRO_ALTQUOTE: | |
251 | if (sc.ch == '#') { | |
252 | if (sc.chNext == '#') { | |
253 | sc.Forward(); | |
254 | } else { | |
255 | sc.ForwardSetState(SCE_POWERPRO_DEFAULT); | |
256 | } | |
257 | } | |
258 | break; | |
259 | ||
260 | case SCE_POWERPRO_FUNCTION: | |
261 | if (sc.ch == '\r' || sc.ch == '\n' || sc.ch == ' ' || sc.ch == '(') { | |
262 | sc.SetState(SCE_POWERPRO_DEFAULT); | |
263 | } | |
264 | break; | |
265 | } | |
266 | ||
267 | // Determine if a new state should be entered. | |
268 | if (sc.state == SCE_POWERPRO_DEFAULT) { | |
269 | if (sc.Match('?', '\"')) { | |
270 | sc.SetState(SCE_POWERPRO_VERBATIM); | |
271 | sc.Forward(); | |
272 | } else if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) { | |
273 | sc.SetState(SCE_POWERPRO_NUMBER); | |
274 | }else if (sc.Match('?','#')) { | |
275 | if (sc.ch == '?' && sc.chNext == '#') { | |
276 | sc.SetState(SCE_POWERPRO_ALTQUOTE); | |
277 | sc.Forward(); | |
278 | } | |
279 | } else if (HasFunction(styler, sc.currentPos)) { //highlight <name> in 'function <name>' | |
280 | sc.SetState(SCE_POWERPRO_FUNCTION); | |
281 | } else if (sc.ch == '@' && sc.atLineStart) { //alternate function definition [label] | |
282 | sc.SetState(SCE_POWERPRO_FUNCTION); | |
283 | } else if ((sc.ch > 0) && (setWordStart.Contains(sc.ch) || (sc.ch == '?'))) { | |
284 | sc.SetState(SCE_POWERPRO_IDENTIFIER); | |
285 | } else if (sc.Match(";;+")) { | |
286 | sc.SetState(SCE_POWERPRO_LINECONTINUE); | |
287 | } else if (sc.Match('/', '*')) { | |
288 | sc.SetState(SCE_POWERPRO_COMMENTBLOCK); | |
289 | sc.Forward(); // Eat the * so it isn't used for the end of the comment | |
290 | } else if (sc.Match('/', '/')) { | |
291 | sc.SetState(SCE_POWERPRO_COMMENTLINE); | |
292 | } else if (sc.atLineStart && sc.ch == ';') { //legacy comment that can only appear at the beginning of a line | |
293 | sc.SetState(SCE_POWERPRO_COMMENTLINE); | |
294 | } else if (sc.Match(";;")) { | |
295 | sc.SetState(SCE_POWERPRO_COMMENTLINE); | |
296 | } else if (sc.ch == '\"') { | |
297 | sc.SetState(SCE_POWERPRO_DOUBLEQUOTEDSTRING); | |
298 | } else if (sc.ch == '\'') { | |
299 | sc.SetState(SCE_POWERPRO_SINGLEQUOTEDSTRING); | |
300 | } else if (isoperator(static_cast<char>(sc.ch))) { | |
301 | sc.SetState(SCE_POWERPRO_OPERATOR); | |
302 | } | |
303 | } | |
304 | } | |
305 | ||
306 | //************************************* | |
307 | // Colourize the last word correctly | |
308 | //************************************* | |
309 | if (sc.state == SCE_POWERPRO_IDENTIFIER) | |
310 | { | |
311 | if (keywords.InList(s_save)) { | |
312 | sc.ChangeState(SCE_POWERPRO_WORD); | |
313 | sc.SetState(SCE_POWERPRO_DEFAULT); | |
314 | } | |
315 | else if (keywords2.InList(s_save)) { | |
316 | sc.ChangeState(SCE_POWERPRO_WORD2); | |
317 | sc.SetState(SCE_POWERPRO_DEFAULT); | |
318 | } | |
319 | else if (keywords3.InList(s_save)) { | |
320 | sc.ChangeState(SCE_POWERPRO_WORD3); | |
321 | sc.SetState(SCE_POWERPRO_DEFAULT); | |
322 | } | |
323 | else if (keywords4.InList(s_save)) { | |
324 | sc.ChangeState(SCE_POWERPRO_WORD4); | |
325 | sc.SetState(SCE_POWERPRO_DEFAULT); | |
326 | } | |
327 | else { | |
328 | sc.SetState(SCE_POWERPRO_DEFAULT); | |
329 | } | |
330 | } | |
331 | sc.Complete(); | |
332 | } | |
333 | ||
334 | static void FoldPowerProDoc(unsigned int startPos, int length, int, WordList *[], Accessor &styler) | |
335 | { | |
336 | //define the character sets | |
337 | CharacterSet setWordStart(CharacterSet::setAlpha, "_@", 0x80, true); | |
338 | CharacterSet setWord(CharacterSet::setAlphaNum, "._", 0x80, true); | |
339 | ||
340 | bool isFoldingAll = true; //used to tell if we're recursively folding the whole document, or just a small piece (ie: if statement or 1 function) | |
341 | int endPos = startPos + length; | |
342 | int lastLine = styler.GetLine(styler.Length()); //used to help fold the last line correctly | |
343 | ||
344 | // get settings from the config files for folding comments and preprocessor lines | |
345 | bool foldComment = styler.GetPropertyInt("fold.comment") != 0; | |
346 | bool foldInComment = styler.GetPropertyInt("fold.comment") == 2; | |
347 | bool foldCompact = true; | |
348 | ||
349 | // Backtrack to previous line in case need to fix its fold status | |
350 | int lineCurrent = styler.GetLine(startPos); | |
351 | if (startPos > 0) { | |
352 | isFoldingAll = false; | |
353 | if (lineCurrent > 0) { | |
354 | lineCurrent--; | |
355 | startPos = styler.LineStart(lineCurrent); | |
356 | } | |
357 | } | |
358 | // vars for style of previous/current/next lines | |
359 | int style = GetStyleFirstWord(lineCurrent,styler); | |
360 | int stylePrev = 0; | |
361 | ||
362 | // find the first previous line without continuation character at the end | |
363 | while ((lineCurrent > 0 && IsContinuationLine(lineCurrent,styler)) || | |
364 | (lineCurrent > 1 && IsContinuationLine(lineCurrent-1,styler))) { | |
365 | lineCurrent--; | |
366 | startPos = styler.LineStart(lineCurrent); | |
367 | } | |
368 | if (lineCurrent > 0) { | |
369 | stylePrev = GetStyleFirstWord(lineCurrent-1,styler); | |
370 | } | |
371 | // vars for getting first word to check for keywords | |
372 | bool FirstWordStart = false; | |
373 | bool FirstWordEnd = false; | |
374 | ||
375 | const unsigned int KEYWORD_MAX = 10; | |
376 | char szKeyword[KEYWORD_MAX]=""; | |
377 | unsigned int szKeywordlen = 0; | |
378 | ||
379 | char szDo[3]=""; | |
380 | int szDolen = 0; | |
381 | bool DoFoundLast = false; | |
382 | ||
383 | // var for indentlevel | |
384 | int levelCurrent = SC_FOLDLEVELBASE; | |
385 | if (lineCurrent > 0) { | |
386 | levelCurrent = styler.LevelAt(lineCurrent-1) >> 16; | |
387 | } | |
388 | int levelNext = levelCurrent; | |
389 | ||
390 | int visibleChars = 0; | |
391 | int functionCount = 0; | |
392 | ||
393 | char chNext = styler.SafeGetCharAt(startPos); | |
394 | char chPrev = '\0'; | |
395 | char chPrevPrev = '\0'; | |
396 | char chPrevPrevPrev = '\0'; | |
397 | ||
398 | for (int i = startPos; i < endPos; i++) { | |
399 | ||
400 | char ch = chNext; | |
401 | chNext = styler.SafeGetCharAt(i + 1); | |
402 | ||
403 | if ((ch > 0) && setWord.Contains(ch)) { | |
404 | visibleChars++; | |
405 | } | |
406 | ||
407 | // get the syle for the current character neede to check in comment | |
408 | int stylech = styler.StyleAt(i); | |
409 | ||
410 | // get first word for the line for indent check max 9 characters | |
411 | if (FirstWordStart && (!(FirstWordEnd))) { | |
412 | if ((ch > 0) && !setWord.Contains(ch)) { | |
413 | FirstWordEnd = true; | |
414 | } | |
415 | else if (szKeywordlen < KEYWORD_MAX - 1) { | |
416 | szKeyword[szKeywordlen++] = static_cast<char>(tolower(ch)); | |
417 | szKeyword[szKeywordlen] = '\0'; | |
418 | } | |
419 | } | |
420 | ||
421 | // start the capture of the first word | |
422 | if (!(FirstWordStart)) { | |
423 | if ((ch > 0) && (setWord.Contains(ch) || setWordStart.Contains(ch) || ch == ';' || ch == '/')) { | |
424 | FirstWordStart = true; | |
425 | if (szKeywordlen < KEYWORD_MAX - 1) { | |
426 | szKeyword[szKeywordlen++] = static_cast<char>(tolower(ch)); | |
427 | szKeyword[szKeywordlen] = '\0'; | |
428 | } | |
429 | } | |
430 | } | |
431 | // only process this logic when not in comment section | |
432 | if (stylech != SCE_POWERPRO_COMMENTLINE) { | |
433 | if (DoFoundLast) { | |
434 | if (DoFoundLast && (ch > 0) && setWord.Contains(ch)) { | |
435 | DoFoundLast = false; | |
436 | } | |
437 | } | |
438 | // find out if the word "do" is the last on a "if" line | |
439 | if (FirstWordEnd && strcmp(szKeyword,"if") == 0) { | |
440 | if (szDolen == 2) { | |
441 | szDo[0] = szDo[1]; | |
442 | szDo[1] = static_cast<char>(tolower(ch)); | |
443 | szDo[2] = '\0'; | |
444 | if (strcmp(szDo,"do") == 0 ) { | |
445 | DoFoundLast = true; | |
446 | } | |
447 | } | |
448 | else if (szDolen < 2) { | |
449 | szDo[szDolen++] = static_cast<char>(tolower(ch)); | |
450 | szDo[szDolen] = '\0'; | |
451 | } | |
452 | } | |
453 | } | |
454 | ||
455 | // End of Line found so process the information | |
456 | if ((ch == '\r' && chNext != '\n') || (ch == '\n') || (i == endPos)) { | |
457 | ||
458 | // ************************** | |
459 | // Folding logic for Keywords | |
460 | // ************************** | |
461 | ||
462 | // if a keyword is found on the current line and the line doesn't end with ;;+ (continuation) | |
463 | // and we are not inside a commentblock. | |
464 | if (szKeywordlen > 0 && | |
465 | (!(chPrev == '+' && chPrevPrev == ';' && chPrevPrevPrev ==';')) && | |
466 | ((!(IsStreamCommentStyle(style)) || foldInComment)) ) { | |
467 | ||
468 | // only fold "if" last keyword is "then" (else its a one line if) | |
469 | if (strcmp(szKeyword,"if") == 0 && DoFoundLast) { | |
470 | levelNext++; | |
471 | } | |
472 | // create new fold for these words | |
473 | if (strcmp(szKeyword,"for") == 0) { | |
474 | levelNext++; | |
475 | } | |
476 | ||
477 | //handle folding for functions/labels | |
478 | //Note: Functions and labels don't have an explicit end like [end function] | |
479 | // 1. functions/labels end at the start of another function | |
480 | // 2. functions/labels end at the end of the file | |
481 | if ((strcmp(szKeyword,"function") == 0) || (szKeywordlen > 0 && szKeyword[0] == '@')) { | |
482 | if (isFoldingAll) { //if we're folding the whole document (recursivly by lua script) | |
483 | ||
484 | if (functionCount > 0) { | |
485 | levelCurrent--; | |
486 | } else { | |
487 | levelNext++; | |
488 | } | |
489 | functionCount++; | |
490 | ||
491 | } else { //if just folding a small piece (by clicking on the minus sign next to the word) | |
492 | levelCurrent--; | |
493 | } | |
494 | } | |
495 | ||
496 | // end the fold for these words before the current line | |
497 | if (strcmp(szKeyword,"endif") == 0 || strcmp(szKeyword,"endfor") == 0) { | |
498 | levelNext--; | |
499 | levelCurrent--; | |
500 | } | |
501 | // end the fold for these words before the current line and Start new fold | |
502 | if (strcmp(szKeyword,"else") == 0 || strcmp(szKeyword,"elseif") == 0 ) { | |
503 | levelCurrent--; | |
504 | } | |
505 | } | |
506 | // Preprocessor and Comment folding | |
507 | int styleNext = GetStyleFirstWord(lineCurrent + 1,styler); | |
508 | ||
509 | // ********************************* | |
510 | // Folding logic for Comment blocks | |
511 | // ********************************* | |
512 | if (foldComment && IsStreamCommentStyle(style)) { | |
513 | // Start of a comment block | |
514 | if (!(stylePrev==style) && IsStreamCommentStyle(styleNext) && styleNext==style) { | |
515 | levelNext++; | |
516 | } | |
517 | // fold till the last line for normal comment lines | |
518 | else if (IsStreamCommentStyle(stylePrev) | |
519 | && !(styleNext == SCE_POWERPRO_COMMENTLINE) | |
520 | && stylePrev == SCE_POWERPRO_COMMENTLINE | |
521 | && style == SCE_POWERPRO_COMMENTLINE) { | |
522 | levelNext--; | |
523 | } | |
524 | // fold till the one but last line for Blockcomment lines | |
525 | else if (IsStreamCommentStyle(stylePrev) | |
526 | && !(styleNext == SCE_POWERPRO_COMMENTBLOCK) | |
527 | && style == SCE_POWERPRO_COMMENTBLOCK) { | |
528 | levelNext--; | |
529 | levelCurrent--; | |
530 | } | |
531 | } | |
532 | ||
533 | int levelUse = levelCurrent; | |
534 | int lev = levelUse | levelNext << 16; | |
535 | if (visibleChars == 0 && foldCompact) | |
536 | lev |= SC_FOLDLEVELWHITEFLAG; | |
537 | if (levelUse < levelNext) { | |
538 | lev |= SC_FOLDLEVELHEADERFLAG; | |
539 | } | |
540 | if (lev != styler.LevelAt(lineCurrent)) { | |
541 | styler.SetLevel(lineCurrent, lev); | |
542 | } | |
543 | ||
544 | // reset values for the next line | |
545 | lineCurrent++; | |
546 | stylePrev = style; | |
547 | style = styleNext; | |
548 | levelCurrent = levelNext; | |
549 | visibleChars = 0; | |
550 | ||
551 | // if the last characters are ;;+ then don't reset since the line continues on the next line. | |
552 | if (chPrev == '+' && chPrevPrev == ';' && chPrevPrevPrev == ';') { | |
553 | //do nothing | |
554 | } else { | |
555 | szKeywordlen = 0; | |
556 | szDolen = 0; | |
557 | FirstWordStart = false; | |
558 | FirstWordEnd = false; | |
559 | DoFoundLast = false; | |
560 | //blank out keyword | |
561 | for (unsigned int i = 0; i < KEYWORD_MAX; i++) { | |
562 | szKeyword[i] = '\0'; | |
563 | } | |
564 | } | |
565 | } | |
566 | ||
567 | // save the last processed characters | |
568 | if ((ch > 0) && !isspacechar(ch)) { | |
569 | chPrevPrevPrev = chPrevPrev; | |
570 | chPrevPrev = chPrev; | |
571 | chPrev = ch; | |
572 | visibleChars++; | |
573 | } | |
574 | } | |
575 | ||
576 | //close folds on the last line - without this a 'phantom' | |
577 | //fold can appear when an open fold is on the last line | |
578 | //this can occur because functions and labels don't have an explicit end | |
579 | if (lineCurrent >= lastLine) { | |
580 | int lev = 0; | |
581 | lev |= SC_FOLDLEVELWHITEFLAG; | |
582 | styler.SetLevel(lineCurrent, lev); | |
583 | } | |
584 | ||
585 | } | |
586 | ||
587 | static const char * const powerProWordLists[] = { | |
588 | "Keyword list 1", | |
589 | "Keyword list 2", | |
590 | "Keyword list 3", | |
591 | "Keyword list 4", | |
592 | 0, | |
593 | }; | |
594 | ||
595 | static void ColourisePowerProDocWrapper(unsigned int startPos, int length, int initStyle, WordList *keywordlists[], | |
596 | Accessor &styler) { | |
597 | ColourisePowerProDoc(startPos, length, initStyle, keywordlists, styler, false); | |
598 | } | |
599 | ||
600 | LexerModule lmPowerPro(SCLEX_POWERPRO, ColourisePowerProDocWrapper, "powerpro", FoldPowerProDoc, powerProWordLists); |