OSDN Git Service

Commit DialogBox compile Okay
[tortoisegit/TortoiseGitJp.git] / ext / scintilla / src / LexCPP.cxx
1 // Scintilla source code edit control\r
2 /** @file LexCPP.cxx\r
3  ** Lexer for C++, C, Java, and JavaScript.\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 #include "CharacterSet.h"\r
23 \r
24 #ifdef SCI_NAMESPACE\r
25 using namespace Scintilla;\r
26 #endif\r
27 \r
28 static bool IsSpaceEquiv(int state) {\r
29         return (state <= SCE_C_COMMENTDOC) ||\r
30                 // including SCE_C_DEFAULT, SCE_C_COMMENT, SCE_C_COMMENTLINE\r
31                 (state == SCE_C_COMMENTLINEDOC) || (state == SCE_C_COMMENTDOCKEYWORD) ||\r
32                 (state == SCE_C_COMMENTDOCKEYWORDERROR);\r
33 }\r
34 \r
35 // Preconditions: sc.currentPos points to a character after '+' or '-'.\r
36 // The test for pos reaching 0 should be redundant,\r
37 // and is in only for safety measures.\r
38 // Limitation: this code will give the incorrect answer for code like\r
39 // a = b+++/ptn/...\r
40 // Putting a space between the '++' post-inc operator and the '+' binary op\r
41 // fixes this, and is highly recommended for readability anyway.\r
42 static bool FollowsPostfixOperator(StyleContext &sc, Accessor &styler) {\r
43         int pos = (int) sc.currentPos;\r
44         while (--pos > 0) {\r
45                 char ch = styler[pos];\r
46                 if (ch == '+' || ch == '-') {\r
47                         return styler[pos - 1] == ch;\r
48                 }\r
49         }\r
50         return false;\r
51 }\r
52 \r
53 static void ColouriseCppDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],\r
54                             Accessor &styler, bool caseSensitive) {\r
55 \r
56         WordList &keywords = *keywordlists[0];\r
57         WordList &keywords2 = *keywordlists[1];\r
58         WordList &keywords3 = *keywordlists[2];\r
59         WordList &keywords4 = *keywordlists[3];\r
60 \r
61         bool stylingWithinPreprocessor = styler.GetPropertyInt("styling.within.preprocessor") != 0;\r
62 \r
63         CharacterSet setOKBeforeRE(CharacterSet::setNone, "([{=,:;!%^&*|?~+-");\r
64         CharacterSet setCouldBePostOp(CharacterSet::setNone, "+-");\r
65 \r
66         CharacterSet setDoxygen(CharacterSet::setAlpha, "$@\\&<>#{}[]");\r
67 \r
68         CharacterSet setWordStart(CharacterSet::setAlpha, "_", 0x80, true);\r
69         CharacterSet setWord(CharacterSet::setAlphaNum, "._", 0x80, true);\r
70         if (styler.GetPropertyInt("lexer.cpp.allow.dollars", 1) != 0) {\r
71                 setWordStart.Add('$');\r
72                 setWord.Add('$');\r
73         }\r
74 \r
75         int chPrevNonWhite = ' ';\r
76         int visibleChars = 0;\r
77         bool lastWordWasUUID = false;\r
78         int styleBeforeDCKeyword = SCE_C_DEFAULT;\r
79         bool continuationLine = false;\r
80 \r
81         if (initStyle == SCE_C_PREPROCESSOR) {\r
82                 // Set continuationLine if last character of previous line is '\'\r
83                 int lineCurrent = styler.GetLine(startPos);\r
84                 if (lineCurrent > 0) {\r
85                         int chBack = styler.SafeGetCharAt(startPos-1, 0);\r
86                         int chBack2 = styler.SafeGetCharAt(startPos-2, 0);\r
87                         int lineEndChar = '!';\r
88                         if (chBack2 == '\r' && chBack == '\n') {\r
89                                 lineEndChar = styler.SafeGetCharAt(startPos-3, 0);\r
90                         } else if (chBack == '\n' || chBack == '\r') {\r
91                                 lineEndChar = chBack2;\r
92                         }\r
93                         continuationLine = lineEndChar == '\\';\r
94                 }\r
95         }\r
96 \r
97         // look back to set chPrevNonWhite properly for better regex colouring\r
98         if (startPos > 0) {\r
99                 int back = startPos;\r
100                 while (--back && IsSpaceEquiv(styler.StyleAt(back)))\r
101                         ;\r
102                 if (styler.StyleAt(back) == SCE_C_OPERATOR) {\r
103                         chPrevNonWhite = styler.SafeGetCharAt(back);\r
104                 }\r
105         }\r
106 \r
107         StyleContext sc(startPos, length, initStyle, styler);\r
108 \r
109         for (; sc.More(); sc.Forward()) {\r
110 \r
111                 if (sc.atLineStart) {\r
112                         if (sc.state == SCE_C_STRING) {\r
113                                 // Prevent SCE_C_STRINGEOL from leaking back to previous line which\r
114                                 // ends with a line continuation by locking in the state upto this position.\r
115                                 sc.SetState(SCE_C_STRING);\r
116                         }\r
117                         // Reset states to begining of colourise so no surprises\r
118                         // if different sets of lines lexed.\r
119                         visibleChars = 0;\r
120                         lastWordWasUUID = false;\r
121                 }\r
122 \r
123                 // Handle line continuation generically.\r
124                 if (sc.ch == '\\') {\r
125                         if (sc.chNext == '\n' || sc.chNext == '\r') {\r
126                                 sc.Forward();\r
127                                 if (sc.ch == '\r' && sc.chNext == '\n') {\r
128                                         sc.Forward();\r
129                                 }\r
130                                 continuationLine = true;\r
131                                 continue;\r
132                         }\r
133                 }\r
134 \r
135                 // Determine if the current state should terminate.\r
136                 switch (sc.state) {\r
137                         case SCE_C_OPERATOR:\r
138                                 sc.SetState(SCE_C_DEFAULT);\r
139                                 break;\r
140                         case SCE_C_NUMBER:\r
141                                 // We accept almost anything because of hex. and number suffixes\r
142                                 if (!setWord.Contains(sc.ch)) {\r
143                                         sc.SetState(SCE_C_DEFAULT);\r
144                                 }\r
145                                 break;\r
146                         case SCE_C_IDENTIFIER:\r
147                                 if (!setWord.Contains(sc.ch) || (sc.ch == '.')) {\r
148                                         char s[1000];\r
149                                         if (caseSensitive) {\r
150                                                 sc.GetCurrent(s, sizeof(s));\r
151                                         } else {\r
152                                                 sc.GetCurrentLowered(s, sizeof(s));\r
153                                         }\r
154                                         if (keywords.InList(s)) {\r
155                                                 lastWordWasUUID = strcmp(s, "uuid") == 0;\r
156                                                 sc.ChangeState(SCE_C_WORD);\r
157                                         } else if (keywords2.InList(s)) {\r
158                                                 sc.ChangeState(SCE_C_WORD2);\r
159                                         } else if (keywords4.InList(s)) {\r
160                                                 sc.ChangeState(SCE_C_GLOBALCLASS);\r
161                                         }\r
162                                         sc.SetState(SCE_C_DEFAULT);\r
163                                 }\r
164                                 break;\r
165                         case SCE_C_PREPROCESSOR:\r
166                                 if (sc.atLineStart && !continuationLine) {\r
167                                         sc.SetState(SCE_C_DEFAULT);\r
168                                 } else if (stylingWithinPreprocessor) {\r
169                                         if (IsASpace(sc.ch)) {\r
170                                                 sc.SetState(SCE_C_DEFAULT);\r
171                                         }\r
172                                 } else {\r
173                                         if (sc.Match('/', '*') || sc.Match('/', '/')) {\r
174                                                 sc.SetState(SCE_C_DEFAULT);\r
175                                         }\r
176                                 }\r
177                                 break;\r
178                         case SCE_C_COMMENT:\r
179                                 if (sc.Match('*', '/')) {\r
180                                         sc.Forward();\r
181                                         sc.ForwardSetState(SCE_C_DEFAULT);\r
182                                 }\r
183                                 break;\r
184                         case SCE_C_COMMENTDOC:\r
185                                 if (sc.Match('*', '/')) {\r
186                                         sc.Forward();\r
187                                         sc.ForwardSetState(SCE_C_DEFAULT);\r
188                                 } else if (sc.ch == '@' || sc.ch == '\\') { // JavaDoc and Doxygen support\r
189                                         // Verify that we have the conditions to mark a comment-doc-keyword\r
190                                         if ((IsASpace(sc.chPrev) || sc.chPrev == '*') && (!IsASpace(sc.chNext))) {\r
191                                                 styleBeforeDCKeyword = SCE_C_COMMENTDOC;\r
192                                                 sc.SetState(SCE_C_COMMENTDOCKEYWORD);\r
193                                         }\r
194                                 }\r
195                                 break;\r
196                         case SCE_C_COMMENTLINE:\r
197                                 if (sc.atLineStart) {\r
198                                         sc.SetState(SCE_C_DEFAULT);\r
199                                 }\r
200                                 break;\r
201                         case SCE_C_COMMENTLINEDOC:\r
202                                 if (sc.atLineStart) {\r
203                                         sc.SetState(SCE_C_DEFAULT);\r
204                                 } else if (sc.ch == '@' || sc.ch == '\\') { // JavaDoc and Doxygen support\r
205                                         // Verify that we have the conditions to mark a comment-doc-keyword\r
206                                         if ((IsASpace(sc.chPrev) || sc.chPrev == '/' || sc.chPrev == '!') && (!IsASpace(sc.chNext))) {\r
207                                                 styleBeforeDCKeyword = SCE_C_COMMENTLINEDOC;\r
208                                                 sc.SetState(SCE_C_COMMENTDOCKEYWORD);\r
209                                         }\r
210                                 }\r
211                                 break;\r
212                         case SCE_C_COMMENTDOCKEYWORD:\r
213                                 if ((styleBeforeDCKeyword == SCE_C_COMMENTDOC) && sc.Match('*', '/')) {\r
214                                         sc.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR);\r
215                                         sc.Forward();\r
216                                         sc.ForwardSetState(SCE_C_DEFAULT);\r
217                                 } else if (!setDoxygen.Contains(sc.ch)) {\r
218                                         char s[100];\r
219                                         if (caseSensitive) {\r
220                                                 sc.GetCurrent(s, sizeof(s));\r
221                                         } else {\r
222                                                 sc.GetCurrentLowered(s, sizeof(s));\r
223                                         }\r
224                                         if (!IsASpace(sc.ch) || !keywords3.InList(s + 1)) {\r
225                                                 sc.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR);\r
226                                         }\r
227                                         sc.SetState(styleBeforeDCKeyword);\r
228                                 }\r
229                                 break;\r
230                         case SCE_C_STRING:\r
231                                 if (sc.atLineEnd) {\r
232                                         sc.ChangeState(SCE_C_STRINGEOL);\r
233                                 } else if (sc.ch == '\\') {\r
234                                         if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {\r
235                                                 sc.Forward();\r
236                                         }\r
237                                 } else if (sc.ch == '\"') {\r
238                                         sc.ForwardSetState(SCE_C_DEFAULT);\r
239                                 }\r
240                                 break;\r
241                         case SCE_C_CHARACTER:\r
242                                 if (sc.atLineEnd) {\r
243                                         sc.ChangeState(SCE_C_STRINGEOL);\r
244                                 } else if (sc.ch == '\\') {\r
245                                         if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {\r
246                                                 sc.Forward();\r
247                                         }\r
248                                 } else if (sc.ch == '\'') {\r
249                                         sc.ForwardSetState(SCE_C_DEFAULT);\r
250                                 }\r
251                                 break;\r
252                         case SCE_C_REGEX:\r
253                                 if (sc.atLineStart) {\r
254                                         sc.SetState(SCE_C_DEFAULT);\r
255                                 } else if (sc.ch == '/') {\r
256                                         sc.Forward();\r
257                                         while ((sc.ch < 0x80) && islower(sc.ch))\r
258                                                 sc.Forward();    // gobble regex flags\r
259                                         sc.SetState(SCE_C_DEFAULT);\r
260                                 } else if (sc.ch == '\\') {\r
261                                         // Gobble up the quoted character\r
262                                         if (sc.chNext == '\\' || sc.chNext == '/') {\r
263                                                 sc.Forward();\r
264                                         }\r
265                                 }\r
266                                 break;\r
267                         case SCE_C_STRINGEOL:\r
268                                 if (sc.atLineStart) {\r
269                                         sc.SetState(SCE_C_DEFAULT);\r
270                                 }\r
271                                 break;\r
272                         case SCE_C_VERBATIM:\r
273                                 if (sc.ch == '\"') {\r
274                                         if (sc.chNext == '\"') {\r
275                                                 sc.Forward();\r
276                                         } else {\r
277                                                 sc.ForwardSetState(SCE_C_DEFAULT);\r
278                                         }\r
279                                 }\r
280                                 break;\r
281                         case SCE_C_UUID:\r
282                                 if (sc.ch == '\r' || sc.ch == '\n' || sc.ch == ')') {\r
283                                         sc.SetState(SCE_C_DEFAULT);\r
284                                 }\r
285                 }\r
286 \r
287                 // Determine if a new state should be entered.\r
288                 if (sc.state == SCE_C_DEFAULT) {\r
289                         if (sc.Match('@', '\"')) {\r
290                                 sc.SetState(SCE_C_VERBATIM);\r
291                                 sc.Forward();\r
292                         } else if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {\r
293                                 if (lastWordWasUUID) {\r
294                                         sc.SetState(SCE_C_UUID);\r
295                                         lastWordWasUUID = false;\r
296                                 } else {\r
297                                         sc.SetState(SCE_C_NUMBER);\r
298                                 }\r
299                         } else if (setWordStart.Contains(sc.ch) || (sc.ch == '@')) {\r
300                                 if (lastWordWasUUID) {\r
301                                         sc.SetState(SCE_C_UUID);\r
302                                         lastWordWasUUID = false;\r
303                                 } else {\r
304                                         sc.SetState(SCE_C_IDENTIFIER);\r
305                                 }\r
306                         } else if (sc.Match('/', '*')) {\r
307                                 if (sc.Match("/**") || sc.Match("/*!")) {       // Support of Qt/Doxygen doc. style\r
308                                         sc.SetState(SCE_C_COMMENTDOC);\r
309                                 } else {\r
310                                         sc.SetState(SCE_C_COMMENT);\r
311                                 }\r
312                                 sc.Forward();   // Eat the * so it isn't used for the end of the comment\r
313                         } else if (sc.Match('/', '/')) {\r
314                                 if ((sc.Match("///") && !sc.Match("////")) || sc.Match("//!"))\r
315                                         // Support of Qt/Doxygen doc. style\r
316                                         sc.SetState(SCE_C_COMMENTLINEDOC);\r
317                                 else\r
318                                         sc.SetState(SCE_C_COMMENTLINE);\r
319                         } else if (sc.ch == '/' && setOKBeforeRE.Contains(chPrevNonWhite) &&\r
320                                 (!setCouldBePostOp.Contains(chPrevNonWhite) || !FollowsPostfixOperator(sc, styler))) {\r
321                                 sc.SetState(SCE_C_REGEX);       // JavaScript's RegEx\r
322                         } else if (sc.ch == '\"') {\r
323                                 sc.SetState(SCE_C_STRING);\r
324                         } else if (sc.ch == '\'') {\r
325                                 sc.SetState(SCE_C_CHARACTER);\r
326                         } else if (sc.ch == '#' && visibleChars == 0) {\r
327                                 // Preprocessor commands are alone on their line\r
328                                 sc.SetState(SCE_C_PREPROCESSOR);\r
329                                 // Skip whitespace between # and preprocessor word\r
330                                 do {\r
331                                         sc.Forward();\r
332                                 } while ((sc.ch == ' ' || sc.ch == '\t') && sc.More());\r
333                                 if (sc.atLineEnd) {\r
334                                         sc.SetState(SCE_C_DEFAULT);\r
335                                 }\r
336                         } else if (isoperator(static_cast<char>(sc.ch))) {\r
337                                 sc.SetState(SCE_C_OPERATOR);\r
338                         }\r
339                 }\r
340 \r
341                 if (!IsASpace(sc.ch) && !IsSpaceEquiv(sc.state)) {\r
342                         chPrevNonWhite = sc.ch;\r
343                         visibleChars++;\r
344                 }\r
345                 continuationLine = false;\r
346         }\r
347         sc.Complete();\r
348 }\r
349 \r
350 static bool IsStreamCommentStyle(int style) {\r
351         return style == SCE_C_COMMENT ||\r
352                 style == SCE_C_COMMENTDOC ||\r
353                 style == SCE_C_COMMENTDOCKEYWORD ||\r
354                 style == SCE_C_COMMENTDOCKEYWORDERROR;\r
355 }\r
356 \r
357 // Store both the current line's fold level and the next lines in the\r
358 // level store to make it easy to pick up with each increment\r
359 // and to make it possible to fiddle the current level for "} else {".\r
360 static void FoldCppDoc(unsigned int startPos, int length, int initStyle,\r
361                                            WordList *[], Accessor &styler) {\r
362         bool foldComment = styler.GetPropertyInt("fold.comment") != 0;\r
363         bool foldPreprocessor = styler.GetPropertyInt("fold.preprocessor") != 0;\r
364         bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;\r
365         bool foldAtElse = styler.GetPropertyInt("fold.at.else", 0) != 0;\r
366         unsigned int endPos = startPos + length;\r
367         int visibleChars = 0;\r
368         int lineCurrent = styler.GetLine(startPos);\r
369         int levelCurrent = SC_FOLDLEVELBASE;\r
370         if (lineCurrent > 0)\r
371                 levelCurrent = styler.LevelAt(lineCurrent-1) >> 16;\r
372         int levelMinCurrent = levelCurrent;\r
373         int levelNext = levelCurrent;\r
374         char chNext = styler[startPos];\r
375         int styleNext = styler.StyleAt(startPos);\r
376         int style = initStyle;\r
377         for (unsigned int i = startPos; i < endPos; i++) {\r
378                 char ch = chNext;\r
379                 chNext = styler.SafeGetCharAt(i + 1);\r
380                 int stylePrev = style;\r
381                 style = styleNext;\r
382                 styleNext = styler.StyleAt(i + 1);\r
383                 bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');\r
384                 if (foldComment && IsStreamCommentStyle(style)) {\r
385                         if (!IsStreamCommentStyle(stylePrev) && (stylePrev != SCE_C_COMMENTLINEDOC)) {\r
386                                 levelNext++;\r
387                         } else if (!IsStreamCommentStyle(styleNext) && (styleNext != SCE_C_COMMENTLINEDOC) && !atEOL) {\r
388                                 // Comments don't end at end of line and the next character may be unstyled.\r
389                                 levelNext--;\r
390                         }\r
391                 }\r
392                 if (foldComment && (style == SCE_C_COMMENTLINE)) {\r
393                         if ((ch == '/') && (chNext == '/')) {\r
394                                 char chNext2 = styler.SafeGetCharAt(i + 2);\r
395                                 if (chNext2 == '{') {\r
396                                         levelNext++;\r
397                                 } else if (chNext2 == '}') {\r
398                                         levelNext--;\r
399                                 }\r
400                         }\r
401                 }\r
402                 if (foldPreprocessor && (style == SCE_C_PREPROCESSOR)) {\r
403                         if (ch == '#') {\r
404                                 unsigned int j = i + 1;\r
405                                 while ((j < endPos) && IsASpaceOrTab(styler.SafeGetCharAt(j))) {\r
406                                         j++;\r
407                                 }\r
408                                 if (styler.Match(j, "region") || styler.Match(j, "if")) {\r
409                                         levelNext++;\r
410                                 } else if (styler.Match(j, "end")) {\r
411                                         levelNext--;\r
412                                 }\r
413                         }\r
414                 }\r
415                 if (style == SCE_C_OPERATOR) {\r
416                         if (ch == '{') {\r
417                                 // Measure the minimum before a '{' to allow\r
418                                 // folding on "} else {"\r
419                                 if (levelMinCurrent > levelNext) {\r
420                                         levelMinCurrent = levelNext;\r
421                                 }\r
422                                 levelNext++;\r
423                         } else if (ch == '}') {\r
424                                 levelNext--;\r
425                         }\r
426                 }\r
427                 if (!IsASpace(ch))\r
428                         visibleChars++;\r
429                 if (atEOL || (i == endPos-1)) {\r
430                         int levelUse = levelCurrent;\r
431                         if (foldAtElse) {\r
432                                 levelUse = levelMinCurrent;\r
433                         }\r
434                         int lev = levelUse | levelNext << 16;\r
435                         if (visibleChars == 0 && foldCompact)\r
436                                 lev |= SC_FOLDLEVELWHITEFLAG;\r
437                         if (levelUse < levelNext)\r
438                                 lev |= SC_FOLDLEVELHEADERFLAG;\r
439                         if (lev != styler.LevelAt(lineCurrent)) {\r
440                                 styler.SetLevel(lineCurrent, lev);\r
441                         }\r
442                         lineCurrent++;\r
443                         levelCurrent = levelNext;\r
444                         levelMinCurrent = levelCurrent;\r
445                         visibleChars = 0;\r
446                 }\r
447         }\r
448 }\r
449 \r
450 static const char * const cppWordLists[] = {\r
451             "Primary keywords and identifiers",\r
452             "Secondary keywords and identifiers",\r
453             "Documentation comment keywords",\r
454             "Unused",\r
455             "Global classes and typedefs",\r
456             0,\r
457         };\r
458 \r
459 static void ColouriseCppDocSensitive(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],\r
460                                      Accessor &styler) {\r
461         ColouriseCppDoc(startPos, length, initStyle, keywordlists, styler, true);\r
462 }\r
463 \r
464 static void ColouriseCppDocInsensitive(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],\r
465                                        Accessor &styler) {\r
466         ColouriseCppDoc(startPos, length, initStyle, keywordlists, styler, false);\r
467 }\r
468 \r
469 LexerModule lmCPP(SCLEX_CPP, ColouriseCppDocSensitive, "cpp", FoldCppDoc, cppWordLists);\r
470 LexerModule lmCPPNoCase(SCLEX_CPPNOCASE, ColouriseCppDocInsensitive, "cppnocase", FoldCppDoc, cppWordLists);\r