OSDN Git Service

Commit DialogBox compile Okay
[tortoisegit/TortoiseGitJp.git] / ext / scintilla / src / LexMySQL.cxx
1 // Scintilla source code edit control\r
2 /** @file LexMySQL.cxx\r
3  ** Lexer for MySQL\r
4  **/\r
5 // Adopted from LexSQL.cxx by Anders Karlsson <anders@mysql.com>\r
6 // Original work by Neil Hodgson <neilh@scintilla.org>\r
7 // Copyright 1998-2005 by Neil Hodgson <neilh@scintilla.org>\r
8 // The License.txt file describes the conditions under which this software may be distributed.\r
9 \r
10 #include <stdlib.h>\r
11 #include <string.h>\r
12 #include <ctype.h>\r
13 #include <stdio.h>\r
14 #include <stdarg.h>\r
15 \r
16 #include "Platform.h"\r
17 \r
18 #include "PropSet.h"\r
19 #include "Accessor.h"\r
20 #include "StyleContext.h"\r
21 #include "KeyWords.h"\r
22 #include "Scintilla.h"\r
23 #include "SciLexer.h"\r
24 \r
25 #ifdef SCI_NAMESPACE\r
26 using namespace Scintilla;\r
27 #endif\r
28 \r
29 static inline bool IsAWordChar(int ch) {\r
30         return (ch < 0x80) && (isalnum(ch) || ch == '_');\r
31 }\r
32 \r
33 static inline bool IsAWordStart(int ch) {\r
34         return (ch < 0x80) && (isalpha(ch) || ch == '_');\r
35 }\r
36 \r
37 static inline bool IsADoxygenChar(int ch) {\r
38         return (islower(ch) || ch == '$' || ch == '@' ||\r
39                 ch == '\\' || ch == '&' || ch == '<' ||\r
40                 ch == '>' || ch == '#' || ch == '{' ||\r
41                 ch == '}' || ch == '[' || ch == ']');\r
42 }\r
43 \r
44 static inline bool IsANumberChar(int ch) {\r
45         // Not exactly following number definition (several dots are seen as OK, etc.)\r
46         // but probably enough in most cases.\r
47         return (ch < 0x80) &&\r
48                 (isdigit(ch) || toupper(ch) == 'E' ||\r
49              ch == '.' || ch == '-' || ch == '+');\r
50 }\r
51 \r
52 static void ColouriseMySQLDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],\r
53                             Accessor &styler) {\r
54 \r
55         WordList &major_keywords = *keywordlists[0];\r
56         WordList &keywords = *keywordlists[1];\r
57         WordList &database_objects = *keywordlists[2];\r
58         WordList &functions = *keywordlists[3];\r
59         WordList &system_variables = *keywordlists[4];\r
60         WordList &procedure_keywords = *keywordlists[5];\r
61         WordList &kw_user1 = *keywordlists[6];\r
62         WordList &kw_user2 = *keywordlists[7];\r
63         WordList &kw_user3 = *keywordlists[8];\r
64 \r
65         StyleContext sc(startPos, length, initStyle, styler);\r
66 \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_MYSQL_OPERATOR:\r
71                         sc.SetState(SCE_MYSQL_DEFAULT);\r
72                         break;\r
73                 case SCE_MYSQL_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_MYSQL_DEFAULT);\r
77                         }\r
78                         break;\r
79                 case SCE_MYSQL_IDENTIFIER:\r
80                         if (!IsAWordChar(sc.ch)) {\r
81                                 int nextState = SCE_MYSQL_DEFAULT;\r
82                                 char s[1000];\r
83                                 sc.GetCurrentLowered(s, sizeof(s));\r
84                                 if (major_keywords.InList(s)) {\r
85                                         sc.ChangeState(SCE_MYSQL_MAJORKEYWORD);\r
86                                 } else if (keywords.InList(s)) {\r
87                                         sc.ChangeState(SCE_MYSQL_KEYWORD);\r
88                                 } else if (database_objects.InList(s)) {\r
89                                         sc.ChangeState(SCE_MYSQL_DATABASEOBJECT);\r
90                                 } else if (functions.InList(s)) {\r
91                                         sc.ChangeState(SCE_MYSQL_FUNCTION);\r
92                                 } else if (procedure_keywords.InList(s)) {\r
93                                         sc.ChangeState(SCE_MYSQL_PROCEDUREKEYWORD);\r
94                                 } else if (kw_user1.InList(s)) {\r
95                                         sc.ChangeState(SCE_MYSQL_USER1);\r
96                                 } else if (kw_user2.InList(s)) {\r
97                                         sc.ChangeState(SCE_MYSQL_USER2);\r
98                                 } else if (kw_user3.InList(s)) {\r
99                                         sc.ChangeState(SCE_MYSQL_USER3);\r
100                                 }\r
101                                 sc.SetState(nextState);\r
102                         }\r
103                         break;\r
104                 case SCE_MYSQL_VARIABLE:\r
105                         if (!IsAWordChar(sc.ch)) {\r
106                                 sc.SetState(SCE_MYSQL_DEFAULT);\r
107                         }\r
108                         break;\r
109                 case SCE_MYSQL_SYSTEMVARIABLE:\r
110                         if (!IsAWordChar(sc.ch)) {\r
111                                 char s[1000];\r
112                                 sc.GetCurrentLowered(s, sizeof(s));\r
113 // Check for known system variables here.\r
114                                 if (system_variables.InList(&s[2])) {\r
115                                         sc.ChangeState(SCE_MYSQL_KNOWNSYSTEMVARIABLE);\r
116                                 }\r
117                                 sc.SetState(SCE_MYSQL_DEFAULT);\r
118                         }\r
119                         break;\r
120                 case SCE_MYSQL_QUOTEDIDENTIFIER:\r
121                         if (sc.ch == 0x60) {\r
122                                 if (sc.chNext == 0x60) {\r
123                                         sc.Forward();   // Ignore it\r
124                                 } else {\r
125                                         sc.ForwardSetState(SCE_MYSQL_DEFAULT);\r
126                                 }\r
127                         }\r
128                         break;\r
129                 case SCE_MYSQL_COMMENT:\r
130                         if (sc.Match('*', '/')) {\r
131                                 sc.Forward();\r
132                                 sc.ForwardSetState(SCE_MYSQL_DEFAULT);\r
133                         }\r
134                         break;\r
135                 case SCE_MYSQL_COMMENTLINE:\r
136                         if (sc.atLineStart) {\r
137                                 sc.SetState(SCE_MYSQL_DEFAULT);\r
138                         }\r
139                         break;\r
140                 case SCE_MYSQL_SQSTRING:\r
141                         if (sc.ch == '\\') {\r
142                                 // Escape sequence\r
143                                 sc.Forward();\r
144                         } else if (sc.ch == '\'') {\r
145                                 if (sc.chNext == '\'') {\r
146                                         sc.Forward();\r
147                                 } else {\r
148                                         sc.ChangeState(SCE_MYSQL_STRING);\r
149                                         sc.ForwardSetState(SCE_MYSQL_DEFAULT);\r
150                                 }\r
151                         }\r
152                         break;\r
153                 case SCE_MYSQL_DQSTRING:\r
154                         if (sc.ch == '\\') {\r
155                                 // Escape sequence\r
156                                 sc.Forward();\r
157                         } else if (sc.ch == '\"') {\r
158                                 if (sc.chNext == '\"') {\r
159                                         sc.Forward();\r
160                                 } else {\r
161                                         sc.ChangeState(SCE_MYSQL_STRING);\r
162                                         sc.ForwardSetState(SCE_MYSQL_DEFAULT);\r
163                                 }\r
164                         }\r
165                         break;\r
166                 }\r
167 \r
168                 // Determine if a new state should be entered.\r
169                 if (sc.state == SCE_MYSQL_DEFAULT) {\r
170                         if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {\r
171                                 sc.SetState(SCE_MYSQL_NUMBER);\r
172                         } else if (IsAWordStart(sc.ch)) {\r
173                                 sc.SetState(SCE_MYSQL_IDENTIFIER);\r
174 // Note that the order of SYSTEMVARIABLE and VARIABLE is important here.\r
175                         } else if (sc.ch == 0x40 && sc.chNext == 0x40) {\r
176                                 sc.SetState(SCE_MYSQL_SYSTEMVARIABLE);\r
177                                 sc.Forward(); // Skip past the second at-sign.\r
178                         } else if (sc.ch == 0x40) {\r
179                                 sc.SetState(SCE_MYSQL_VARIABLE);\r
180                         } else if (sc.ch == 0x60) {\r
181                                 sc.SetState(SCE_MYSQL_QUOTEDIDENTIFIER);\r
182                         } else if (sc.Match('/', '*')) {\r
183                                 sc.SetState(SCE_MYSQL_COMMENT);\r
184                                 sc.Forward();   // Eat the * so it isn't used for the end of the comment\r
185                         } else if (sc.Match('-', '-') || sc.Match('#')) {\r
186                                 sc.SetState(SCE_MYSQL_COMMENTLINE);\r
187                         } else if (sc.ch == '\'') {\r
188                                 sc.SetState(SCE_MYSQL_SQSTRING);\r
189                         } else if (sc.ch == '\"') {\r
190                                 sc.SetState(SCE_MYSQL_DQSTRING);\r
191                         } else if (isoperator(static_cast<char>(sc.ch))) {\r
192                                 sc.SetState(SCE_MYSQL_OPERATOR);\r
193                         }\r
194                 }\r
195         }\r
196         sc.Complete();\r
197 }\r
198 \r
199 static bool IsStreamCommentStyle(int style) {\r
200         return style == SCE_MYSQL_COMMENT;\r
201 }\r
202 \r
203 // Store both the current line's fold level and the next lines in the\r
204 // level store to make it easy to pick up with each increment.\r
205 static void FoldMySQLDoc(unsigned int startPos, int length, int initStyle,\r
206                             WordList *[], Accessor &styler) {\r
207         bool foldComment = styler.GetPropertyInt("fold.comment") != 0;\r
208         bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;\r
209         bool foldOnlyBegin = styler.GetPropertyInt("fold.sql.only.begin", 0) != 0;\r
210 \r
211         unsigned int endPos = startPos + length;\r
212         int visibleChars = 0;\r
213         int lineCurrent = styler.GetLine(startPos);\r
214         int levelCurrent = SC_FOLDLEVELBASE;\r
215         if (lineCurrent > 0) {\r
216                 levelCurrent = styler.LevelAt(lineCurrent - 1) >> 16;\r
217         }\r
218         int levelNext = levelCurrent;\r
219         char chNext = styler[startPos];\r
220         int styleNext = styler.StyleAt(startPos);\r
221         int style = initStyle;\r
222         bool endFound = false;\r
223         bool whenFound = false;\r
224         bool elseFound = false;\r
225         for (unsigned int i = startPos; i < endPos; i++) {\r
226                 char ch = chNext;\r
227                 chNext = styler.SafeGetCharAt(i + 1);\r
228                 int stylePrev = style;\r
229                 style = styleNext;\r
230                 styleNext = styler.StyleAt(i + 1);\r
231                 bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');\r
232                 if (foldComment && IsStreamCommentStyle(style)) {\r
233                         if (!IsStreamCommentStyle(stylePrev)) {\r
234                                 levelNext++;\r
235                         } else if (!IsStreamCommentStyle(styleNext) && !atEOL) {\r
236                                 // Comments don't end at end of line and the next character may be unstyled.\r
237                                 levelNext--;\r
238                         }\r
239                 }\r
240                 if (foldComment && (style == SCE_MYSQL_COMMENTLINE)) {\r
241                         // MySQL needs -- comments to be followed by space or control char\r
242                         if ((ch == '-') && (chNext == '-')) {\r
243                                 char chNext2 = styler.SafeGetCharAt(i + 2);\r
244                                 char chNext3 = styler.SafeGetCharAt(i + 3);\r
245                                 if (chNext2 == '{' || chNext3 == '{') {\r
246                                         levelNext++;\r
247                                 } else if (chNext2 == '}' || chNext3 == '}') {\r
248                                         levelNext--;\r
249                                 }\r
250                         }\r
251                 }\r
252                 if (style == SCE_MYSQL_OPERATOR) {\r
253                         if (ch == '(') {\r
254                                 levelNext++;\r
255                         } else if (ch == ')') {\r
256                                 levelNext--;\r
257                         }\r
258                 }\r
259 \r
260 // Style new keywords here.\r
261                 if ((style == SCE_MYSQL_MAJORKEYWORD && stylePrev != SCE_MYSQL_MAJORKEYWORD)\r
262                   || (style == SCE_MYSQL_KEYWORD && stylePrev != SCE_MYSQL_KEYWORD)\r
263                   || (style == SCE_MYSQL_PROCEDUREKEYWORD && stylePrev != SCE_MYSQL_PROCEDUREKEYWORD)) {\r
264                         const int MAX_KW_LEN = 6;       // Maximum length of folding keywords\r
265                         char s[MAX_KW_LEN + 2];\r
266                         unsigned int j = 0;\r
267                         for (; j < MAX_KW_LEN + 1; j++) {\r
268                                 if (!iswordchar(styler[i + j])) {\r
269                                         break;\r
270                                 }\r
271                                 s[j] = static_cast<char>(tolower(styler[i + j]));\r
272                         }\r
273                         if (j == MAX_KW_LEN + 1) {\r
274                                 // Keyword too long, don't test it\r
275                                 s[0] = '\0';\r
276                         } else {\r
277                                 s[j] = '\0';\r
278                         }\r
279                         if (!foldOnlyBegin && endFound && (strcmp(s, "if") == 0 || strcmp(s, "while") == 0 || strcmp(s, "loop") == 0)) {\r
280                                 endFound = false;\r
281                                 levelNext--;\r
282                                 if (levelNext < SC_FOLDLEVELBASE) {\r
283                                         levelNext = SC_FOLDLEVELBASE;\r
284                                 }\r
285 // Note that else is special here. It may or may be followed by an if then, but in aly case the level stays the\r
286 // same. When followed by a if .. then, the level will be increased later, if not, at eol.\r
287                         } else if (!foldOnlyBegin && strcmp(s, "else") == 0) {\r
288                                 levelNext--;\r
289                                 elseFound = true;\r
290                         } else if (!foldOnlyBegin && strcmp(s, "then") == 0) {\r
291                                 if(whenFound) {\r
292                                         whenFound = false;\r
293                                 } else {\r
294                                         levelNext++;\r
295                                 }\r
296                         } else if (strcmp(s, "if") == 0) {\r
297                                 elseFound = false;\r
298                         } else if (strcmp(s, "when") == 0) {\r
299                                 whenFound = true;\r
300                         } else if (strcmp(s, "begin") == 0) {\r
301                                 levelNext++;\r
302                         } else if (!foldOnlyBegin && (strcmp(s, "loop") == 0 || strcmp(s, "repeat") == 0\r
303                           || strcmp(s, "while") == 0)) {\r
304                                 if(endFound) {\r
305                                         endFound = false;\r
306                                 } else {\r
307                                         levelNext++;\r
308                                 }\r
309                         } else if (strcmp(s, "end") == 0) {\r
310 // Multiple END in a row are counted multiple times!\r
311                                 if (endFound) {\r
312                                         levelNext--;\r
313                                         if (levelNext < SC_FOLDLEVELBASE) {\r
314                                                 levelNext = SC_FOLDLEVELBASE;\r
315                                         }\r
316                                 }\r
317                                 endFound = true;\r
318                                 whenFound = false;\r
319                         }\r
320                 }\r
321 // Handle this for a trailing end withiut an if / while etc, as in the case of a begin.\r
322                 if (endFound) {\r
323                         endFound = false;\r
324                         levelNext--;\r
325                         if (levelNext < SC_FOLDLEVELBASE) {\r
326                                 levelNext = SC_FOLDLEVELBASE;\r
327                         }\r
328                 }\r
329                 if (atEOL) {\r
330                         if(elseFound)\r
331                                 levelNext++;\r
332                         elseFound = false;\r
333 \r
334                         int levelUse = levelCurrent;\r
335                         int lev = levelUse | levelNext << 16;\r
336                         if (visibleChars == 0 && foldCompact)\r
337                                 lev |= SC_FOLDLEVELWHITEFLAG;\r
338                         if (levelUse < levelNext)\r
339                                 lev |= SC_FOLDLEVELHEADERFLAG;\r
340                         if (lev != styler.LevelAt(lineCurrent)) {\r
341                                 styler.SetLevel(lineCurrent, lev);\r
342                         }\r
343                         lineCurrent++;\r
344                         levelCurrent = levelNext;\r
345                         visibleChars = 0;\r
346                         endFound = false;\r
347                         whenFound = false;\r
348                 }\r
349                 if (!isspacechar(ch)) {\r
350                         visibleChars++;\r
351                 }\r
352         }\r
353 }\r
354 \r
355 static const char * const mysqlWordListDesc[] = {\r
356         "Major Keywords",\r
357         "Keywords",\r
358         "Database Objects",\r
359         "Functions",\r
360         "System Variables",\r
361         "Procedure keywords",\r
362         "User Keywords 1",\r
363         "User Keywords 2",\r
364         "User Keywords 3"\r
365 };\r
366 \r
367 LexerModule lmMySQL(SCLEX_MYSQL, ColouriseMySQLDoc, "mysql", FoldMySQLDoc, mysqlWordListDesc);\r