LexCmake.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. // Scintilla source code edit control
  2. /** @file LexCmake.cxx
  3. ** Lexer for Cmake
  4. **/
  5. // Copyright 2007 by Cristian Adam <cristian [dot] adam [at] gmx [dot] net>
  6. // based on the NSIS lexer
  7. // The License.txt file describes the conditions under which this software may be distributed.
  8. #include <stdlib.h>
  9. #include <string.h>
  10. #include <stdio.h>
  11. #include <stdarg.h>
  12. #include <assert.h>
  13. #include <ctype.h>
  14. #include "ILexer.h"
  15. #include "Scintilla.h"
  16. #include "SciLexer.h"
  17. #include "WordList.h"
  18. #include "LexAccessor.h"
  19. #include "Accessor.h"
  20. #include "StyleContext.h"
  21. #include "CharacterSet.h"
  22. #include "LexerModule.h"
  23. #ifdef SCI_NAMESPACE
  24. using namespace Scintilla;
  25. #endif
  26. static bool isCmakeNumber(char ch)
  27. {
  28. return(ch >= '0' && ch <= '9');
  29. }
  30. static bool isCmakeChar(char ch)
  31. {
  32. return(ch == '.' ) || (ch == '_' ) || isCmakeNumber(ch) || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z');
  33. }
  34. static bool isCmakeLetter(char ch)
  35. {
  36. return(ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z');
  37. }
  38. static bool CmakeNextLineHasElse(Sci_PositionU start, Sci_PositionU end, Accessor &styler)
  39. {
  40. Sci_Position nNextLine = -1;
  41. for ( Sci_PositionU i = start; i < end; i++ ) {
  42. char cNext = styler.SafeGetCharAt( i );
  43. if ( cNext == '\n' ) {
  44. nNextLine = i+1;
  45. break;
  46. }
  47. }
  48. if ( nNextLine == -1 ) // We never foudn the next line...
  49. return false;
  50. for ( Sci_PositionU firstChar = nNextLine; firstChar < end; firstChar++ ) {
  51. char cNext = styler.SafeGetCharAt( firstChar );
  52. if ( cNext == ' ' )
  53. continue;
  54. if ( cNext == '\t' )
  55. continue;
  56. if ( styler.Match(firstChar, "ELSE") || styler.Match(firstChar, "else"))
  57. return true;
  58. break;
  59. }
  60. return false;
  61. }
  62. static int calculateFoldCmake(Sci_PositionU start, Sci_PositionU end, int foldlevel, Accessor &styler, bool bElse)
  63. {
  64. // If the word is too long, it is not what we are looking for
  65. if ( end - start > 20 )
  66. return foldlevel;
  67. int newFoldlevel = foldlevel;
  68. char s[20]; // The key word we are looking for has atmost 13 characters
  69. for (unsigned int i = 0; i < end - start + 1 && i < 19; i++) {
  70. s[i] = static_cast<char>( styler[ start + i ] );
  71. s[i + 1] = '\0';
  72. }
  73. if ( CompareCaseInsensitive(s, "IF") == 0 || CompareCaseInsensitive(s, "WHILE") == 0
  74. || CompareCaseInsensitive(s, "MACRO") == 0 || CompareCaseInsensitive(s, "FOREACH") == 0
  75. || CompareCaseInsensitive(s, "ELSEIF") == 0 )
  76. newFoldlevel++;
  77. else if ( CompareCaseInsensitive(s, "ENDIF") == 0 || CompareCaseInsensitive(s, "ENDWHILE") == 0
  78. || CompareCaseInsensitive(s, "ENDMACRO") == 0 || CompareCaseInsensitive(s, "ENDFOREACH") == 0)
  79. newFoldlevel--;
  80. else if ( bElse && CompareCaseInsensitive(s, "ELSEIF") == 0 )
  81. newFoldlevel++;
  82. else if ( bElse && CompareCaseInsensitive(s, "ELSE") == 0 )
  83. newFoldlevel++;
  84. return newFoldlevel;
  85. }
  86. static int classifyWordCmake(Sci_PositionU start, Sci_PositionU end, WordList *keywordLists[], Accessor &styler )
  87. {
  88. char word[100] = {0};
  89. char lowercaseWord[100] = {0};
  90. WordList &Commands = *keywordLists[0];
  91. WordList &Parameters = *keywordLists[1];
  92. WordList &UserDefined = *keywordLists[2];
  93. for (Sci_PositionU i = 0; i < end - start + 1 && i < 99; i++) {
  94. word[i] = static_cast<char>( styler[ start + i ] );
  95. lowercaseWord[i] = static_cast<char>(tolower(word[i]));
  96. }
  97. // Check for special words...
  98. if ( CompareCaseInsensitive(word, "MACRO") == 0 || CompareCaseInsensitive(word, "ENDMACRO") == 0 )
  99. return SCE_CMAKE_MACRODEF;
  100. if ( CompareCaseInsensitive(word, "IF") == 0 || CompareCaseInsensitive(word, "ENDIF") == 0 )
  101. return SCE_CMAKE_IFDEFINEDEF;
  102. if ( CompareCaseInsensitive(word, "ELSEIF") == 0 || CompareCaseInsensitive(word, "ELSE") == 0 )
  103. return SCE_CMAKE_IFDEFINEDEF;
  104. if ( CompareCaseInsensitive(word, "WHILE") == 0 || CompareCaseInsensitive(word, "ENDWHILE") == 0)
  105. return SCE_CMAKE_WHILEDEF;
  106. if ( CompareCaseInsensitive(word, "FOREACH") == 0 || CompareCaseInsensitive(word, "ENDFOREACH") == 0)
  107. return SCE_CMAKE_FOREACHDEF;
  108. if ( Commands.InList(lowercaseWord) )
  109. return SCE_CMAKE_COMMANDS;
  110. if ( Parameters.InList(word) )
  111. return SCE_CMAKE_PARAMETERS;
  112. if ( UserDefined.InList(word) )
  113. return SCE_CMAKE_USERDEFINED;
  114. if ( strlen(word) > 3 ) {
  115. if ( word[1] == '{' && word[strlen(word)-1] == '}' )
  116. return SCE_CMAKE_VARIABLE;
  117. }
  118. // To check for numbers
  119. if ( isCmakeNumber( word[0] ) ) {
  120. bool bHasSimpleCmakeNumber = true;
  121. for (unsigned int j = 1; j < end - start + 1 && j < 99; j++) {
  122. if ( !isCmakeNumber( word[j] ) ) {
  123. bHasSimpleCmakeNumber = false;
  124. break;
  125. }
  126. }
  127. if ( bHasSimpleCmakeNumber )
  128. return SCE_CMAKE_NUMBER;
  129. }
  130. return SCE_CMAKE_DEFAULT;
  131. }
  132. static void ColouriseCmakeDoc(Sci_PositionU startPos, Sci_Position length, int, WordList *keywordLists[], Accessor &styler)
  133. {
  134. int state = SCE_CMAKE_DEFAULT;
  135. if ( startPos > 0 )
  136. state = styler.StyleAt(startPos-1); // Use the style from the previous line, usually default, but could be commentbox
  137. styler.StartAt( startPos );
  138. styler.GetLine( startPos );
  139. Sci_PositionU nLengthDoc = startPos + length;
  140. styler.StartSegment( startPos );
  141. char cCurrChar;
  142. bool bVarInString = false;
  143. bool bClassicVarInString = false;
  144. Sci_PositionU i;
  145. for ( i = startPos; i < nLengthDoc; i++ ) {
  146. cCurrChar = styler.SafeGetCharAt( i );
  147. char cNextChar = styler.SafeGetCharAt(i+1);
  148. switch (state) {
  149. case SCE_CMAKE_DEFAULT:
  150. if ( cCurrChar == '#' ) { // we have a comment line
  151. styler.ColourTo(i-1, state );
  152. state = SCE_CMAKE_COMMENT;
  153. break;
  154. }
  155. if ( cCurrChar == '"' ) {
  156. styler.ColourTo(i-1, state );
  157. state = SCE_CMAKE_STRINGDQ;
  158. bVarInString = false;
  159. bClassicVarInString = false;
  160. break;
  161. }
  162. if ( cCurrChar == '\'' ) {
  163. styler.ColourTo(i-1, state );
  164. state = SCE_CMAKE_STRINGRQ;
  165. bVarInString = false;
  166. bClassicVarInString = false;
  167. break;
  168. }
  169. if ( cCurrChar == '`' ) {
  170. styler.ColourTo(i-1, state );
  171. state = SCE_CMAKE_STRINGLQ;
  172. bVarInString = false;
  173. bClassicVarInString = false;
  174. break;
  175. }
  176. // CMake Variable
  177. if ( cCurrChar == '$' || isCmakeChar(cCurrChar)) {
  178. styler.ColourTo(i-1,state);
  179. state = SCE_CMAKE_VARIABLE;
  180. // If it is a number, we must check and set style here first...
  181. if ( isCmakeNumber(cCurrChar) && (cNextChar == '\t' || cNextChar == ' ' || cNextChar == '\r' || cNextChar == '\n' ) )
  182. styler.ColourTo( i, SCE_CMAKE_NUMBER);
  183. break;
  184. }
  185. break;
  186. case SCE_CMAKE_COMMENT:
  187. if ( cCurrChar == '\n' || cCurrChar == '\r' ) {
  188. if ( styler.SafeGetCharAt(i-1) == '\\' ) {
  189. styler.ColourTo(i-2,state);
  190. styler.ColourTo(i-1,SCE_CMAKE_DEFAULT);
  191. }
  192. else {
  193. styler.ColourTo(i-1,state);
  194. state = SCE_CMAKE_DEFAULT;
  195. }
  196. }
  197. break;
  198. case SCE_CMAKE_STRINGDQ:
  199. case SCE_CMAKE_STRINGLQ:
  200. case SCE_CMAKE_STRINGRQ:
  201. if ( styler.SafeGetCharAt(i-1) == '\\' && styler.SafeGetCharAt(i-2) == '$' )
  202. break; // Ignore the next character, even if it is a quote of some sort
  203. if ( cCurrChar == '"' && state == SCE_CMAKE_STRINGDQ ) {
  204. styler.ColourTo(i,state);
  205. state = SCE_CMAKE_DEFAULT;
  206. break;
  207. }
  208. if ( cCurrChar == '`' && state == SCE_CMAKE_STRINGLQ ) {
  209. styler.ColourTo(i,state);
  210. state = SCE_CMAKE_DEFAULT;
  211. break;
  212. }
  213. if ( cCurrChar == '\'' && state == SCE_CMAKE_STRINGRQ ) {
  214. styler.ColourTo(i,state);
  215. state = SCE_CMAKE_DEFAULT;
  216. break;
  217. }
  218. if ( cNextChar == '\r' || cNextChar == '\n' ) {
  219. Sci_Position nCurLine = styler.GetLine(i+1);
  220. Sci_Position nBack = i;
  221. // We need to check if the previous line has a \ in it...
  222. bool bNextLine = false;
  223. while ( nBack > 0 ) {
  224. if ( styler.GetLine(nBack) != nCurLine )
  225. break;
  226. char cTemp = styler.SafeGetCharAt(nBack, 'a'); // Letter 'a' is safe here
  227. if ( cTemp == '\\' ) {
  228. bNextLine = true;
  229. break;
  230. }
  231. if ( cTemp != '\r' && cTemp != '\n' && cTemp != '\t' && cTemp != ' ' )
  232. break;
  233. nBack--;
  234. }
  235. if ( bNextLine ) {
  236. styler.ColourTo(i+1,state);
  237. }
  238. if ( bNextLine == false ) {
  239. styler.ColourTo(i,state);
  240. state = SCE_CMAKE_DEFAULT;
  241. }
  242. }
  243. break;
  244. case SCE_CMAKE_VARIABLE:
  245. // CMake Variable:
  246. if ( cCurrChar == '$' )
  247. state = SCE_CMAKE_DEFAULT;
  248. else if ( cCurrChar == '\\' && (cNextChar == 'n' || cNextChar == 'r' || cNextChar == 't' ) )
  249. state = SCE_CMAKE_DEFAULT;
  250. else if ( (isCmakeChar(cCurrChar) && !isCmakeChar( cNextChar) && cNextChar != '}') || cCurrChar == '}' ) {
  251. state = classifyWordCmake( styler.GetStartSegment(), i, keywordLists, styler );
  252. styler.ColourTo( i, state);
  253. state = SCE_CMAKE_DEFAULT;
  254. }
  255. else if ( !isCmakeChar( cCurrChar ) && cCurrChar != '{' && cCurrChar != '}' ) {
  256. if ( classifyWordCmake( styler.GetStartSegment(), i-1, keywordLists, styler) == SCE_CMAKE_NUMBER )
  257. styler.ColourTo( i-1, SCE_CMAKE_NUMBER );
  258. state = SCE_CMAKE_DEFAULT;
  259. if ( cCurrChar == '"' ) {
  260. state = SCE_CMAKE_STRINGDQ;
  261. bVarInString = false;
  262. bClassicVarInString = false;
  263. }
  264. else if ( cCurrChar == '`' ) {
  265. state = SCE_CMAKE_STRINGLQ;
  266. bVarInString = false;
  267. bClassicVarInString = false;
  268. }
  269. else if ( cCurrChar == '\'' ) {
  270. state = SCE_CMAKE_STRINGRQ;
  271. bVarInString = false;
  272. bClassicVarInString = false;
  273. }
  274. else if ( cCurrChar == '#' ) {
  275. state = SCE_CMAKE_COMMENT;
  276. }
  277. }
  278. break;
  279. }
  280. if ( state == SCE_CMAKE_STRINGDQ || state == SCE_CMAKE_STRINGLQ || state == SCE_CMAKE_STRINGRQ ) {
  281. bool bIngoreNextDollarSign = false;
  282. if ( bVarInString && cCurrChar == '$' ) {
  283. bVarInString = false;
  284. bIngoreNextDollarSign = true;
  285. }
  286. else if ( bVarInString && cCurrChar == '\\' && (cNextChar == 'n' || cNextChar == 'r' || cNextChar == 't' || cNextChar == '"' || cNextChar == '`' || cNextChar == '\'' ) ) {
  287. styler.ColourTo( i+1, SCE_CMAKE_STRINGVAR);
  288. bVarInString = false;
  289. bIngoreNextDollarSign = false;
  290. }
  291. else if ( bVarInString && !isCmakeChar(cNextChar) ) {
  292. int nWordState = classifyWordCmake( styler.GetStartSegment(), i, keywordLists, styler);
  293. if ( nWordState == SCE_CMAKE_VARIABLE )
  294. styler.ColourTo( i, SCE_CMAKE_STRINGVAR);
  295. bVarInString = false;
  296. }
  297. // Covers "${TEST}..."
  298. else if ( bClassicVarInString && cNextChar == '}' ) {
  299. styler.ColourTo( i+1, SCE_CMAKE_STRINGVAR);
  300. bClassicVarInString = false;
  301. }
  302. // Start of var in string
  303. if ( !bIngoreNextDollarSign && cCurrChar == '$' && cNextChar == '{' ) {
  304. styler.ColourTo( i-1, state);
  305. bClassicVarInString = true;
  306. bVarInString = false;
  307. }
  308. else if ( !bIngoreNextDollarSign && cCurrChar == '$' ) {
  309. styler.ColourTo( i-1, state);
  310. bVarInString = true;
  311. bClassicVarInString = false;
  312. }
  313. }
  314. }
  315. // Colourise remaining document
  316. styler.ColourTo(nLengthDoc-1,state);
  317. }
  318. static void FoldCmakeDoc(Sci_PositionU startPos, Sci_Position length, int, WordList *[], Accessor &styler)
  319. {
  320. // No folding enabled, no reason to continue...
  321. if ( styler.GetPropertyInt("fold") == 0 )
  322. return;
  323. bool foldAtElse = styler.GetPropertyInt("fold.at.else", 0) == 1;
  324. Sci_Position lineCurrent = styler.GetLine(startPos);
  325. Sci_PositionU safeStartPos = styler.LineStart( lineCurrent );
  326. bool bArg1 = true;
  327. Sci_Position nWordStart = -1;
  328. int levelCurrent = SC_FOLDLEVELBASE;
  329. if (lineCurrent > 0)
  330. levelCurrent = styler.LevelAt(lineCurrent-1) >> 16;
  331. int levelNext = levelCurrent;
  332. for (Sci_PositionU i = safeStartPos; i < startPos + length; i++) {
  333. char chCurr = styler.SafeGetCharAt(i);
  334. if ( bArg1 ) {
  335. if ( nWordStart == -1 && (isCmakeLetter(chCurr)) ) {
  336. nWordStart = i;
  337. }
  338. else if ( isCmakeLetter(chCurr) == false && nWordStart > -1 ) {
  339. int newLevel = calculateFoldCmake( nWordStart, i-1, levelNext, styler, foldAtElse);
  340. if ( newLevel == levelNext ) {
  341. if ( foldAtElse ) {
  342. if ( CmakeNextLineHasElse(i, startPos + length, styler) )
  343. levelNext--;
  344. }
  345. }
  346. else
  347. levelNext = newLevel;
  348. bArg1 = false;
  349. }
  350. }
  351. if ( chCurr == '\n' ) {
  352. if ( bArg1 && foldAtElse) {
  353. if ( CmakeNextLineHasElse(i, startPos + length, styler) )
  354. levelNext--;
  355. }
  356. // If we are on a new line...
  357. int levelUse = levelCurrent;
  358. int lev = levelUse | levelNext << 16;
  359. if (levelUse < levelNext )
  360. lev |= SC_FOLDLEVELHEADERFLAG;
  361. if (lev != styler.LevelAt(lineCurrent))
  362. styler.SetLevel(lineCurrent, lev);
  363. lineCurrent++;
  364. levelCurrent = levelNext;
  365. bArg1 = true; // New line, lets look at first argument again
  366. nWordStart = -1;
  367. }
  368. }
  369. int levelUse = levelCurrent;
  370. int lev = levelUse | levelNext << 16;
  371. if (levelUse < levelNext)
  372. lev |= SC_FOLDLEVELHEADERFLAG;
  373. if (lev != styler.LevelAt(lineCurrent))
  374. styler.SetLevel(lineCurrent, lev);
  375. }
  376. static const char * const cmakeWordLists[] = {
  377. "Commands",
  378. "Parameters",
  379. "UserDefined",
  380. 0,
  381. 0,};
  382. LexerModule lmCmake(SCLEX_CMAKE, ColouriseCmakeDoc, "cmake", FoldCmakeDoc, cmakeWordLists);