mirror of
https://github.com/notepad-plus-plus/notepad-plus-plus.git
synced 2025-07-31 01:34:58 +02:00
Release 5.5.2 ( https://www.scintilla.org/scintilla552.zip ) Released 21 August 2024. Add SCI_SETCOPYSEPARATOR for separator between parts of a multiple selection when copied to the clipboard. Feature #1530. Add SCI_GETUNDOSEQUENCE to determine whether an undo sequence is active and its nesting depth. Add SCI_STYLESETSTRETCH to support condensed and expanded text styles. Add SCI_LINEINDENT and SCI_LINEDEDENT. Feature #1524. Fix bug on Cocoa where double-click stopped working when system had been running for a long time. On Cocoa implement more values of font weight and stretch. Release 5.4.0 ( https://www.scintilla.org/lexilla540.zip ) Released 21 August 2024. Inside Lexilla, LexerModule instances are now const. This will require changes to applications that modify Lexilla.cxx, which may be done to add custom lexers. Lexer added for TOML "toml". Bash: Handle backslash in heredoc delimiter. Issue #257. Progress: Fix lexing of nested comments. Pull request #258. Force lower-casing of case-insensitive keyword lists so keywords match in some lexers. Issue #259. Close #15564
460 lines
16 KiB
C++
460 lines
16 KiB
C++
// 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 <string>
|
|
#include <string_view>
|
|
|
|
#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"
|
|
|
|
using namespace Lexilla;
|
|
|
|
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, "FUNCTION") == 0)
|
|
newFoldlevel++;
|
|
else if ( CompareCaseInsensitive(s, "ENDIF") == 0 || CompareCaseInsensitive(s, "ENDWHILE") == 0
|
|
|| CompareCaseInsensitive(s, "ENDMACRO") == 0 || CompareCaseInsensitive(s, "ENDFOREACH") == 0
|
|
|| CompareCaseInsensitive(s, "ENDFUNCTION") == 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,};
|
|
|
|
extern const LexerModule lmCmake(SCLEX_CMAKE, ColouriseCmakeDoc, "cmake", FoldCmakeDoc, cmakeWordLists);
|