// Scintilla source code edit control /** @file LexSQL.cxx ** Lexer for SQL. **/ // Copyright 1998-2002 by Neil Hodgson // 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" static inline bool IsASpace(unsigned int ch) { return (ch == ' ') || ((ch >= 0x09) && (ch <= 0x0d)); } static bool MatchIgnoreCaseSubstring(const char *s, Accessor &styler, unsigned int startPos) { char ch = styler.SafeGetCharAt(startPos); bool isSubword = false; if (tolower(ch) != *s) return false; s++; if (*s == '~') { isSubword = true; s++; } int n; for (n = 1; *s; n++) { if (*s == '~') { isSubword = true; s++; } if (isSubword && IsASpace(styler.SafeGetCharAt(startPos + n))) return true; if (*s != tolower((styler.SafeGetCharAt(startPos + n)))) return false; s++; } return (IsASpace(styler.SafeGetCharAt(startPos + n)) || styler.SafeGetCharAt(startPos + n) == ';'); } static void getCurrent(unsigned int start, unsigned int end, Accessor &styler, char *s, unsigned int len) { for (unsigned int i = 0; i < end - start + 1 && i < len; i++) { s[i] = static_cast(tolower(styler[start + i])); s[i + 1] = '\0'; } } static void classifyWordSQL(unsigned int start, unsigned int end, WordList *keywordlists[], Accessor &styler) { char s[100]; bool wordIsNumber = isdigit(styler[start]) || (styler[start] == '.'); for (unsigned int i = 0; i < end - start + 1 && i < 80; i++) { s[i] = static_cast(tolower(styler[start + i])); s[i + 1] = '\0'; } WordList &keywords1 = *keywordlists[0]; WordList &keywords2 = *keywordlists[1]; // WordList &kw_pldoc = *keywordlists[2]; WordList &kw_sqlplus = *keywordlists[3]; WordList &kw_user1 = *keywordlists[4]; WordList &kw_user2 = *keywordlists[5]; WordList &kw_user3 = *keywordlists[6]; WordList &kw_user4 = *keywordlists[7]; char chAttr = SCE_SQL_IDENTIFIER; if (wordIsNumber) chAttr = SCE_SQL_NUMBER; else if (keywords1.InList(s)) chAttr = SCE_SQL_WORD; else if (keywords2.InList(s)) chAttr = SCE_SQL_WORD2; else if (kw_sqlplus.InListAbbreviated(s, '~')) chAttr = SCE_SQL_SQLPLUS; else if (kw_user1.InList(s)) chAttr = SCE_SQL_USER1; else if (kw_user2.InList(s)) chAttr = SCE_SQL_USER2; else if (kw_user3.InList(s)) chAttr = SCE_SQL_USER3; else if (kw_user4.InList(s)) chAttr = SCE_SQL_USER4; styler.ColourTo(end, chAttr); } static void ColouriseSQLDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[], Accessor &styler) { WordList &kw_pldoc = *keywordlists[2]; styler.StartAt(startPos); bool fold = styler.GetPropertyInt("fold") != 0; bool sqlBackslashEscapes = styler.GetPropertyInt("sql.backslash.escapes", 0) != 0; int lineCurrent = styler.GetLine(startPos); int spaceFlags = 0; int state = 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; } if (state == SCE_SQL_DEFAULT) { if (MatchIgnoreCaseSubstring("rem~ark", styler, i)) { styler.ColourTo(i - 1, state); state = SCE_SQL_SQLPLUS_COMMENT; } else if (MatchIgnoreCaseSubstring("pro~mpt", styler, i)) { styler.ColourTo(i - 1, state); state = SCE_SQL_SQLPLUS_PROMPT; } else if (iswordstart(ch)) { styler.ColourTo(i - 1, state); state = SCE_SQL_WORD; } else if (ch == '/' && chNext == '*') { styler.ColourTo(i - 1, state); if ((styler.SafeGetCharAt(i + 2)) == '*') { // Support of Doxygen doc. style state = SCE_SQL_COMMENTDOC; } else { state = SCE_SQL_COMMENT; } } else if (ch == '-' && chNext == '-') { styler.ColourTo(i - 1, state); state = SCE_SQL_COMMENTLINE; } else if (ch == '#') { styler.ColourTo(i - 1, state); state = SCE_SQL_COMMENTLINEDOC; } else if (ch == '\'') { styler.ColourTo(i - 1, state); state = SCE_SQL_CHARACTER; } else if (ch == '"') { styler.ColourTo(i - 1, state); state = SCE_SQL_STRING; } else if (isoperator(ch)) { styler.ColourTo(i - 1, state); styler.ColourTo(i, SCE_SQL_OPERATOR); } } else if (state == SCE_SQL_WORD) { if (!iswordchar(ch)) { classifyWordSQL(styler.GetStartSegment(), i - 1, keywordlists, styler); state = SCE_SQL_DEFAULT; if (ch == '/' && chNext == '*') { if ((styler.SafeGetCharAt(i + 2)) == '*') { // Support of Doxygen doc. style state = SCE_SQL_COMMENTDOC; } else { state = SCE_SQL_COMMENT; } } else if (ch == '-' && chNext == '-') { state = SCE_SQL_COMMENTLINE; } else if (ch == '#') { state = SCE_SQL_COMMENTLINEDOC; } else if (ch == '\'') { state = SCE_SQL_CHARACTER; } else if (ch == '"') { state = SCE_SQL_STRING; } else if (isoperator(ch)) { styler.ColourTo(i, SCE_SQL_OPERATOR); } } } else { if (state == SCE_SQL_COMMENT) { if (ch == '/' && chPrev == '*') { if (((i > (styler.GetStartSegment() + 2)) || ((initStyle == SCE_C_COMMENT) && (styler.GetStartSegment() == startPos)))) { styler.ColourTo(i, state); state = SCE_SQL_DEFAULT; } } } else if (state == SCE_SQL_COMMENTDOC) { if (ch == '/' && chPrev == '*') { if (((i > (styler.GetStartSegment() + 2)) || ((initStyle == SCE_SQL_COMMENTDOC) && (styler.GetStartSegment() == startPos)))) { styler.ColourTo(i, state); state = SCE_SQL_DEFAULT; } } else if (ch == '@') { // Verify that we have the conditions to mark a comment-doc-keyword if ((IsASpace(chPrev) || chPrev == '*') && (!IsASpace(chNext))) { styler.ColourTo(i - 1, state); state = SCE_SQL_COMMENTDOCKEYWORD; } } } else if (state == SCE_SQL_COMMENTLINE || state == SCE_SQL_COMMENTLINEDOC || state == SCE_SQL_SQLPLUS_COMMENT) { if (ch == '\r' || ch == '\n') { styler.ColourTo(i - 1, state); state = SCE_SQL_DEFAULT; } } else if (state == SCE_SQL_COMMENTDOCKEYWORD) { if (ch == '/' && chPrev == '*') { styler.ColourTo(i - 1, SCE_SQL_COMMENTDOCKEYWORDERROR); state = SCE_SQL_DEFAULT; } else if (!iswordchar(ch)) { char s[100]; getCurrent(styler.GetStartSegment(), i - 1, styler, s, 30); if (!kw_pldoc.InList(s + 1)) { state = SCE_SQL_COMMENTDOCKEYWORDERROR; } styler.ColourTo(i - 1, state); state = SCE_SQL_COMMENTDOC; } } else if (state == SCE_SQL_SQLPLUS_PROMPT) { if (ch == '\r' || ch == '\n') { styler.ColourTo(i - 1, state); state = SCE_SQL_DEFAULT; } } else if (state == SCE_SQL_CHARACTER) { if (sqlBackslashEscapes && ch == '\\') { i++; ch = chNext; chNext = styler.SafeGetCharAt(i + 1); } else if (ch == '\'') { if (chNext == '\'') { i++; } else { styler.ColourTo(i, state); state = SCE_SQL_DEFAULT; i++; } ch = chNext; chNext = styler.SafeGetCharAt(i + 1); } } else if (state == SCE_SQL_STRING) { if (ch == '"') { if (chNext == '"') { i++; } else { styler.ColourTo(i, state); state = SCE_SQL_DEFAULT; i++; } ch = chNext; chNext = styler.SafeGetCharAt(i + 1); } } if (state == SCE_SQL_DEFAULT) { // One of the above succeeded if (ch == '/' && chNext == '*') { if ((styler.SafeGetCharAt(i + 2)) == '*') { // Support of Doxygen doc. style state = SCE_SQL_COMMENTDOC; } else { state = SCE_SQL_COMMENT; } } else if (ch == '-' && chNext == '-') { state = SCE_SQL_COMMENTLINE; } else if (ch == '#') { state = SCE_SQL_COMMENTLINEDOC; } else if (MatchIgnoreCaseSubstring("rem~ark", styler, i)) { state = SCE_SQL_SQLPLUS_COMMENT; } else if (MatchIgnoreCaseSubstring("pro~mpt", styler, i)) { state = SCE_SQL_SQLPLUS_PROMPT; } else if (ch == '\'') { state = SCE_SQL_CHARACTER; } else if (ch == '"') { state = SCE_SQL_STRING; } else if (iswordstart(ch)) { state = SCE_SQL_WORD; } else if (isoperator(ch)) { styler.ColourTo(i, SCE_SQL_OPERATOR); } } } chPrev = ch; } styler.ColourTo(lengthDoc - 1, state); } static bool IsStreamCommentStyle(int style) { return style == SCE_SQL_COMMENT || style == SCE_SQL_COMMENTDOC || style == SCE_SQL_COMMENTDOCKEYWORD || style == SCE_SQL_COMMENTDOCKEYWORDERROR; } // Store both the current line's fold level and the next lines in the // level store to make it easy to pick up with each increment // and to make it possible to fiddle the current level for "} else {". static void FoldSQLDoc(unsigned int startPos, int length, int initStyle, 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 levelCurrent = SC_FOLDLEVELBASE; if (lineCurrent > 0) levelCurrent = styler.LevelAt(lineCurrent-1) >> 16; int levelNext = levelCurrent; char chNext = styler[startPos]; int styleNext = styler.StyleAt(startPos); int style = initStyle; bool endFound = false; for (unsigned int i = startPos; i < endPos; i++) { char ch = chNext; chNext = styler.SafeGetCharAt(i + 1); int stylePrev = style; style = styleNext; styleNext = styler.StyleAt(i + 1); bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n'); if (foldComment && IsStreamCommentStyle(style)) { if (!IsStreamCommentStyle(stylePrev)) { levelNext++; } else if (!IsStreamCommentStyle(styleNext) && !atEOL) { // Comments don't end at end of line and the next character may be unstyled. levelNext--; } } if (foldComment && (style == SCE_SQL_COMMENTLINE)) { if ((ch == '-') && (chNext == '-')) { char chNext2 = styler.SafeGetCharAt(i + 2); if (chNext2 == '{') { levelNext++; } else if (chNext2 == '}') { levelNext--; } } } if (style == SCE_SQL_WORD) { if (MatchIgnoreCaseSubstring("elsif", styler, i)) { // ignore elsif i += 4; } else if (MatchIgnoreCaseSubstring("if", styler, i) || MatchIgnoreCaseSubstring("loop", styler, i)){ if (endFound){ // ignore endFound = false; } else { levelNext++; } } else if (MatchIgnoreCaseSubstring("begin", styler, i)){ levelNext++; } else if (MatchIgnoreCaseSubstring("end", styler, i)) { endFound = true; levelNext--; if (levelNext < SC_FOLDLEVELBASE) levelNext = SC_FOLDLEVELBASE; } } if (atEOL) { int levelUse = levelCurrent; int lev = levelUse | levelNext << 16; if (visibleChars == 0 && foldCompact) lev |= SC_FOLDLEVELWHITEFLAG; if (levelUse < levelNext) lev |= SC_FOLDLEVELHEADERFLAG; if (lev != styler.LevelAt(lineCurrent)) { styler.SetLevel(lineCurrent, lev); } lineCurrent++; levelCurrent = levelNext; visibleChars = 0; endFound = false; } if (!isspacechar(ch)) visibleChars++; } } static const char * const sqlWordListDesc[] = { "Keywords", "Database Objects", "PLDoc", "SQL*Plus", "User Keywords 1", "User Keywords 2", "User Keywords 3", "User Keywords 4", 0 }; LexerModule lmSQL(SCLEX_SQL, ColouriseSQLDoc, "sql", FoldSQLDoc, sqlWordListDesc);