LexPowerPro.cpp 20 KB

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