// Scintilla source code edit control /** @file LexMSSQL.cxx ** Lexer for MSSQL. **/ // By Filip Yaghob // The License.txt file describes the conditions under which this software may be distributed. #include #include #include #include #include #include "Platform.h" #include "PropSet.h" #include "Accessor.h" #include "KeyWords.h" #include "Scintilla.h" #include "SciLexer.h" #define KW_MSSQL_STATEMENTS 0 #define KW_MSSQL_DATA_TYPES 1 #define KW_MSSQL_SYSTEM_TABLES 2 #define KW_MSSQL_GLOBAL_VARIABLES 3 #define KW_MSSQL_FUNCTIONS 4 #define KW_MSSQL_STORED_PROCEDURES 5 #define KW_MSSQL_OPERATORS 6 static bool isMSSQLOperator(char ch) { if (isascii(ch) && isalnum(ch)) return false; // '.' left out as it is used to make up numbers if (ch == '%' || ch == '^' || ch == '&' || ch == '*' || ch == '-' || ch == '+' || ch == '=' || ch == '|' || ch == '<' || ch == '>' || ch == '/' || ch == '!' || ch == '~' || ch == '(' || ch == ')' || ch == ',') return true; return false; } static char classifyWordSQL(unsigned int start, unsigned int end, WordList *keywordlists[], Accessor &styler, unsigned int actualState, unsigned int prevState) { char s[256]; bool wordIsNumber = isdigit(styler[start]) || (styler[start] == '.'); WordList &kwStatements = *keywordlists[KW_MSSQL_STATEMENTS]; WordList &kwDataTypes = *keywordlists[KW_MSSQL_DATA_TYPES]; WordList &kwSystemTables = *keywordlists[KW_MSSQL_SYSTEM_TABLES]; WordList &kwGlobalVariables = *keywordlists[KW_MSSQL_GLOBAL_VARIABLES]; WordList &kwFunctions = *keywordlists[KW_MSSQL_FUNCTIONS]; WordList &kwStoredProcedures = *keywordlists[KW_MSSQL_STORED_PROCEDURES]; WordList &kwOperators = *keywordlists[KW_MSSQL_OPERATORS]; for (unsigned int i = 0; i < end - start + 1 && i < 128; i++) { s[i] = static_cast(tolower(styler[start + i])); s[i + 1] = '\0'; } char chAttr = SCE_MSSQL_IDENTIFIER; if (actualState == SCE_MSSQL_GLOBAL_VARIABLE) { if (kwGlobalVariables.InList(&s[2])) chAttr = SCE_MSSQL_GLOBAL_VARIABLE; } else if (wordIsNumber) { chAttr = SCE_MSSQL_NUMBER; } else if (prevState == SCE_MSSQL_DEFAULT_PREF_DATATYPE) { // Look first in datatypes if (kwDataTypes.InList(s)) chAttr = SCE_MSSQL_DATATYPE; else if (kwOperators.InList(s)) chAttr = SCE_MSSQL_OPERATOR; else if (kwStatements.InList(s)) chAttr = SCE_MSSQL_STATEMENT; else if (kwSystemTables.InList(s)) chAttr = SCE_MSSQL_SYSTABLE; else if (kwFunctions.InList(s)) chAttr = SCE_MSSQL_FUNCTION; else if (kwStoredProcedures.InList(s)) chAttr = SCE_MSSQL_STORED_PROCEDURE; } else { if (kwOperators.InList(s)) chAttr = SCE_MSSQL_OPERATOR; else if (kwStatements.InList(s)) chAttr = SCE_MSSQL_STATEMENT; else if (kwSystemTables.InList(s)) chAttr = SCE_MSSQL_SYSTABLE; else if (kwFunctions.InList(s)) chAttr = SCE_MSSQL_FUNCTION; else if (kwStoredProcedures.InList(s)) chAttr = SCE_MSSQL_STORED_PROCEDURE; else if (kwDataTypes.InList(s)) chAttr = SCE_MSSQL_DATATYPE; } styler.ColourTo(end, chAttr); return chAttr; } static void ColouriseMSSQLDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[], Accessor &styler) { styler.StartAt(startPos); bool fold = styler.GetPropertyInt("fold") != 0; int lineCurrent = styler.GetLine(startPos); int spaceFlags = 0; int state = initStyle; int prevState = initStyle; char chPrev = ' '; char chNext = styler[startPos]; styler.StartSegment(startPos); unsigned int lengthDoc = startPos + length; for (unsigned int i = startPos; i < lengthDoc; i++) { char ch = chNext; chNext = styler.SafeGetCharAt(i + 1); if ((ch == '\r' && chNext != '\n') || (ch == '\n')) { int indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags); int lev = indentCurrent; if (!(indentCurrent & SC_FOLDLEVELWHITEFLAG)) { // Only non whitespace lines can be headers int indentNext = styler.IndentAmount(lineCurrent + 1, &spaceFlags); if (indentCurrent < (indentNext & ~SC_FOLDLEVELWHITEFLAG)) { lev |= SC_FOLDLEVELHEADERFLAG; } } if (fold) { styler.SetLevel(lineCurrent, lev); } } if (styler.IsLeadByte(ch)) { chNext = styler.SafeGetCharAt(i + 2); chPrev = ' '; i += 1; continue; } // When the last char isn't part of the state (have to deal with it too)... if ( (state == SCE_MSSQL_IDENTIFIER) || (state == SCE_MSSQL_STORED_PROCEDURE) || (state == SCE_MSSQL_DATATYPE) || //~ (state == SCE_MSSQL_COLUMN_NAME) || (state == SCE_MSSQL_FUNCTION) || //~ (state == SCE_MSSQL_GLOBAL_VARIABLE) || (state == SCE_MSSQL_VARIABLE)) { if (!iswordchar(ch)) { int stateTmp; if ((state == SCE_MSSQL_VARIABLE) || (state == SCE_MSSQL_COLUMN_NAME)) { styler.ColourTo(i - 1, state); stateTmp = state; } else stateTmp = classifyWordSQL(styler.GetStartSegment(), i - 1, keywordlists, styler, state, prevState); prevState = state; if (stateTmp == SCE_MSSQL_IDENTIFIER || stateTmp == SCE_MSSQL_VARIABLE) state = SCE_MSSQL_DEFAULT_PREF_DATATYPE; else state = SCE_MSSQL_DEFAULT; } } else if (state == SCE_MSSQL_LINE_COMMENT) { if (ch == '\r' || ch == '\n') { styler.ColourTo(i - 1, state); prevState = state; state = SCE_MSSQL_DEFAULT; } } else if (state == SCE_MSSQL_GLOBAL_VARIABLE) { if ((ch != '@') && !iswordchar(ch)) { classifyWordSQL(styler.GetStartSegment(), i - 1, keywordlists, styler, state, prevState); prevState = state; state = SCE_MSSQL_DEFAULT; } } // If is the default or one of the above succeeded if (state == SCE_MSSQL_DEFAULT || state == SCE_MSSQL_DEFAULT_PREF_DATATYPE) { if (iswordstart(ch)) { styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT); prevState = state; state = SCE_MSSQL_IDENTIFIER; } else if (ch == '/' && chNext == '*') { styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT); prevState = state; state = SCE_MSSQL_COMMENT; } else if (ch == '-' && chNext == '-') { styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT); prevState = state; state = SCE_MSSQL_LINE_COMMENT; } else if (ch == '\'') { styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT); prevState = state; state = SCE_MSSQL_STRING; } else if (ch == '"') { styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT); prevState = state; state = SCE_MSSQL_COLUMN_NAME; } else if (ch == '[') { styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT); prevState = state; state = SCE_MSSQL_COLUMN_NAME_2; } else if (isMSSQLOperator(ch)) { styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT); styler.ColourTo(i, SCE_MSSQL_OPERATOR); //~ style = SCE_MSSQL_DEFAULT; prevState = state; state = SCE_MSSQL_DEFAULT; } else if (ch == '@') { styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT); prevState = state; if (chNext == '@') { state = SCE_MSSQL_GLOBAL_VARIABLE; // i += 2; } else state = SCE_MSSQL_VARIABLE; } // When the last char is part of the state... } else if (state == SCE_MSSQL_COMMENT) { if (ch == '/' && chPrev == '*') { if (((i > (styler.GetStartSegment() + 2)) || ((initStyle == SCE_MSSQL_COMMENT) && (styler.GetStartSegment() == startPos)))) { styler.ColourTo(i, state); //~ state = SCE_MSSQL_COMMENT; prevState = state; state = SCE_MSSQL_DEFAULT; } } } else if (state == SCE_MSSQL_STRING) { if (ch == '\'') { if ( chNext == '\'' ) { i++; ch = chNext; chNext = styler.SafeGetCharAt(i + 1); } else { styler.ColourTo(i, state); prevState = state; state = SCE_MSSQL_DEFAULT; //i++; } //ch = chNext; //chNext = styler.SafeGetCharAt(i + 1); } } else if (state == SCE_MSSQL_COLUMN_NAME) { if (ch == '"') { if (chNext == '"') { i++; ch = chNext; chNext = styler.SafeGetCharAt(i + 1); } else { styler.ColourTo(i, state); prevState = state; state = SCE_MSSQL_DEFAULT_PREF_DATATYPE; //i++; } } } else if (state == SCE_MSSQL_COLUMN_NAME_2) { if (ch == ']') { styler.ColourTo(i, state); prevState = state; state = SCE_MSSQL_DEFAULT_PREF_DATATYPE; //i++; } } chPrev = ch; } styler.ColourTo(lengthDoc - 1, state); } static void FoldMSSQLDoc(unsigned int startPos, int length, int, WordList *[], Accessor &styler) { bool foldComment = styler.GetPropertyInt("fold.comment") != 0; bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0; unsigned int endPos = startPos + length; int visibleChars = 0; int lineCurrent = styler.GetLine(startPos); int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK; int levelCurrent = levelPrev; char chNext = styler[startPos]; bool inComment = (styler.StyleAt(startPos-1) == SCE_MSSQL_COMMENT); char s[10]; for (unsigned int i = startPos; i < endPos; i++) { char ch = chNext; chNext = styler.SafeGetCharAt(i + 1); int style = styler.StyleAt(i); bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n'); // Comment folding if (foldComment) { if (!inComment && (style == SCE_MSSQL_COMMENT)) levelCurrent++; else if (inComment && (style != SCE_MSSQL_COMMENT)) levelCurrent--; inComment = (style == SCE_MSSQL_COMMENT); } if (style == SCE_MSSQL_STATEMENT) { // Folding between begin and end if (ch == 'b' || ch == 'e') { for (unsigned int j = 0; j < 5; j++) { if (!iswordchar(styler[i + j])) { break; } s[j] = styler[i + j]; s[j + 1] = '\0'; } if (strcmp(s, "begin") == 0) { levelCurrent++; } if (strcmp(s, "end") == 0) { levelCurrent--; } } } if (atEOL) { int lev = levelPrev; if (visibleChars == 0 && foldCompact) lev |= SC_FOLDLEVELWHITEFLAG; if ((levelCurrent > levelPrev) && (visibleChars > 0)) lev |= SC_FOLDLEVELHEADERFLAG; if (lev != styler.LevelAt(lineCurrent)) { styler.SetLevel(lineCurrent, lev); } lineCurrent++; levelPrev = levelCurrent; visibleChars = 0; } if (!isspacechar(ch)) visibleChars++; } // Fill in the real level of the next line, keeping the current flags as they will be filled in later int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK; styler.SetLevel(lineCurrent, levelPrev | flagsNext); } static const char * const sqlWordListDesc[] = { "Statements", "Data Types", "System tables", "Global variables", "Functions", "System Stored Procedures", "Operators", 0, }; LexerModule lmMSSQL(SCLEX_MSSQL, ColouriseMSSQLDoc, "mssql", FoldMSSQLDoc, sqlWordListDesc);