OSDN Git Service

Add SCI Edit to GitBlameView
[tortoisegit/TortoiseGitJp.git] / ext / scintilla / src / LexD.cxx
1 /** @file LexD.cxx\r
2  ** Lexer for D.\r
3  **\r
4  ** Copyright (c) 2006 by Waldemar Augustyn <waldemar@wdmsys.com>\r
5  **/\r
6 // Copyright 1998-2005 by Neil Hodgson <neilh@scintilla.org>\r
7 // The License.txt file describes the conditions under which this software may be distributed.\r
8 \r
9 #include <stdlib.h>\r
10 #include <string.h>\r
11 #include <ctype.h>\r
12 #include <stdio.h>\r
13 #include <stdarg.h>\r
14 \r
15 #include "Platform.h"\r
16 \r
17 #include "PropSet.h"\r
18 #include "Accessor.h"\r
19 #include "StyleContext.h"\r
20 #include "KeyWords.h"\r
21 #include "Scintilla.h"\r
22 #include "SciLexer.h"\r
23 \r
24 #ifdef SCI_NAMESPACE\r
25 using namespace Scintilla;\r
26 #endif\r
27 \r
28 /*/ Nested comments require keeping the value of the nesting level for every\r
29     position in the document.  But since scintilla always styles line by line,\r
30     we only need to store one value per line. The non-negative number indicates\r
31     nesting level at the end of the line.\r
32 /*/\r
33 \r
34 // We use custom qualifiers since it is not clear what D allows.\r
35 \r
36 static bool IsWordStart(int ch) {\r
37         return isascii(ch) && (isalpha(ch) || ch == '_');\r
38 }\r
39 \r
40 static bool IsWord(int ch) {\r
41         return isascii(ch) && (isalnum(ch) || ch == '_');\r
42 }\r
43 \r
44 static bool IsDoxygen(int ch) {\r
45         if (isascii(ch) && islower(ch))\r
46                 return true;\r
47         if (ch == '$' || ch == '@' || ch == '\\' ||\r
48                 ch == '&' || ch == '#' || ch == '<' || ch == '>' ||\r
49                 ch == '{' || ch == '}' || ch == '[' || ch == ']')\r
50                 return true;\r
51         return false;\r
52 }\r
53 \r
54 \r
55 static void ColouriseDoc(unsigned int startPos, int length, int initStyle, \r
56     WordList *keywordlists[], Accessor &styler, bool caseSensitive) {\r
57 \r
58     WordList &keywords = *keywordlists[0];\r
59     WordList &keywords2 = *keywordlists[1];\r
60     WordList &keywords3 = *keywordlists[2];\r
61     WordList &keywords4 = *keywordlists[3];\r
62 \r
63     int styleBeforeDCKeyword = SCE_D_DEFAULT;\r
64 \r
65     StyleContext sc(startPos, length, initStyle, styler);\r
66 \r
67     int curLine = styler.GetLine(startPos);\r
68     int curNcLevel = curLine > 0? styler.GetLineState(curLine-1): 0;\r
69 \r
70     for (; sc.More(); sc.Forward()) {\r
71 \r
72         if (sc.atLineStart) {\r
73             if (sc.state == SCE_D_STRING) {\r
74                 // Prevent SCE_D_STRINGEOL from leaking back to previous line which\r
75                 // ends with a line continuation by locking in the state upto this position.\r
76                 sc.SetState(SCE_D_STRING);\r
77             }\r
78             curLine = styler.GetLine(sc.currentPos);\r
79             styler.SetLineState(curLine, curNcLevel);\r
80         }\r
81 \r
82         // Handle line continuation generically.\r
83         if (sc.ch == '\\') {\r
84             if (sc.chNext == '\n' || sc.chNext == '\r') {\r
85                 sc.Forward();\r
86                 if (sc.ch == '\r' && sc.chNext == '\n') {\r
87                     sc.Forward();\r
88                 }\r
89                 continue;\r
90             }\r
91         }\r
92 \r
93         // Determine if the current state should terminate.\r
94         switch (sc.state) {\r
95             case SCE_D_OPERATOR:\r
96                 sc.SetState(SCE_D_DEFAULT);\r
97                 break;\r
98             case SCE_D_NUMBER:\r
99                 // We accept almost anything because of hex. and number suffixes\r
100                 if (!IsWord(sc.ch) && sc.ch != '.') {\r
101                     sc.SetState(SCE_D_DEFAULT);\r
102                 }\r
103                 break;\r
104             case SCE_D_IDENTIFIER:\r
105                 if (!IsWord(sc.ch)) {\r
106                     char s[1000];\r
107                     if (caseSensitive) {\r
108                         sc.GetCurrent(s, sizeof(s));\r
109                     } else {\r
110                         sc.GetCurrentLowered(s, sizeof(s));\r
111                     }\r
112                     if (keywords.InList(s)) {\r
113                         sc.ChangeState(SCE_D_WORD);\r
114                     } else if (keywords2.InList(s)) {\r
115                         sc.ChangeState(SCE_D_WORD2);\r
116                     } else if (keywords4.InList(s)) {\r
117                         sc.ChangeState(SCE_D_TYPEDEF);\r
118                     }\r
119                     sc.SetState(SCE_D_DEFAULT);\r
120                 }\r
121                 break;\r
122             case SCE_D_COMMENT:\r
123                 if (sc.Match('*', '/')) {\r
124                     sc.Forward();\r
125                     sc.ForwardSetState(SCE_D_DEFAULT);\r
126                 }\r
127                 break;\r
128             case SCE_D_COMMENTDOC:\r
129                 if (sc.Match('*', '/')) {\r
130                     sc.Forward();\r
131                     sc.ForwardSetState(SCE_D_DEFAULT);\r
132                 } else if (sc.ch == '@' || sc.ch == '\\') { // JavaDoc and Doxygen support\r
133                     // Verify that we have the conditions to mark a comment-doc-keyword\r
134                     if ((IsASpace(sc.chPrev) || sc.chPrev == '*') && (!IsASpace(sc.chNext))) {\r
135                         styleBeforeDCKeyword = SCE_D_COMMENTDOC;\r
136                         sc.SetState(SCE_D_COMMENTDOCKEYWORD);\r
137                     }\r
138                 }\r
139                 break;\r
140             case SCE_D_COMMENTLINE:\r
141                 if (sc.atLineStart) {\r
142                     sc.SetState(SCE_D_DEFAULT);\r
143                 }\r
144                 break;\r
145             case SCE_D_COMMENTLINEDOC:\r
146                 if (sc.atLineStart) {\r
147                     sc.SetState(SCE_D_DEFAULT);\r
148                 } else if (sc.ch == '@' || sc.ch == '\\') { // JavaDoc and Doxygen support\r
149                     // Verify that we have the conditions to mark a comment-doc-keyword\r
150                     if ((IsASpace(sc.chPrev) || sc.chPrev == '/' || sc.chPrev == '!') && (!IsASpace(sc.chNext))) {\r
151                         styleBeforeDCKeyword = SCE_D_COMMENTLINEDOC;\r
152                         sc.SetState(SCE_D_COMMENTDOCKEYWORD);\r
153                     }\r
154                 }\r
155                 break;\r
156             case SCE_D_COMMENTDOCKEYWORD:\r
157                 if ((styleBeforeDCKeyword == SCE_D_COMMENTDOC) && sc.Match('*', '/')) {\r
158                     sc.ChangeState(SCE_D_COMMENTDOCKEYWORDERROR);\r
159                     sc.Forward();\r
160                     sc.ForwardSetState(SCE_D_DEFAULT);\r
161                 } else if (!IsDoxygen(sc.ch)) {\r
162                     char s[100];\r
163                     if (caseSensitive) {\r
164                         sc.GetCurrent(s, sizeof(s));\r
165                     } else {\r
166                         sc.GetCurrentLowered(s, sizeof(s));\r
167                     }\r
168                     if (!IsASpace(sc.ch) || !keywords3.InList(s + 1)) {\r
169                         sc.ChangeState(SCE_D_COMMENTDOCKEYWORDERROR);\r
170                     }\r
171                     sc.SetState(styleBeforeDCKeyword);\r
172                 }\r
173                 break;\r
174             case SCE_D_COMMENTNESTED:\r
175                 if (sc.Match('+', '/')) {\r
176                     if (curNcLevel > 0)\r
177                         curNcLevel -= 1;\r
178                     curLine = styler.GetLine(sc.currentPos);\r
179                     styler.SetLineState(curLine, curNcLevel);\r
180                     sc.Forward();\r
181                     if (curNcLevel == 0) {\r
182                         sc.ForwardSetState(SCE_D_DEFAULT);\r
183                     }\r
184                 }\r
185                 else if (sc.Match('/','+')) {\r
186                     curNcLevel += 1;\r
187                     curLine = styler.GetLine(sc.currentPos);\r
188                     styler.SetLineState(curLine, curNcLevel);\r
189                     sc.Forward();\r
190                 }\r
191                 break;\r
192             case SCE_D_STRING:\r
193                 if (sc.atLineEnd) {\r
194                     sc.ChangeState(SCE_D_STRINGEOL);\r
195                 } else if (sc.ch == '\\') {\r
196                     if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {\r
197                         sc.Forward();\r
198                     }\r
199                 } else if (sc.ch == '\"') {\r
200                     sc.ForwardSetState(SCE_D_DEFAULT);\r
201                 }\r
202                 break;\r
203             case SCE_D_CHARACTER:\r
204                 if (sc.atLineEnd) {\r
205                     sc.ChangeState(SCE_D_STRINGEOL);\r
206                 } else if (sc.ch == '\\') {\r
207                     if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {\r
208                         sc.Forward();\r
209                     }\r
210                 } else if (sc.ch == '\'') {\r
211                     sc.ForwardSetState(SCE_D_DEFAULT);\r
212                 }\r
213                 break;\r
214             case SCE_D_STRINGEOL:\r
215                 if (sc.atLineStart) {\r
216                     sc.SetState(SCE_D_DEFAULT);\r
217                 }\r
218                 break;\r
219         }\r
220 \r
221         // Determine if a new state should be entered.\r
222         if (sc.state == SCE_D_DEFAULT) {\r
223             if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {\r
224                     sc.SetState(SCE_D_NUMBER);\r
225             } else if (IsWordStart(sc.ch)) {\r
226                     sc.SetState(SCE_D_IDENTIFIER);\r
227             } else if (sc.Match('/','+')) {\r
228                 curNcLevel += 1;\r
229                 curLine = styler.GetLine(sc.currentPos);\r
230                 styler.SetLineState(curLine, curNcLevel);\r
231                 sc.SetState(SCE_D_COMMENTNESTED);\r
232                 sc.Forward();\r
233             } else if (sc.Match('/', '*')) {\r
234                 if (sc.Match("/**") || sc.Match("/*!")) {   // Support of Qt/Doxygen doc. style\r
235                     sc.SetState(SCE_D_COMMENTDOC);\r
236                 } else {\r
237                     sc.SetState(SCE_D_COMMENT);\r
238                 }\r
239                 sc.Forward();   // Eat the * so it isn't used for the end of the comment\r
240             } else if (sc.Match('/', '/')) {\r
241                 if ((sc.Match("///") && !sc.Match("////")) || sc.Match("//!"))\r
242                     // Support of Qt/Doxygen doc. style\r
243                     sc.SetState(SCE_D_COMMENTLINEDOC);\r
244                 else\r
245                     sc.SetState(SCE_D_COMMENTLINE);\r
246             } else if (sc.ch == '\"') {\r
247                 sc.SetState(SCE_D_STRING);\r
248             } else if (sc.ch == '\'') {\r
249                 sc.SetState(SCE_D_CHARACTER);\r
250             } else if (isoperator(static_cast<char>(sc.ch))) {\r
251                 sc.SetState(SCE_D_OPERATOR);\r
252             }\r
253         }\r
254     }\r
255     sc.Complete();\r
256 }\r
257 \r
258 static bool IsStreamCommentStyle(int style) {\r
259     return style == SCE_D_COMMENT ||\r
260         style == SCE_D_COMMENTDOC ||\r
261         style == SCE_D_COMMENTDOCKEYWORD ||\r
262         style == SCE_D_COMMENTDOCKEYWORDERROR;\r
263 }\r
264 \r
265 // Store both the current line's fold level and the next lines in the\r
266 // level store to make it easy to pick up with each increment\r
267 // and to make it possible to fiddle the current level for "} else {".\r
268 static void FoldDoc(unsigned int startPos, int length, int initStyle, Accessor &styler) {\r
269     bool foldComment = styler.GetPropertyInt("fold.comment") != 0;\r
270     bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;\r
271     bool foldAtElse = styler.GetPropertyInt("lexer.d.fold.at.else",\r
272                 styler.GetPropertyInt("fold.at.else", 0)) != 0;\r
273     unsigned int endPos = startPos + length;\r
274     int visibleChars = 0;\r
275     int lineCurrent = styler.GetLine(startPos);\r
276     int levelCurrent = SC_FOLDLEVELBASE;\r
277     if (lineCurrent > 0)\r
278         levelCurrent = styler.LevelAt(lineCurrent-1) >> 16;\r
279     int levelMinCurrent = levelCurrent;\r
280     int levelNext = levelCurrent;\r
281     char chNext = styler[startPos];\r
282     int styleNext = styler.StyleAt(startPos);\r
283     int style = initStyle;\r
284     for (unsigned int i = startPos; i < endPos; i++) {\r
285         char ch = chNext;\r
286         chNext = styler.SafeGetCharAt(i + 1);\r
287         int stylePrev = style;\r
288         style = styleNext;\r
289         styleNext = styler.StyleAt(i + 1);\r
290         bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');\r
291         if (foldComment && IsStreamCommentStyle(style)) {\r
292             if (!IsStreamCommentStyle(stylePrev)) {\r
293                 levelNext++;\r
294             } else if (!IsStreamCommentStyle(styleNext) && !atEOL) {\r
295                 // Comments don't end at end of line and the next character may be unstyled.\r
296                 levelNext--;\r
297             }\r
298         }\r
299         if (style == SCE_D_OPERATOR) {\r
300             if (ch == '{') {\r
301                 // Measure the minimum before a '{' to allow\r
302                 // folding on "} else {"\r
303                 if (levelMinCurrent > levelNext) {\r
304                     levelMinCurrent = levelNext;\r
305                 }\r
306                 levelNext++;\r
307             } else if (ch == '}') {\r
308                 levelNext--;\r
309             }\r
310         }\r
311         if (atEOL) {\r
312             if (foldComment) {  // Handle nested comments\r
313                 int nc;\r
314                 nc =  styler.GetLineState(lineCurrent);\r
315                 nc -= lineCurrent>0? styler.GetLineState(lineCurrent-1): 0;\r
316                 levelNext += nc;\r
317             }\r
318             int levelUse = levelCurrent;\r
319             if (foldAtElse) {\r
320                 levelUse = levelMinCurrent;\r
321             }\r
322             int lev = levelUse | levelNext << 16;\r
323             if (visibleChars == 0 && foldCompact)\r
324                 lev |= SC_FOLDLEVELWHITEFLAG;\r
325             if (levelUse < levelNext)\r
326                 lev |= SC_FOLDLEVELHEADERFLAG;\r
327             if (lev != styler.LevelAt(lineCurrent)) {\r
328                 styler.SetLevel(lineCurrent, lev);\r
329             }\r
330             lineCurrent++;\r
331             levelCurrent = levelNext;\r
332             levelMinCurrent = levelCurrent;\r
333             visibleChars = 0;\r
334         }\r
335         if (!IsASpace(ch))\r
336             visibleChars++;\r
337     }\r
338 }\r
339 \r
340 static void FoldDDoc(unsigned int startPos, int length, int initStyle,\r
341     WordList *[], Accessor &styler) {\r
342         FoldDoc(startPos, length, initStyle, styler);\r
343 }\r
344 \r
345 static const char * const dWordLists[] = {\r
346             "Primary keywords and identifiers",\r
347             "Secondary keywords and identifiers",\r
348             "Documentation comment keywords",\r
349             "Type definitions and aliases",\r
350             0,\r
351         };\r
352 \r
353 static void ColouriseDDoc(unsigned int startPos, int length, \r
354     int initStyle, WordList *keywordlists[], Accessor &styler) {\r
355         ColouriseDoc(startPos, length, initStyle, keywordlists, styler, true);\r
356 }\r
357 \r
358 LexerModule lmD(SCLEX_D, ColouriseDDoc, "d", FoldDDoc, dWordLists);\r