OSDN Git Service

Commit DialogBox compile Okay
[tortoisegit/TortoiseGitJp.git] / ext / scintilla / src / LexSQL.cxx
1 // Scintilla source code edit control\r
2 /** @file LexSQL.cxx\r
3  ** Lexer for SQL, including PL/SQL and SQL*Plus.\r
4  **/\r
5 // Copyright 1998-2005 by Neil Hodgson <neilh@scintilla.org>\r
6 // The License.txt file describes the conditions under which this software may be distributed.\r
7 \r
8 #include <stdlib.h>\r
9 #include <string.h>\r
10 #include <ctype.h>\r
11 #include <stdio.h>\r
12 #include <stdarg.h>\r
13 \r
14 #include "Platform.h"\r
15 \r
16 #include "PropSet.h"\r
17 #include "Accessor.h"\r
18 #include "StyleContext.h"\r
19 #include "KeyWords.h"\r
20 #include "Scintilla.h"\r
21 #include "SciLexer.h"\r
22 \r
23 #ifdef SCI_NAMESPACE\r
24 using namespace Scintilla;\r
25 #endif\r
26 \r
27 static inline bool IsAWordChar(int ch) {\r
28         return (ch < 0x80) && (isalnum(ch) || ch == '_');\r
29 }\r
30 \r
31 static inline bool IsAWordStart(int ch) {\r
32         return (ch < 0x80) && (isalpha(ch) || ch == '_');\r
33 }\r
34 \r
35 static inline bool IsADoxygenChar(int ch) {\r
36         return (islower(ch) || ch == '$' || ch == '@' ||\r
37                 ch == '\\' || ch == '&' || ch == '<' ||\r
38                 ch == '>' || ch == '#' || ch == '{' ||\r
39                 ch == '}' || ch == '[' || ch == ']');\r
40 }\r
41 \r
42 static inline bool IsANumberChar(int ch) {\r
43         // Not exactly following number definition (several dots are seen as OK, etc.)\r
44         // but probably enough in most cases.\r
45         return (ch < 0x80) &&\r
46                 (isdigit(ch) || toupper(ch) == 'E' ||\r
47              ch == '.' || ch == '-' || ch == '+');\r
48 }\r
49 \r
50 static void ColouriseSQLDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],\r
51                             Accessor &styler) {\r
52 \r
53         WordList &keywords1 = *keywordlists[0];\r
54         WordList &keywords2 = *keywordlists[1];\r
55         WordList &kw_pldoc = *keywordlists[2];\r
56         WordList &kw_sqlplus = *keywordlists[3];\r
57         WordList &kw_user1 = *keywordlists[4];\r
58         WordList &kw_user2 = *keywordlists[5];\r
59         WordList &kw_user3 = *keywordlists[6];\r
60         WordList &kw_user4 = *keywordlists[7];\r
61 \r
62         StyleContext sc(startPos, length, initStyle, styler);\r
63 \r
64         bool sqlBackslashEscapes = styler.GetPropertyInt("sql.backslash.escapes", 0) != 0;\r
65         bool sqlBackticksIdentifier = styler.GetPropertyInt("lexer.sql.backticks.identifier", 0) != 0;\r
66         int styleBeforeDCKeyword = SCE_SQL_DEFAULT;\r
67         for (; sc.More(); sc.Forward()) {\r
68                 // Determine if the current state should terminate.\r
69                 switch (sc.state) {\r
70                 case SCE_SQL_OPERATOR:\r
71                         sc.SetState(SCE_SQL_DEFAULT);\r
72                         break;\r
73                 case SCE_SQL_NUMBER:\r
74                         // We stop the number definition on non-numerical non-dot non-eE non-sign char\r
75                         if (!IsANumberChar(sc.ch)) {\r
76                                 sc.SetState(SCE_SQL_DEFAULT);\r
77                         }\r
78                         break;\r
79                 case SCE_SQL_IDENTIFIER:\r
80                         if (!IsAWordChar(sc.ch)) {\r
81                                 int nextState = SCE_SQL_DEFAULT;\r
82                                 char s[1000];\r
83                                 sc.GetCurrentLowered(s, sizeof(s));\r
84                                 if (keywords1.InList(s)) {\r
85                                         sc.ChangeState(SCE_SQL_WORD);\r
86                                 } else if (keywords2.InList(s)) {\r
87                                         sc.ChangeState(SCE_SQL_WORD2);\r
88                                 } else if (kw_sqlplus.InListAbbreviated(s, '~')) {\r
89                                         sc.ChangeState(SCE_SQL_SQLPLUS);\r
90                                         if (strncmp(s, "rem", 3) == 0) {\r
91                                                 nextState = SCE_SQL_SQLPLUS_COMMENT;\r
92                                         } else if (strncmp(s, "pro", 3) == 0) {\r
93                                                 nextState = SCE_SQL_SQLPLUS_PROMPT;\r
94                                         }\r
95                                 } else if (kw_user1.InList(s)) {\r
96                                         sc.ChangeState(SCE_SQL_USER1);\r
97                                 } else if (kw_user2.InList(s)) {\r
98                                         sc.ChangeState(SCE_SQL_USER2);\r
99                                 } else if (kw_user3.InList(s)) {\r
100                                         sc.ChangeState(SCE_SQL_USER3);\r
101                                 } else if (kw_user4.InList(s)) {\r
102                                         sc.ChangeState(SCE_SQL_USER4);\r
103                                 }\r
104                                 sc.SetState(nextState);\r
105                         }\r
106                         break;\r
107                 case SCE_SQL_QUOTEDIDENTIFIER:\r
108                         if (sc.ch == 0x60) {\r
109                                 if (sc.chNext == 0x60) {\r
110                                         sc.Forward();   // Ignore it\r
111                                 } else {\r
112                                         sc.ForwardSetState(SCE_SQL_DEFAULT);\r
113                                 }\r
114                         }\r
115                         break;\r
116                 case SCE_SQL_COMMENT:\r
117                         if (sc.Match('*', '/')) {\r
118                                 sc.Forward();\r
119                                 sc.ForwardSetState(SCE_SQL_DEFAULT);\r
120                         }\r
121                         break;\r
122                 case SCE_SQL_COMMENTDOC:\r
123                         if (sc.Match('*', '/')) {\r
124                                 sc.Forward();\r
125                                 sc.ForwardSetState(SCE_SQL_DEFAULT);\r
126                         } else if (sc.ch == '@' || sc.ch == '\\') { // Doxygen support\r
127                                 // Verify that we have the conditions to mark a comment-doc-keyword\r
128                                 if ((IsASpace(sc.chPrev) || sc.chPrev == '*') && (!IsASpace(sc.chNext))) {\r
129                                         styleBeforeDCKeyword = SCE_SQL_COMMENTDOC;\r
130                                         sc.SetState(SCE_SQL_COMMENTDOCKEYWORD);\r
131                                 }\r
132                         }\r
133                         break;\r
134                 case SCE_SQL_COMMENTLINE:\r
135                 case SCE_SQL_COMMENTLINEDOC:\r
136                 case SCE_SQL_SQLPLUS_COMMENT:\r
137                 case SCE_SQL_SQLPLUS_PROMPT:\r
138                         if (sc.atLineStart) {\r
139                                 sc.SetState(SCE_SQL_DEFAULT);\r
140                         }\r
141                         break;\r
142                 case SCE_SQL_COMMENTDOCKEYWORD:\r
143                         if ((styleBeforeDCKeyword == SCE_SQL_COMMENTDOC) && sc.Match('*', '/')) {\r
144                                 sc.ChangeState(SCE_SQL_COMMENTDOCKEYWORDERROR);\r
145                                 sc.Forward();\r
146                                 sc.ForwardSetState(SCE_SQL_DEFAULT);\r
147                         } else if (!IsADoxygenChar(sc.ch)) {\r
148                                 char s[100];\r
149                                 sc.GetCurrentLowered(s, sizeof(s));\r
150                                 if (!isspace(sc.ch) || !kw_pldoc.InList(s + 1)) {\r
151                                         sc.ChangeState(SCE_SQL_COMMENTDOCKEYWORDERROR);\r
152                                 }\r
153                                 sc.SetState(styleBeforeDCKeyword);\r
154                         }\r
155                         break;\r
156                 case SCE_SQL_CHARACTER:\r
157                         if (sqlBackslashEscapes && sc.ch == '\\') {\r
158                                 sc.Forward();\r
159                         } else if (sc.ch == '\'') {\r
160                                 if (sc.chNext == '\"') {\r
161                                         sc.Forward();\r
162                                 } else {\r
163                                         sc.ForwardSetState(SCE_SQL_DEFAULT);\r
164                                 }\r
165                         }\r
166                         break;\r
167                 case SCE_SQL_STRING:\r
168                         if (sc.ch == '\\') {\r
169                                 // Escape sequence\r
170                                 sc.Forward();\r
171                         } else if (sc.ch == '\"') {\r
172                                 if (sc.chNext == '\"') {\r
173                                         sc.Forward();\r
174                                 } else {\r
175                                         sc.ForwardSetState(SCE_SQL_DEFAULT);\r
176                                 }\r
177                         }\r
178                         break;\r
179                 }\r
180 \r
181                 // Determine if a new state should be entered.\r
182                 if (sc.state == SCE_SQL_DEFAULT) {\r
183                         if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {\r
184                                 sc.SetState(SCE_SQL_NUMBER);\r
185                         } else if (IsAWordStart(sc.ch)) {\r
186                                 sc.SetState(SCE_SQL_IDENTIFIER);\r
187                         } else if (sc.ch == 0x60 && sqlBackticksIdentifier) {\r
188                                 sc.SetState(SCE_SQL_QUOTEDIDENTIFIER);\r
189                         } else if (sc.Match('/', '*')) {\r
190                                 if (sc.Match("/**") || sc.Match("/*!")) {       // Support of Doxygen doc. style\r
191                                         sc.SetState(SCE_SQL_COMMENTDOC);\r
192                                 } else {\r
193                                         sc.SetState(SCE_SQL_COMMENT);\r
194                                 }\r
195                                 sc.Forward();   // Eat the * so it isn't used for the end of the comment\r
196                         } else if (sc.Match('-', '-')) {\r
197                                 // MySQL requires a space or control char after --\r
198                                 // http://dev.mysql.com/doc/mysql/en/ansi-diff-comments.html\r
199                                 // Perhaps we should enforce that with proper property:\r
200 //~                     } else if (sc.Match("-- ")) {\r
201                                 sc.SetState(SCE_SQL_COMMENTLINE);\r
202                         } else if (sc.ch == '#') {\r
203                                 sc.SetState(SCE_SQL_COMMENTLINEDOC);\r
204                         } else if (sc.ch == '\'') {\r
205                                 sc.SetState(SCE_SQL_CHARACTER);\r
206                         } else if (sc.ch == '\"') {\r
207                                 sc.SetState(SCE_SQL_STRING);\r
208                         } else if (isoperator(static_cast<char>(sc.ch))) {\r
209                                 sc.SetState(SCE_SQL_OPERATOR);\r
210                         }\r
211                 }\r
212         }\r
213         sc.Complete();\r
214 }\r
215 \r
216 static bool IsStreamCommentStyle(int style) {\r
217         return style == SCE_SQL_COMMENT ||\r
218                style == SCE_SQL_COMMENTDOC ||\r
219                style == SCE_SQL_COMMENTDOCKEYWORD ||\r
220                style == SCE_SQL_COMMENTDOCKEYWORDERROR;\r
221 }\r
222 \r
223 // Store both the current line's fold level and the next lines in the\r
224 // level store to make it easy to pick up with each increment.\r
225 static void FoldSQLDoc(unsigned int startPos, int length, int initStyle,\r
226                             WordList *[], Accessor &styler) {\r
227         bool foldComment = styler.GetPropertyInt("fold.comment") != 0;\r
228         bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;\r
229         bool foldOnlyBegin = styler.GetPropertyInt("fold.sql.only.begin", 0) != 0;\r
230 \r
231         unsigned int endPos = startPos + length;\r
232         int visibleChars = 0;\r
233         int lineCurrent = styler.GetLine(startPos);\r
234         int levelCurrent = SC_FOLDLEVELBASE;\r
235         if (lineCurrent > 0) {\r
236                 levelCurrent = styler.LevelAt(lineCurrent - 1) >> 16;\r
237         }\r
238         int levelNext = levelCurrent;\r
239         char chNext = styler[startPos];\r
240         int styleNext = styler.StyleAt(startPos);\r
241         int style = initStyle;\r
242         bool endFound = false;\r
243         for (unsigned int i = startPos; i < endPos; i++) {\r
244                 char ch = chNext;\r
245                 chNext = styler.SafeGetCharAt(i + 1);\r
246                 int stylePrev = style;\r
247                 style = styleNext;\r
248                 styleNext = styler.StyleAt(i + 1);\r
249                 bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');\r
250                 if (foldComment && IsStreamCommentStyle(style)) {\r
251                         if (!IsStreamCommentStyle(stylePrev)) {\r
252                                 levelNext++;\r
253                         } else if (!IsStreamCommentStyle(styleNext) && !atEOL) {\r
254                                 // Comments don't end at end of line and the next character may be unstyled.\r
255                                 levelNext--;\r
256                         }\r
257                 }\r
258                 if (foldComment && (style == SCE_SQL_COMMENTLINE)) {\r
259                         // MySQL needs -- comments to be followed by space or control char\r
260                         if ((ch == '-') && (chNext == '-')) {\r
261                                 char chNext2 = styler.SafeGetCharAt(i + 2);\r
262                                 char chNext3 = styler.SafeGetCharAt(i + 3);\r
263                                 if (chNext2 == '{' || chNext3 == '{') {\r
264                                         levelNext++;\r
265                                 } else if (chNext2 == '}' || chNext3 == '}') {\r
266                                         levelNext--;\r
267                                 }\r
268                         }\r
269                 }\r
270                 if (style == SCE_SQL_OPERATOR) {\r
271                         if (ch == '(') {\r
272                                 levelNext++;\r
273                         } else if (ch == ')') {\r
274                                 levelNext--;\r
275                         }\r
276                 }\r
277                 // If new keyword (cannot trigger on elseif or nullif, does less tests)\r
278                 if (style == SCE_SQL_WORD && stylePrev != SCE_SQL_WORD) {\r
279                         const int MAX_KW_LEN = 6;       // Maximum length of folding keywords\r
280                         char s[MAX_KW_LEN + 2];\r
281                         unsigned int j = 0;\r
282                         for (; j < MAX_KW_LEN + 1; j++) {\r
283                                 if (!iswordchar(styler[i + j])) {\r
284                                         break;\r
285                                 }\r
286                                 s[j] = static_cast<char>(tolower(styler[i + j]));\r
287                         }\r
288                         if (j == MAX_KW_LEN + 1) {\r
289                                 // Keyword too long, don't test it\r
290                                 s[0] = '\0';\r
291                         } else {\r
292                                 s[j] = '\0';\r
293                         }\r
294                         if ((!foldOnlyBegin) && (strcmp(s, "if") == 0 || strcmp(s, "loop") == 0)) {\r
295                                 if (endFound) {\r
296                                         // ignore\r
297                                         endFound = false;\r
298                                 } else {\r
299                                         levelNext++;\r
300                                 }\r
301                         } else if (strcmp(s, "begin") == 0) {\r
302                                 levelNext++;\r
303                         } else if (strcmp(s, "end") == 0 ||\r
304                                                 // DROP TABLE IF EXISTS or CREATE TABLE IF NOT EXISTS\r
305                                                 strcmp(s, "exists") == 0) {\r
306                                 endFound = true;\r
307                                 levelNext--;\r
308                                 if (levelNext < SC_FOLDLEVELBASE) {\r
309                                         levelNext = SC_FOLDLEVELBASE;\r
310                                 }\r
311                         }\r
312                 }\r
313                 if (atEOL) {\r
314                         int levelUse = levelCurrent;\r
315                         int lev = levelUse | levelNext << 16;\r
316                         if (visibleChars == 0 && foldCompact)\r
317                                 lev |= SC_FOLDLEVELWHITEFLAG;\r
318                         if (levelUse < levelNext)\r
319                                 lev |= SC_FOLDLEVELHEADERFLAG;\r
320                         if (lev != styler.LevelAt(lineCurrent)) {\r
321                                 styler.SetLevel(lineCurrent, lev);\r
322                         }\r
323                         lineCurrent++;\r
324                         levelCurrent = levelNext;\r
325                         visibleChars = 0;\r
326                         endFound = false;\r
327                 }\r
328                 if (!isspacechar(ch)) {\r
329                         visibleChars++;\r
330                 }\r
331         }\r
332 }\r
333 \r
334 static const char * const sqlWordListDesc[] = {\r
335         "Keywords",\r
336         "Database Objects",\r
337         "PLDoc",\r
338         "SQL*Plus",\r
339         "User Keywords 1",\r
340         "User Keywords 2",\r
341         "User Keywords 3",\r
342         "User Keywords 4",\r
343         0\r
344 };\r
345 \r
346 LexerModule lmSQL(SCLEX_SQL, ColouriseSQLDoc, "sql", FoldSQLDoc, sqlWordListDesc);\r