123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458 |
- // Scintilla source code edit control
- /** @file LexCmake.cxx
- ** Lexer for Cmake
- **/
- // Copyright 2007 by Cristian Adam <cristian [dot] adam [at] gmx [dot] net>
- // based on the NSIS lexer
- // The License.txt file describes the conditions under which this software may be distributed.
- #include <stdlib.h>
- #include <string.h>
- #include <stdio.h>
- #include <stdarg.h>
- #include <assert.h>
- #include <ctype.h>
- #include "ILexer.h"
- #include "Scintilla.h"
- #include "SciLexer.h"
- #include "WordList.h"
- #include "LexAccessor.h"
- #include "Accessor.h"
- #include "StyleContext.h"
- #include "CharacterSet.h"
- #include "LexerModule.h"
- #ifdef SCI_NAMESPACE
- using namespace Scintilla;
- #endif
- static bool isCmakeNumber(char ch)
- {
- return(ch >= '0' && ch <= '9');
- }
- static bool isCmakeChar(char ch)
- {
- return(ch == '.' ) || (ch == '_' ) || isCmakeNumber(ch) || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z');
- }
- static bool isCmakeLetter(char ch)
- {
- return(ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z');
- }
- static bool CmakeNextLineHasElse(Sci_PositionU start, Sci_PositionU end, Accessor &styler)
- {
- Sci_Position nNextLine = -1;
- for ( Sci_PositionU i = start; i < end; i++ ) {
- char cNext = styler.SafeGetCharAt( i );
- if ( cNext == '\n' ) {
- nNextLine = i+1;
- break;
- }
- }
- if ( nNextLine == -1 ) // We never foudn the next line...
- return false;
- for ( Sci_PositionU firstChar = nNextLine; firstChar < end; firstChar++ ) {
- char cNext = styler.SafeGetCharAt( firstChar );
- if ( cNext == ' ' )
- continue;
- if ( cNext == '\t' )
- continue;
- if ( styler.Match(firstChar, "ELSE") || styler.Match(firstChar, "else"))
- return true;
- break;
- }
- return false;
- }
- static int calculateFoldCmake(Sci_PositionU start, Sci_PositionU end, int foldlevel, Accessor &styler, bool bElse)
- {
- // If the word is too long, it is not what we are looking for
- if ( end - start > 20 )
- return foldlevel;
- int newFoldlevel = foldlevel;
- char s[20]; // The key word we are looking for has atmost 13 characters
- for (unsigned int i = 0; i < end - start + 1 && i < 19; i++) {
- s[i] = static_cast<char>( styler[ start + i ] );
- s[i + 1] = '\0';
- }
- if ( CompareCaseInsensitive(s, "IF") == 0 || CompareCaseInsensitive(s, "WHILE") == 0
- || CompareCaseInsensitive(s, "MACRO") == 0 || CompareCaseInsensitive(s, "FOREACH") == 0
- || CompareCaseInsensitive(s, "ELSEIF") == 0 )
- newFoldlevel++;
- else if ( CompareCaseInsensitive(s, "ENDIF") == 0 || CompareCaseInsensitive(s, "ENDWHILE") == 0
- || CompareCaseInsensitive(s, "ENDMACRO") == 0 || CompareCaseInsensitive(s, "ENDFOREACH") == 0)
- newFoldlevel--;
- else if ( bElse && CompareCaseInsensitive(s, "ELSEIF") == 0 )
- newFoldlevel++;
- else if ( bElse && CompareCaseInsensitive(s, "ELSE") == 0 )
- newFoldlevel++;
- return newFoldlevel;
- }
- static int classifyWordCmake(Sci_PositionU start, Sci_PositionU end, WordList *keywordLists[], Accessor &styler )
- {
- char word[100] = {0};
- char lowercaseWord[100] = {0};
- WordList &Commands = *keywordLists[0];
- WordList &Parameters = *keywordLists[1];
- WordList &UserDefined = *keywordLists[2];
- for (Sci_PositionU i = 0; i < end - start + 1 && i < 99; i++) {
- word[i] = static_cast<char>( styler[ start + i ] );
- lowercaseWord[i] = static_cast<char>(tolower(word[i]));
- }
- // Check for special words...
- if ( CompareCaseInsensitive(word, "MACRO") == 0 || CompareCaseInsensitive(word, "ENDMACRO") == 0 )
- return SCE_CMAKE_MACRODEF;
- if ( CompareCaseInsensitive(word, "IF") == 0 || CompareCaseInsensitive(word, "ENDIF") == 0 )
- return SCE_CMAKE_IFDEFINEDEF;
- if ( CompareCaseInsensitive(word, "ELSEIF") == 0 || CompareCaseInsensitive(word, "ELSE") == 0 )
- return SCE_CMAKE_IFDEFINEDEF;
- if ( CompareCaseInsensitive(word, "WHILE") == 0 || CompareCaseInsensitive(word, "ENDWHILE") == 0)
- return SCE_CMAKE_WHILEDEF;
- if ( CompareCaseInsensitive(word, "FOREACH") == 0 || CompareCaseInsensitive(word, "ENDFOREACH") == 0)
- return SCE_CMAKE_FOREACHDEF;
- if ( Commands.InList(lowercaseWord) )
- return SCE_CMAKE_COMMANDS;
- if ( Parameters.InList(word) )
- return SCE_CMAKE_PARAMETERS;
- if ( UserDefined.InList(word) )
- return SCE_CMAKE_USERDEFINED;
- if ( strlen(word) > 3 ) {
- if ( word[1] == '{' && word[strlen(word)-1] == '}' )
- return SCE_CMAKE_VARIABLE;
- }
- // To check for numbers
- if ( isCmakeNumber( word[0] ) ) {
- bool bHasSimpleCmakeNumber = true;
- for (unsigned int j = 1; j < end - start + 1 && j < 99; j++) {
- if ( !isCmakeNumber( word[j] ) ) {
- bHasSimpleCmakeNumber = false;
- break;
- }
- }
- if ( bHasSimpleCmakeNumber )
- return SCE_CMAKE_NUMBER;
- }
- return SCE_CMAKE_DEFAULT;
- }
- static void ColouriseCmakeDoc(Sci_PositionU startPos, Sci_Position length, int, WordList *keywordLists[], Accessor &styler)
- {
- int state = SCE_CMAKE_DEFAULT;
- if ( startPos > 0 )
- state = styler.StyleAt(startPos-1); // Use the style from the previous line, usually default, but could be commentbox
- styler.StartAt( startPos );
- styler.GetLine( startPos );
- Sci_PositionU nLengthDoc = startPos + length;
- styler.StartSegment( startPos );
- char cCurrChar;
- bool bVarInString = false;
- bool bClassicVarInString = false;
- Sci_PositionU i;
- for ( i = startPos; i < nLengthDoc; i++ ) {
- cCurrChar = styler.SafeGetCharAt( i );
- char cNextChar = styler.SafeGetCharAt(i+1);
- switch (state) {
- case SCE_CMAKE_DEFAULT:
- if ( cCurrChar == '#' ) { // we have a comment line
- styler.ColourTo(i-1, state );
- state = SCE_CMAKE_COMMENT;
- break;
- }
- if ( cCurrChar == '"' ) {
- styler.ColourTo(i-1, state );
- state = SCE_CMAKE_STRINGDQ;
- bVarInString = false;
- bClassicVarInString = false;
- break;
- }
- if ( cCurrChar == '\'' ) {
- styler.ColourTo(i-1, state );
- state = SCE_CMAKE_STRINGRQ;
- bVarInString = false;
- bClassicVarInString = false;
- break;
- }
- if ( cCurrChar == '`' ) {
- styler.ColourTo(i-1, state );
- state = SCE_CMAKE_STRINGLQ;
- bVarInString = false;
- bClassicVarInString = false;
- break;
- }
- // CMake Variable
- if ( cCurrChar == '$' || isCmakeChar(cCurrChar)) {
- styler.ColourTo(i-1,state);
- state = SCE_CMAKE_VARIABLE;
- // If it is a number, we must check and set style here first...
- if ( isCmakeNumber(cCurrChar) && (cNextChar == '\t' || cNextChar == ' ' || cNextChar == '\r' || cNextChar == '\n' ) )
- styler.ColourTo( i, SCE_CMAKE_NUMBER);
- break;
- }
- break;
- case SCE_CMAKE_COMMENT:
- if ( cCurrChar == '\n' || cCurrChar == '\r' ) {
- if ( styler.SafeGetCharAt(i-1) == '\\' ) {
- styler.ColourTo(i-2,state);
- styler.ColourTo(i-1,SCE_CMAKE_DEFAULT);
- }
- else {
- styler.ColourTo(i-1,state);
- state = SCE_CMAKE_DEFAULT;
- }
- }
- break;
- case SCE_CMAKE_STRINGDQ:
- case SCE_CMAKE_STRINGLQ:
- case SCE_CMAKE_STRINGRQ:
- if ( styler.SafeGetCharAt(i-1) == '\\' && styler.SafeGetCharAt(i-2) == '$' )
- break; // Ignore the next character, even if it is a quote of some sort
- if ( cCurrChar == '"' && state == SCE_CMAKE_STRINGDQ ) {
- styler.ColourTo(i,state);
- state = SCE_CMAKE_DEFAULT;
- break;
- }
- if ( cCurrChar == '`' && state == SCE_CMAKE_STRINGLQ ) {
- styler.ColourTo(i,state);
- state = SCE_CMAKE_DEFAULT;
- break;
- }
- if ( cCurrChar == '\'' && state == SCE_CMAKE_STRINGRQ ) {
- styler.ColourTo(i,state);
- state = SCE_CMAKE_DEFAULT;
- break;
- }
- if ( cNextChar == '\r' || cNextChar == '\n' ) {
- Sci_Position nCurLine = styler.GetLine(i+1);
- Sci_Position nBack = i;
- // We need to check if the previous line has a \ in it...
- bool bNextLine = false;
- while ( nBack > 0 ) {
- if ( styler.GetLine(nBack) != nCurLine )
- break;
- char cTemp = styler.SafeGetCharAt(nBack, 'a'); // Letter 'a' is safe here
- if ( cTemp == '\\' ) {
- bNextLine = true;
- break;
- }
- if ( cTemp != '\r' && cTemp != '\n' && cTemp != '\t' && cTemp != ' ' )
- break;
- nBack--;
- }
- if ( bNextLine ) {
- styler.ColourTo(i+1,state);
- }
- if ( bNextLine == false ) {
- styler.ColourTo(i,state);
- state = SCE_CMAKE_DEFAULT;
- }
- }
- break;
- case SCE_CMAKE_VARIABLE:
- // CMake Variable:
- if ( cCurrChar == '$' )
- state = SCE_CMAKE_DEFAULT;
- else if ( cCurrChar == '\\' && (cNextChar == 'n' || cNextChar == 'r' || cNextChar == 't' ) )
- state = SCE_CMAKE_DEFAULT;
- else if ( (isCmakeChar(cCurrChar) && !isCmakeChar( cNextChar) && cNextChar != '}') || cCurrChar == '}' ) {
- state = classifyWordCmake( styler.GetStartSegment(), i, keywordLists, styler );
- styler.ColourTo( i, state);
- state = SCE_CMAKE_DEFAULT;
- }
- else if ( !isCmakeChar( cCurrChar ) && cCurrChar != '{' && cCurrChar != '}' ) {
- if ( classifyWordCmake( styler.GetStartSegment(), i-1, keywordLists, styler) == SCE_CMAKE_NUMBER )
- styler.ColourTo( i-1, SCE_CMAKE_NUMBER );
- state = SCE_CMAKE_DEFAULT;
- if ( cCurrChar == '"' ) {
- state = SCE_CMAKE_STRINGDQ;
- bVarInString = false;
- bClassicVarInString = false;
- }
- else if ( cCurrChar == '`' ) {
- state = SCE_CMAKE_STRINGLQ;
- bVarInString = false;
- bClassicVarInString = false;
- }
- else if ( cCurrChar == '\'' ) {
- state = SCE_CMAKE_STRINGRQ;
- bVarInString = false;
- bClassicVarInString = false;
- }
- else if ( cCurrChar == '#' ) {
- state = SCE_CMAKE_COMMENT;
- }
- }
- break;
- }
- if ( state == SCE_CMAKE_STRINGDQ || state == SCE_CMAKE_STRINGLQ || state == SCE_CMAKE_STRINGRQ ) {
- bool bIngoreNextDollarSign = false;
- if ( bVarInString && cCurrChar == '$' ) {
- bVarInString = false;
- bIngoreNextDollarSign = true;
- }
- else if ( bVarInString && cCurrChar == '\\' && (cNextChar == 'n' || cNextChar == 'r' || cNextChar == 't' || cNextChar == '"' || cNextChar == '`' || cNextChar == '\'' ) ) {
- styler.ColourTo( i+1, SCE_CMAKE_STRINGVAR);
- bVarInString = false;
- bIngoreNextDollarSign = false;
- }
- else if ( bVarInString && !isCmakeChar(cNextChar) ) {
- int nWordState = classifyWordCmake( styler.GetStartSegment(), i, keywordLists, styler);
- if ( nWordState == SCE_CMAKE_VARIABLE )
- styler.ColourTo( i, SCE_CMAKE_STRINGVAR);
- bVarInString = false;
- }
- // Covers "${TEST}..."
- else if ( bClassicVarInString && cNextChar == '}' ) {
- styler.ColourTo( i+1, SCE_CMAKE_STRINGVAR);
- bClassicVarInString = false;
- }
- // Start of var in string
- if ( !bIngoreNextDollarSign && cCurrChar == '$' && cNextChar == '{' ) {
- styler.ColourTo( i-1, state);
- bClassicVarInString = true;
- bVarInString = false;
- }
- else if ( !bIngoreNextDollarSign && cCurrChar == '$' ) {
- styler.ColourTo( i-1, state);
- bVarInString = true;
- bClassicVarInString = false;
- }
- }
- }
- // Colourise remaining document
- styler.ColourTo(nLengthDoc-1,state);
- }
- static void FoldCmakeDoc(Sci_PositionU startPos, Sci_Position length, int, WordList *[], Accessor &styler)
- {
- // No folding enabled, no reason to continue...
- if ( styler.GetPropertyInt("fold") == 0 )
- return;
- bool foldAtElse = styler.GetPropertyInt("fold.at.else", 0) == 1;
- Sci_Position lineCurrent = styler.GetLine(startPos);
- Sci_PositionU safeStartPos = styler.LineStart( lineCurrent );
- bool bArg1 = true;
- Sci_Position nWordStart = -1;
- int levelCurrent = SC_FOLDLEVELBASE;
- if (lineCurrent > 0)
- levelCurrent = styler.LevelAt(lineCurrent-1) >> 16;
- int levelNext = levelCurrent;
- for (Sci_PositionU i = safeStartPos; i < startPos + length; i++) {
- char chCurr = styler.SafeGetCharAt(i);
- if ( bArg1 ) {
- if ( nWordStart == -1 && (isCmakeLetter(chCurr)) ) {
- nWordStart = i;
- }
- else if ( isCmakeLetter(chCurr) == false && nWordStart > -1 ) {
- int newLevel = calculateFoldCmake( nWordStart, i-1, levelNext, styler, foldAtElse);
- if ( newLevel == levelNext ) {
- if ( foldAtElse ) {
- if ( CmakeNextLineHasElse(i, startPos + length, styler) )
- levelNext--;
- }
- }
- else
- levelNext = newLevel;
- bArg1 = false;
- }
- }
- if ( chCurr == '\n' ) {
- if ( bArg1 && foldAtElse) {
- if ( CmakeNextLineHasElse(i, startPos + length, styler) )
- levelNext--;
- }
- // If we are on a new line...
- int levelUse = levelCurrent;
- int lev = levelUse | levelNext << 16;
- if (levelUse < levelNext )
- lev |= SC_FOLDLEVELHEADERFLAG;
- if (lev != styler.LevelAt(lineCurrent))
- styler.SetLevel(lineCurrent, lev);
- lineCurrent++;
- levelCurrent = levelNext;
- bArg1 = true; // New line, lets look at first argument again
- nWordStart = -1;
- }
- }
- int levelUse = levelCurrent;
- int lev = levelUse | levelNext << 16;
- if (levelUse < levelNext)
- lev |= SC_FOLDLEVELHEADERFLAG;
- if (lev != styler.LevelAt(lineCurrent))
- styler.SetLevel(lineCurrent, lev);
- }
- static const char * const cmakeWordLists[] = {
- "Commands",
- "Parameters",
- "UserDefined",
- 0,
- 0,};
- LexerModule lmCmake(SCLEX_CMAKE, ColouriseCmakeDoc, "cmake", FoldCmakeDoc, cmakeWordLists);
|