diff options
author | nyamatongwe <unknown> | 2009-05-14 12:38:27 +0000 |
---|---|---|
committer | nyamatongwe <unknown> | 2009-05-14 12:38:27 +0000 |
commit | dbf6ed4d115282451b88844e8a8c75dccb9aec4a (patch) | |
tree | 655e5ba85b730eb189f54d8159b0e3fac99e44bf /src/LexMySQL.cxx | |
parent | 56b8f2fb26d5c518712796a9832c3d43ea570c58 (diff) | |
download | scintilla-mirror-dbf6ed4d115282451b88844e8a8c75dccb9aec4a.tar.gz |
Updated MySQL lexer from Mike Lischke at Sun.
Adds SCE_MYSQL_HIDDENCOMMAND.
List of keyword names terminated with 0.
Diffstat (limited to 'src/LexMySQL.cxx')
-rw-r--r-- | src/LexMySQL.cxx | 639 |
1 files changed, 379 insertions, 260 deletions
diff --git a/src/LexMySQL.cxx b/src/LexMySQL.cxx index 742ff4339..b6d7135b1 100644 --- a/src/LexMySQL.cxx +++ b/src/LexMySQL.cxx @@ -1,11 +1,14 @@ -// Scintilla source code edit control -/** @file LexMySQL.cxx - ** Lexer for MySQL - **/ -// Adopted from LexSQL.cxx by Anders Karlsson <anders@mysql.com> -// Original work by Neil Hodgson <neilh@scintilla.org> -// Copyright 1998-2005 by Neil Hodgson <neilh@scintilla.org> -// The License.txt file describes the conditions under which this software may be distributed. +/** + * Scintilla source code edit control + * @file LexMySQL.cxx + * Lexer for MySQL + * + * Improved by Mike Lischke <mike.lischke@sun.com> + * Adopted from LexSQL.cxx by Anders Karlsson <anders@mysql.com> + * Original work by Neil Hodgson <neilh@scintilla.org> + * Copyright 1998-2005 by Neil Hodgson <neilh@scintilla.org> + * The License.txt file describes the conditions under which this software may be distributed. + */ #include <stdlib.h> #include <string.h> @@ -49,287 +52,400 @@ static inline bool IsANumberChar(int ch) { ch == '.' || ch == '-' || ch == '+'); } -static void ColouriseMySQLDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[], - Accessor &styler) { +//-------------------------------------------------------------------------------------------------- + +/** + * Check if the current content context represent a keyword and set the context state if so. + */ +static void CheckForKeyword(StyleContext& sc, WordList* keywordlists[]) +{ + int length = sc.LengthCurrent() + 1; // +1 for the next char + char* s = new char[length]; + sc.GetCurrentLowered(s, length); + if (keywordlists[0]->InList(s)) + sc.ChangeState(SCE_MYSQL_MAJORKEYWORD); + else + if (keywordlists[1]->InList(s)) + sc.ChangeState(SCE_MYSQL_KEYWORD); + else + if (keywordlists[2]->InList(s)) + sc.ChangeState(SCE_MYSQL_DATABASEOBJECT); + else + if (keywordlists[3]->InList(s)) + sc.ChangeState(SCE_MYSQL_FUNCTION); + else + if (keywordlists[5]->InList(s)) + sc.ChangeState(SCE_MYSQL_PROCEDUREKEYWORD); + else + if (keywordlists[6]->InList(s)) + sc.ChangeState(SCE_MYSQL_USER1); + else + if (keywordlists[7]->InList(s)) + sc.ChangeState(SCE_MYSQL_USER2); + else + if (keywordlists[8]->InList(s)) + sc.ChangeState(SCE_MYSQL_USER3); + delete [] s; +} - WordList &major_keywords = *keywordlists[0]; - WordList &keywords = *keywordlists[1]; - WordList &database_objects = *keywordlists[2]; - WordList &functions = *keywordlists[3]; - WordList &system_variables = *keywordlists[4]; - WordList &procedure_keywords = *keywordlists[5]; - WordList &kw_user1 = *keywordlists[6]; - WordList &kw_user2 = *keywordlists[7]; - WordList &kw_user3 = *keywordlists[8]; +//-------------------------------------------------------------------------------------------------- +static void ColouriseMySQLDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[], + Accessor &styler) +{ StyleContext sc(startPos, length, initStyle, styler); - for (; sc.More(); sc.Forward()) { + for (; sc.More(); sc.Forward()) + { // Determine if the current state should terminate. - switch (sc.state) { - case SCE_MYSQL_OPERATOR: - sc.SetState(SCE_MYSQL_DEFAULT); - break; - case SCE_MYSQL_NUMBER: - // We stop the number definition on non-numerical non-dot non-eE non-sign char - if (!IsANumberChar(sc.ch)) { - sc.SetState(SCE_MYSQL_DEFAULT); - } - break; - case SCE_MYSQL_IDENTIFIER: - if (!IsAWordChar(sc.ch)) { - int nextState = SCE_MYSQL_DEFAULT; - char s[1000]; - sc.GetCurrentLowered(s, sizeof(s)); - if (major_keywords.InList(s)) { - sc.ChangeState(SCE_MYSQL_MAJORKEYWORD); - } else if (keywords.InList(s)) { - sc.ChangeState(SCE_MYSQL_KEYWORD); - } else if (database_objects.InList(s)) { - sc.ChangeState(SCE_MYSQL_DATABASEOBJECT); - } else if (functions.InList(s)) { - sc.ChangeState(SCE_MYSQL_FUNCTION); - } else if (procedure_keywords.InList(s)) { - sc.ChangeState(SCE_MYSQL_PROCEDUREKEYWORD); - } else if (kw_user1.InList(s)) { - sc.ChangeState(SCE_MYSQL_USER1); - } else if (kw_user2.InList(s)) { - sc.ChangeState(SCE_MYSQL_USER2); - } else if (kw_user3.InList(s)) { - sc.ChangeState(SCE_MYSQL_USER3); - } - sc.SetState(nextState); - } - break; - case SCE_MYSQL_VARIABLE: - if (!IsAWordChar(sc.ch)) { - sc.SetState(SCE_MYSQL_DEFAULT); - } - break; - case SCE_MYSQL_SYSTEMVARIABLE: - if (!IsAWordChar(sc.ch)) { - char s[1000]; - sc.GetCurrentLowered(s, sizeof(s)); -// Check for known system variables here. - if (system_variables.InList(&s[2])) { - sc.ChangeState(SCE_MYSQL_KNOWNSYSTEMVARIABLE); - } - sc.SetState(SCE_MYSQL_DEFAULT); - } - break; - case SCE_MYSQL_QUOTEDIDENTIFIER: - if (sc.ch == 0x60) { - if (sc.chNext == 0x60) { - sc.Forward(); // Ignore it - } else { - sc.ForwardSetState(SCE_MYSQL_DEFAULT); - } - } - break; - case SCE_MYSQL_COMMENT: - if (sc.Match('*', '/')) { - sc.Forward(); - sc.ForwardSetState(SCE_MYSQL_DEFAULT); - } - break; - case SCE_MYSQL_COMMENTLINE: - if (sc.atLineStart) { - sc.SetState(SCE_MYSQL_DEFAULT); - } - break; - case SCE_MYSQL_SQSTRING: - if (sc.ch == '\\') { - // Escape sequence - sc.Forward(); - } else if (sc.ch == '\'') { - if (sc.chNext == '\'') { - sc.Forward(); - } else { - sc.ChangeState(SCE_MYSQL_STRING); - sc.ForwardSetState(SCE_MYSQL_DEFAULT); - } - } - break; - case SCE_MYSQL_DQSTRING: - if (sc.ch == '\\') { - // Escape sequence - sc.Forward(); - } else if (sc.ch == '\"') { - if (sc.chNext == '\"') { - sc.Forward(); - } else { - sc.ChangeState(SCE_MYSQL_STRING); - sc.ForwardSetState(SCE_MYSQL_DEFAULT); + switch (sc.state) + { + case SCE_MYSQL_OPERATOR: + sc.SetState(SCE_MYSQL_DEFAULT); + break; + case SCE_MYSQL_NUMBER: + // We stop the number definition on non-numerical non-dot non-eE non-sign char. + if (!IsANumberChar(sc.ch)) + sc.SetState(SCE_MYSQL_DEFAULT); + break; + case SCE_MYSQL_IDENTIFIER: + // Switch from identifier to keyword state and open a new state for the new char. + if (!IsAWordChar(sc.ch)) + { + CheckForKeyword(sc, keywordlists); + sc.SetState(SCE_MYSQL_DEFAULT); + } + break; + case SCE_MYSQL_VARIABLE: + if (!IsAWordChar(sc.ch)) + sc.SetState(SCE_MYSQL_DEFAULT); + break; + case SCE_MYSQL_SYSTEMVARIABLE: + if (!IsAWordChar(sc.ch)) + { + int length = sc.LengthCurrent() + 1; + char* s = new char[length]; + sc.GetCurrentLowered(s, length); + + // Check for known system variables here. + if (keywordlists[4]->InList(&s[2])) + sc.ChangeState(SCE_MYSQL_KNOWNSYSTEMVARIABLE); + delete [] s; + + sc.SetState(SCE_MYSQL_DEFAULT); + } + break; + case SCE_MYSQL_QUOTEDIDENTIFIER: + if (sc.ch == '`') + { + if (sc.chNext == '`') + sc.Forward(); // Ignore it + else + sc.ForwardSetState(SCE_MYSQL_DEFAULT); } - } - break; - } + break; + case SCE_MYSQL_COMMENT: + case SCE_MYSQL_HIDDENCOMMAND: + if (sc.Match('*', '/')) + { + sc.Forward(); + sc.ForwardSetState(SCE_MYSQL_DEFAULT); + } + break; + case SCE_MYSQL_COMMENTLINE: + if (sc.atLineStart) + sc.SetState(SCE_MYSQL_DEFAULT); + break; + case SCE_MYSQL_SQSTRING: + if (sc.ch == '\\') + sc.Forward(); // Escape sequence + else + if (sc.ch == '\'') + { + // End of single quoted string reached? + if (sc.chNext == '\'') + sc.Forward(); + else + sc.ForwardSetState(SCE_MYSQL_DEFAULT); + } + break; + case SCE_MYSQL_DQSTRING: + if (sc.ch == '\\') + sc.Forward(); // Escape sequence + else + if (sc.ch == '\"') + { + // End of single quoted string reached? + if (sc.chNext == '\"') + sc.Forward(); + else + sc.ForwardSetState(SCE_MYSQL_DEFAULT); + } + break; + } - // Determine if a new state should be entered. - if (sc.state == SCE_MYSQL_DEFAULT) { - if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) { - sc.SetState(SCE_MYSQL_NUMBER); - } else if (IsAWordStart(sc.ch)) { - sc.SetState(SCE_MYSQL_IDENTIFIER); -// Note that the order of SYSTEMVARIABLE and VARIABLE is important here. - } else if (sc.ch == 0x40 && sc.chNext == 0x40) { - sc.SetState(SCE_MYSQL_SYSTEMVARIABLE); - sc.Forward(); // Skip past the second at-sign. - } else if (sc.ch == 0x40) { - sc.SetState(SCE_MYSQL_VARIABLE); - } else if (sc.ch == 0x60) { - sc.SetState(SCE_MYSQL_QUOTEDIDENTIFIER); - } else if (sc.Match('/', '*')) { - sc.SetState(SCE_MYSQL_COMMENT); - sc.Forward(); // Eat the * so it isn't used for the end of the comment - } else if (sc.Match('-', '-') || sc.Match('#')) { - sc.SetState(SCE_MYSQL_COMMENTLINE); - } else if (sc.ch == '\'') { - sc.SetState(SCE_MYSQL_SQSTRING); - } else if (sc.ch == '\"') { - sc.SetState(SCE_MYSQL_DQSTRING); - } else if (isoperator(static_cast<char>(sc.ch))) { - sc.SetState(SCE_MYSQL_OPERATOR); - } - } - } - sc.Complete(); + // Determine if a new state should be entered. + if (sc.state == SCE_MYSQL_DEFAULT) + { + switch (sc.ch) + { + case '@': + if (sc.chNext == '@') + { + sc.SetState(SCE_MYSQL_SYSTEMVARIABLE); + sc.Forward(2); // Skip past @@. + } + else + if (IsAWordStart(sc.ch)) + { + sc.SetState(SCE_MYSQL_VARIABLE); + sc.Forward(); // Skip past @. + } + else + sc.SetState(SCE_MYSQL_OPERATOR); + break; + case '`': + sc.SetState(SCE_MYSQL_QUOTEDIDENTIFIER); + break; + case '#': + sc.SetState(SCE_MYSQL_COMMENTLINE); + break; + case '\'': + sc.SetState(SCE_MYSQL_SQSTRING); + break; + case '\"': + sc.SetState(SCE_MYSQL_DQSTRING); + break; + default: + if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) + sc.SetState(SCE_MYSQL_NUMBER); + else + if (IsAWordStart(sc.ch)) + sc.SetState(SCE_MYSQL_IDENTIFIER); + else + if (sc.Match('/', '*')) + { + sc.SetState(SCE_MYSQL_COMMENT); + + // Skip comment introducer and check for hidden command. + sc.Forward(2); + if (sc.ch == '!') + { + sc.ChangeState(SCE_MYSQL_HIDDENCOMMAND); + sc.Forward(); + } + } + else + if (sc.Match("--")) + { + // Special MySQL single line comment. + sc.SetState(SCE_MYSQL_COMMENTLINE); + sc.Forward(2); + + // Check the third character too. It must be a space or EOL. + if (sc.ch != ' ' && sc.ch != '\n' && sc.ch != '\r') + sc.ChangeState(SCE_MYSQL_OPERATOR); + } + else + if (isoperator(static_cast<char>(sc.ch))) + sc.SetState(SCE_MYSQL_OPERATOR); + } + } + } + + // Do a final check for keywords if we currently have an identifier, to highlight them + // also at the end of a line. + if (sc.state == SCE_MYSQL_IDENTIFIER) + CheckForKeyword(sc, keywordlists); + + sc.Complete(); } -static bool IsStreamCommentStyle(int style) { +//-------------------------------------------------------------------------------------------------- + +/** + * Helper function to determine if we have a foldable comment currently. + */ +static bool IsStreamCommentStyle(int style) +{ return style == SCE_MYSQL_COMMENT; } +//-------------------------------------------------------------------------------------------------- + // 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. -static void FoldMySQLDoc(unsigned int startPos, int length, int initStyle, - WordList *[], Accessor &styler) { +static void FoldMySQLDoc(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; bool foldOnlyBegin = styler.GetPropertyInt("fold.sql.only.begin", 0) != 0; - unsigned int endPos = startPos + length; + StyleContext sc(startPos, length, initStyle, styler); + int visibleChars = 0; int lineCurrent = styler.GetLine(startPos); int levelCurrent = SC_FOLDLEVELBASE; - if (lineCurrent > 0) { + 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; + + bool endFound = false; bool whenFound = false; bool elseFound = false; - for (unsigned int i = startPos; i < endPos; i++) { - char ch = chNext; - chNext = styler.SafeGetCharAt(i + 1); + + for (unsigned int i = startPos; sc.More(); i++, sc.Forward()) + { 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_MYSQL_COMMENTLINE)) { - // MySQL needs -- comments to be followed by space or control char - if ((ch == '-') && (chNext == '-')) { - char chNext2 = styler.SafeGetCharAt(i + 2); - char chNext3 = styler.SafeGetCharAt(i + 3); - if (chNext2 == '{' || chNext3 == '{') { - levelNext++; - } else if (chNext2 == '}' || chNext3 == '}') { - levelNext--; - } - } - } - if (style == SCE_MYSQL_OPERATOR) { - if (ch == '(') { - levelNext++; - } else if (ch == ')') { - levelNext--; - } - } - -// Style new keywords here. - if ((style == SCE_MYSQL_MAJORKEYWORD && stylePrev != SCE_MYSQL_MAJORKEYWORD) - || (style == SCE_MYSQL_KEYWORD && stylePrev != SCE_MYSQL_KEYWORD) - || (style == SCE_MYSQL_PROCEDUREKEYWORD && stylePrev != SCE_MYSQL_PROCEDUREKEYWORD)) { - const int MAX_KW_LEN = 6; // Maximum length of folding keywords - char s[MAX_KW_LEN + 2]; - unsigned int j = 0; - for (; j < MAX_KW_LEN + 1; j++) { - if (!iswordchar(styler[i + j])) { - break; - } - s[j] = static_cast<char>(tolower(styler[i + j])); - } - if (j == MAX_KW_LEN + 1) { - // Keyword too long, don't test it - s[0] = '\0'; - } else { - s[j] = '\0'; - } - if (!foldOnlyBegin && endFound && (strcmp(s, "if") == 0 || strcmp(s, "while") == 0 || strcmp(s, "loop") == 0)) { - endFound = false; - levelNext--; - if (levelNext < SC_FOLDLEVELBASE) { - levelNext = SC_FOLDLEVELBASE; - } -// Note that else is special here. It may or may be followed by an if then, but in aly case the level stays the -// same. When followed by a if .. then, the level will be increased later, if not, at eol. - } else if (!foldOnlyBegin && strcmp(s, "else") == 0) { - levelNext--; - elseFound = true; - } else if (!foldOnlyBegin && strcmp(s, "then") == 0) { - if(whenFound) { - whenFound = false; - } else { - levelNext++; - } - } else if (strcmp(s, "if") == 0) { - elseFound = false; - } else if (strcmp(s, "when") == 0) { - whenFound = true; - } else if (strcmp(s, "begin") == 0) { - levelNext++; - } else if (!foldOnlyBegin && (strcmp(s, "loop") == 0 || strcmp(s, "repeat") == 0 - || strcmp(s, "while") == 0)) { - if(endFound) { - endFound = false; - } else { - levelNext++; - } - } else if (strcmp(s, "end") == 0) { -// Multiple END in a row are counted multiple times! - if (endFound) { - levelNext--; - if (levelNext < SC_FOLDLEVELBASE) { - levelNext = SC_FOLDLEVELBASE; - } - } - endFound = true; - whenFound = false; - } - } -// Handle this for a trailing end withiut an if / while etc, as in the case of a begin. - if (endFound) { + bool atEOL = (sc.ch == '\r' && sc.chNext != '\n') || (sc.ch == '\n'); + + switch (style) + { + case SCE_MYSQL_COMMENT: + if (foldComment) + { + // Multiline comment style /* .. */. + if (IsStreamCommentStyle(style)) + { + // Increase level if we just start a foldable comment. + if (!IsStreamCommentStyle(stylePrev)) + levelNext++; + else + // If we are in the middle of a foldable comment check if it ends now. + // Don't end at the line end, though. + if (!IsStreamCommentStyle(styleNext) && !atEOL) + levelNext--; + } + } + break; + case SCE_MYSQL_COMMENTLINE: + if (foldComment) + { + // Not really a standard, but we add support for single line comments + // with special curly braces syntax as foldable comments too. + // MySQL needs -- comments to be followed by space or control char + if (sc.Match('-', '-')) + { + char chNext2 = styler.SafeGetCharAt(i + 2); + char chNext3 = styler.SafeGetCharAt(i + 3); + if (chNext2 == '{' || chNext3 == '{') + levelNext++; + else + if (chNext2 == '}' || chNext3 == '}') + levelNext--; + } + } + break; + case SCE_MYSQL_HIDDENCOMMAND: + if (style != stylePrev) + levelNext++; + else + if (style != styleNext) + levelNext--; + break; + case SCE_MYSQL_OPERATOR: + if (sc.ch == '(') + levelNext++; + else + if (sc.ch == ')') + levelNext--; + break; + case SCE_MYSQL_MAJORKEYWORD: + case SCE_MYSQL_KEYWORD: + case SCE_MYSQL_FUNCTION: + case SCE_MYSQL_PROCEDUREKEYWORD: + // Reserved and other keywords. + if (style != stylePrev) + { + bool beginFound = sc.MatchIgnoreCase("begin"); + bool ifFound = sc.MatchIgnoreCase("if"); + bool thenFound = sc.MatchIgnoreCase("then"); + bool whileFound = sc.MatchIgnoreCase("while"); + bool loopFound = sc.MatchIgnoreCase("loop"); + bool repeatFound = sc.MatchIgnoreCase("repeat"); + + if (!foldOnlyBegin && endFound && (ifFound || whileFound || loopFound)) + { + endFound = false; + levelNext--; + if (levelNext < SC_FOLDLEVELBASE) + levelNext = SC_FOLDLEVELBASE; + + // Note that "else" is special here. It may or may not be followed by an "if .. then", + // but in any case the level stays the same. When followed by an "if .. then" the level + // will be increased later, if not, then at eol. + } + else + if (!foldOnlyBegin && sc.MatchIgnoreCase("else")) + { + levelNext--; + elseFound = true; + } + else + if (!foldOnlyBegin && thenFound) + { + if (whenFound) + whenFound = false; + else + levelNext++; + } + else + if (ifFound) + elseFound = false; + else + if (sc.MatchIgnoreCase("when")) + whenFound = true; + else + { + if (beginFound) + levelNext++; + else + if (!foldOnlyBegin && (loopFound || repeatFound || whileFound)) + { + if (endFound) + endFound = false; + else + levelNext++; + } + else + if (sc.MatchIgnoreCase("end")) + { + // Multiple "end" in a row are counted multiple times! + if (endFound) + { + levelNext--; + if (levelNext < SC_FOLDLEVELBASE) + levelNext = SC_FOLDLEVELBASE; + } + endFound = true; + whenFound = false; + } + } + } + break; + } + + // Handle the case of a trailing end without an if / while etc, as in the case of a begin. + if (endFound) + { endFound = false; levelNext--; - if (levelNext < SC_FOLDLEVELBASE) { - levelNext = SC_FOLDLEVELBASE; - } + if (levelNext < SC_FOLDLEVELBASE) + levelNext = SC_FOLDLEVELBASE; } - if (atEOL) { - if(elseFound) + + if (atEOL) + { + if (elseFound) + { levelNext++; - elseFound = false; + elseFound = false; + } int levelUse = levelCurrent; int lev = levelUse | levelNext << 16; @@ -337,21 +453,23 @@ static void FoldMySQLDoc(unsigned int startPos, int length, int initStyle, lev |= SC_FOLDLEVELWHITEFLAG; if (levelUse < levelNext) lev |= SC_FOLDLEVELHEADERFLAG; - if (lev != styler.LevelAt(lineCurrent)) { + if (lev != styler.LevelAt(lineCurrent)) styler.SetLevel(lineCurrent, lev); - } + lineCurrent++; levelCurrent = levelNext; visibleChars = 0; endFound = false; whenFound = false; } - if (!isspacechar(ch)) { + + if (!isspacechar(static_cast<char>(sc.ch))) visibleChars++; - } } } +//-------------------------------------------------------------------------------------------------- + static const char * const mysqlWordListDesc[] = { "Major Keywords", "Keywords", @@ -361,7 +479,8 @@ static const char * const mysqlWordListDesc[] = { "Procedure keywords", "User Keywords 1", "User Keywords 2", - "User Keywords 3" + "User Keywords 3", + 0 }; LexerModule lmMySQL(SCLEX_MYSQL, ColouriseMySQLDoc, "mysql", FoldMySQLDoc, mysqlWordListDesc); |