// Scintilla source code edit control /** @file LexInno.cxx ** Lexer for Inno Setup scripts. **/ // Written by Friedrich Vedder , using code from LexOthers.cxx. // Modified by Michael Heath. // The License.txt file describes the conditions under which this software may be distributed. #include #include #include #include #include #include #include #include #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 innoIsBlank(int ch) { return (ch == ' ') || (ch == '\t'); } static bool innoNextNotBlankIs(Sci_Position i, Accessor &styler, char needle) { char ch; while (i < styler.Length()) { ch = styler.SafeGetCharAt(i); if (ch == needle) return true; if (!innoIsBlank(ch)) return false; i++; } return false; } static void ColouriseInnoDoc(Sci_PositionU startPos, Sci_Position length, int, WordList *keywordLists[], Accessor &styler) { int state = SCE_INNO_DEFAULT; char chPrev; char ch = 0; char chNext = styler[startPos]; Sci_Position lengthDoc = startPos + length; char *buffer = new char[length + 1]; Sci_Position bufferCount = 0; bool isBOL, isEOL, isWS, isBOLWS = 0; // Save line state later with bitState and bitand with bit... to get line state int bitState = 0; int const bitCode = 1, bitMessages = 2, bitCommentCurly = 4, bitCommentRound = 8; // Get keyword lists WordList §ionKeywords = *keywordLists[0]; WordList &standardKeywords = *keywordLists[1]; WordList ¶meterKeywords = *keywordLists[2]; WordList &preprocessorKeywords = *keywordLists[3]; WordList &pascalKeywords = *keywordLists[4]; WordList &userKeywords = *keywordLists[5]; // Get line state Sci_Position curLine = styler.GetLine(startPos); int curLineState = curLine > 0 ? styler.GetLineState(curLine - 1) : 0; bool isCode = (curLineState & bitCode); bool isMessages = (curLineState & bitMessages); bool isCommentCurly = (curLineState & bitCommentCurly); bool isCommentRound = (curLineState & bitCommentRound); bool isCommentSlash = false; // Continue Pascal multline comment state if (isCommentCurly || isCommentRound) state = SCE_INNO_COMMENT_PASCAL; // Go through all provided text segment // using the hand-written state machine shown below styler.StartAt(startPos); styler.StartSegment(startPos); for (Sci_Position i = startPos; i < lengthDoc; i++) { chPrev = ch; ch = chNext; chNext = styler.SafeGetCharAt(i + 1); if (styler.IsLeadByte(ch)) { chNext = styler.SafeGetCharAt(i + 2); i++; continue; } isBOL = (chPrev == 0) || (chPrev == '\n') || (chPrev == '\r' && ch != '\n'); isBOLWS = (isBOL) ? 1 : (isBOLWS && (chPrev == ' ' || chPrev == '\t')); isEOL = (ch == '\n' || ch == '\r'); isWS = (ch == ' ' || ch == '\t'); if ((ch == '\r' && chNext != '\n') || (ch == '\n')) { // Remember the line state for future incremental lexing curLine = styler.GetLine(i); bitState = 0; if (isCode) bitState |= bitCode; if (isMessages) bitState |= bitMessages; if (isCommentCurly) bitState |= bitCommentCurly; if (isCommentRound) bitState |= bitCommentRound; styler.SetLineState(curLine, bitState); } switch(state) { case SCE_INNO_DEFAULT: if (!isCode && ch == ';' && isBOLWS) { // Start of a comment state = SCE_INNO_COMMENT; styler.ColourTo(i, SCE_INNO_COMMENT); } else if (ch == '[' && isBOLWS) { // Start of a section name state = SCE_INNO_SECTION; bufferCount = 0; } else if (ch == '#' && isBOLWS) { // Start of a preprocessor directive state = SCE_INNO_PREPROC; } else if (!isCode && ch == '{' && chNext != '{' && chPrev != '{') { // Start of an inline expansion state = SCE_INNO_INLINE_EXPANSION; } else if (isCode && ch == '{') { // Start of a Pascal comment state = SCE_INNO_COMMENT_PASCAL; isCommentCurly = true; styler.ColourTo(i, SCE_INNO_COMMENT_PASCAL); } else if (isCode && (ch == '(' && chNext == '*')) { // Start of a Pascal comment state = SCE_INNO_COMMENT_PASCAL; isCommentRound = true; styler.ColourTo(i + 1, SCE_INNO_COMMENT_PASCAL); } else if (isCode && ch == '/' && chNext == '/') { // Start of C-style comment state = SCE_INNO_COMMENT_PASCAL; isCommentSlash = true; styler.ColourTo(i + 1, SCE_INNO_COMMENT_PASCAL); } else if (!isMessages && ch == '"') { // Start of a double-quote string state = SCE_INNO_STRING_DOUBLE; styler.ColourTo(i, SCE_INNO_STRING_DOUBLE); } else if (!isMessages && ch == '\'') { // Start of a single-quote string state = SCE_INNO_STRING_SINGLE; styler.ColourTo(i, SCE_INNO_STRING_SINGLE); } else if (!isMessages && IsASCII(ch) && (isalpha(ch) || (ch == '_'))) { // Start of an identifier state = SCE_INNO_IDENTIFIER; bufferCount = 0; buffer[bufferCount++] = static_cast(tolower(ch)); } else { // Style it the default style styler.ColourTo(i, SCE_INNO_DEFAULT); } break; case SCE_INNO_COMMENT: if (isEOL) { state = SCE_INNO_DEFAULT; styler.ColourTo(i - 1, SCE_INNO_COMMENT); styler.ColourTo(i, SCE_INNO_DEFAULT); } else { styler.ColourTo(i, SCE_INNO_COMMENT); } break; case SCE_INNO_IDENTIFIER: if (IsASCII(ch) && (isalnum(ch) || (ch == '_'))) { buffer[bufferCount++] = static_cast(tolower(ch)); } else { state = SCE_INNO_DEFAULT; buffer[bufferCount] = '\0'; // Check if the buffer contains a keyword if (!isCode && standardKeywords.InList(buffer) && innoNextNotBlankIs(i, styler, '=')) { styler.ColourTo(i - 1, SCE_INNO_KEYWORD); } else if (!isCode && parameterKeywords.InList(buffer) && innoNextNotBlankIs(i, styler, ':')) { styler.ColourTo(i - 1, SCE_INNO_PARAMETER); } else if (isCode && pascalKeywords.InList(buffer)) { styler.ColourTo(i - 1, SCE_INNO_KEYWORD_PASCAL); } else if (!isCode && userKeywords.InList(buffer)) { styler.ColourTo(i - 1, SCE_INNO_KEYWORD_USER); } else { styler.ColourTo(i - 1, SCE_INNO_DEFAULT); } // Push back the faulty character chNext = styler[i--]; ch = chPrev; } break; case SCE_INNO_SECTION: if (ch == ']') { state = SCE_INNO_DEFAULT; buffer[bufferCount] = '\0'; // Check if the buffer contains a section name if (sectionKeywords.InList(buffer)) { styler.ColourTo(i, SCE_INNO_SECTION); isCode = !CompareCaseInsensitive(buffer, "code"); isMessages = isCode ? false : ( !CompareCaseInsensitive(buffer, "custommessages") || !CompareCaseInsensitive(buffer, "messages")); } else { styler.ColourTo(i, SCE_INNO_DEFAULT); } } else if (IsASCII(ch) && (isalnum(ch) || (ch == '_'))) { buffer[bufferCount++] = static_cast(tolower(ch)); } else { state = SCE_INNO_DEFAULT; styler.ColourTo(i, SCE_INNO_DEFAULT); } break; case SCE_INNO_PREPROC: if (isWS || isEOL) { if (IsASCII(chPrev) && isalpha(chPrev)) { state = SCE_INNO_DEFAULT; buffer[bufferCount] = '\0'; // Check if the buffer contains a preprocessor directive if (preprocessorKeywords.InList(buffer)) { styler.ColourTo(i - 1, SCE_INNO_PREPROC); } else { styler.ColourTo(i - 1, SCE_INNO_DEFAULT); } // Push back the faulty character chNext = styler[i--]; ch = chPrev; } } else if (IsASCII(ch) && isalpha(ch)) { if (chPrev == '#' || chPrev == ' ' || chPrev == '\t') bufferCount = 0; buffer[bufferCount++] = static_cast(tolower(ch)); } break; case SCE_INNO_STRING_DOUBLE: if (ch == '"') { state = SCE_INNO_DEFAULT; styler.ColourTo(i, SCE_INNO_STRING_DOUBLE); } else if (isEOL) { state = SCE_INNO_DEFAULT; styler.ColourTo(i - 1, SCE_INNO_STRING_DOUBLE); styler.ColourTo(i, SCE_INNO_DEFAULT); } else { styler.ColourTo(i, SCE_INNO_STRING_DOUBLE); } break; case SCE_INNO_STRING_SINGLE: if (ch == '\'') { state = SCE_INNO_DEFAULT; styler.ColourTo(i, SCE_INNO_STRING_SINGLE); } else if (isEOL) { state = SCE_INNO_DEFAULT; styler.ColourTo(i - 1, SCE_INNO_STRING_SINGLE); styler.ColourTo(i, SCE_INNO_DEFAULT); } else { styler.ColourTo(i, SCE_INNO_STRING_SINGLE); } break; case SCE_INNO_INLINE_EXPANSION: if (ch == '}') { state = SCE_INNO_DEFAULT; styler.ColourTo(i, SCE_INNO_INLINE_EXPANSION); } else if (isEOL) { state = SCE_INNO_DEFAULT; styler.ColourTo(i, SCE_INNO_DEFAULT); } break; case SCE_INNO_COMMENT_PASCAL: if (isCommentSlash) { if (isEOL) { state = SCE_INNO_DEFAULT; isCommentSlash = false; styler.ColourTo(i - 1, SCE_INNO_COMMENT_PASCAL); styler.ColourTo(i, SCE_INNO_DEFAULT); } else { styler.ColourTo(i, SCE_INNO_COMMENT_PASCAL); } } else if (isCommentCurly) { if (ch == '}') { state = SCE_INNO_DEFAULT; isCommentCurly = false; } styler.ColourTo(i, SCE_INNO_COMMENT_PASCAL); } else if (isCommentRound) { if (ch == ')' && chPrev == '*') { state = SCE_INNO_DEFAULT; isCommentRound = false; } styler.ColourTo(i, SCE_INNO_COMMENT_PASCAL); } break; } } delete []buffer; } static const char * const innoWordListDesc[] = { "Sections", "Keywords", "Parameters", "Preprocessor directives", "Pascal keywords", "User defined keywords", 0 }; static void FoldInnoDoc(Sci_PositionU startPos, Sci_Position length, int, WordList *[], Accessor &styler) { Sci_PositionU endPos = startPos + length; char chNext = styler[startPos]; Sci_Position lineCurrent = styler.GetLine(startPos); bool sectionFlag = false; int levelPrev = lineCurrent > 0 ? styler.LevelAt(lineCurrent - 1) : SC_FOLDLEVELBASE; int level; for (Sci_PositionU i = startPos; i < endPos; i++) { char ch = chNext; chNext = styler[i + 1]; bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n'); int style = styler.StyleAt(i); if (style == SCE_INNO_SECTION) sectionFlag = true; if (atEOL || i == endPos - 1) { if (sectionFlag) { level = SC_FOLDLEVELBASE | SC_FOLDLEVELHEADERFLAG; if (level == levelPrev) styler.SetLevel(lineCurrent - 1, levelPrev & ~SC_FOLDLEVELHEADERFLAG); } else { level = levelPrev & SC_FOLDLEVELNUMBERMASK; if (levelPrev & SC_FOLDLEVELHEADERFLAG) level++; } styler.SetLevel(lineCurrent, level); levelPrev = level; lineCurrent++; sectionFlag = false; } } } LexerModule lmInno(SCLEX_INNOSETUP, ColouriseInnoDoc, "inno", FoldInnoDoc, innoWordListDesc);