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