1 | | 1 | | // Scintilla source code edit control |
|
|
3 | | ** Lexer for C++, C, Java, and Javascript. | | 3 | | ** Lexer for C++, C, Java, and JavaScript. |
|
5 | | // Copyright 1998-2002 by Neil Hodgson <neilh@scintilla.org> | | 5 | | // Copyright 1998-2005 by Neil Hodgson <neilh@scintilla.org> |
6 | | 6 | | // The License.txt file describes the conditions under which this software may be distributed. |
|
|
|
14 skipped lines |
23 | | 23 | | #define KEYWORD_BOXHEADER 1 |
|
24 | | 24 | | #define KEYWORD_FOLDCONTRACTED 2 |
|
|
26 | | static bool IsOKBeforeRE(const int ch) { | | 26 | | static bool IsOKBeforeRE(int ch) { |
27 | | 27 | | return (ch == '(') || (ch == '=') || (ch == ','); |
|
|
|
30 | | static inline bool IsAWordChar(const int ch) { | | 30 | | static inline bool IsAWordChar(int ch) { |
31 | | 31 | | return (ch < 0x80) && (isalnum(ch) || ch == '.' || ch == '_'); |
|
|
|
34 | | static inline bool IsAWordStart(const int ch) { | | 34 | | static inline bool IsAWordStart(int ch) { |
35 | | return (ch < 0x80) && (isalnum(ch) || ch == '_'); | | 35 | | return (ch < 0x80) && (isalpha(ch) || ch == '_'); |
|
|
38 | | static inline bool IsADoxygenChar(const int ch) { | | 38 | | static inline bool IsADoxygenChar(int ch) { |
39 | | return (islower(ch) || ch == '$' || ch == '@' || | | 39 | | return (ch < 0x80 && islower(ch)) || ch == '$' || ch == '@' || |
40 | | ch == '\\' || ch == '&' || ch == '<' || | | 40 | | ch == '\\' || ch == '&' || ch == '<' || |
41 | | ch == '>' || ch == '#' || ch == '{' || | | 41 | | ch == '>' || ch == '#' || ch == '{' || |
42 | | ch == '}' || ch == '[' || ch == ']'); | | 42 | | ch == '}' || ch == '[' || ch == ']'; |
|
|
45 | | static inline bool IsStateComment(const int state) { | | 45 | | static bool IsSpaceEquiv(int state) { |
46 | | return ((state == SCE_C_COMMENT) || | | 46 | | return (state <= SCE_C_COMMENTDOC) || |
47 | | (state == SCE_C_COMMENTLINE) || | | |
48 | | (state == SCE_C_COMMENTDOC) || | | |
49 | | (state == SCE_C_COMMENTDOCKEYWORD) || | | 47 | | // including SCE_C_DEFAULT, SCE_C_COMMENT, SCE_C_COMMENTLINE |
50 | | (state == SCE_C_COMMENTDOCKEYWORDERROR)); | | 48 | | (state == SCE_C_COMMENTLINEDOC) || (state == SCE_C_COMMENTDOCKEYWORD) || |
51 | | } | | |
52 | | | | |
53 | | static inline bool IsStateString(const int state) { | | |
54 | | return ((state == SCE_C_STRING) || (state == SCE_C_VERBATIM)); | | 49 | | (state == SCE_C_COMMENTDOCKEYWORDERROR); |
|
|
57 | | 52 | | static void ColouriseCppDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[], |
|
6 skipped lines |
|
65 | | 60 | | bool stylingWithinPreprocessor = styler.GetPropertyInt("styling.within.preprocessor") != 0; |
|
|
67 | | // Do not leak onto next line | | |
68 | | if (initStyle == SCE_C_STRINGEOL) | | |
69 | | initStyle = SCE_C_DEFAULT; | | |
70 | | | | |
71 | | 62 | | int chPrevNonWhite = ' '; |
|
72 | | 63 | | int visibleChars = 0; |
|
73 | | 64 | | bool lastWordWasUUID = false; |
|
| | 65 | | int styleBeforeDCKeyword = SCE_C_DEFAULT; |
| | 66 | | bool continuationLine = false; |
| | 67 | | |
| | 68 | | if (initStyle == SCE_C_PREPROCESSOR) { |
| | 69 | | // Set continuationLine if last character of previous line is '\' |
18 skipped lines |
| | 88 | | ; |
| | 89 | | if (styler.StyleAt(back) == SCE_C_OPERATOR) { |
| | 90 | | chPrevNonWhite = styler.SafeGetCharAt(back); |
| | 91 | | } |
| | 92 | | } |
|
75 | | 94 | | StyleContext sc(startPos, length, initStyle, styler); |
|
|
77 | | 96 | | for (; sc.More(); sc.Forward()) { |
|
|
| | 98 | | if (sc.atLineStart) { |
79 | | if (sc.atLineStart && (sc.state == SCE_C_STRING)) { | | 99 | | if (sc.state == SCE_C_STRING) { |
80 | | // Prevent SCE_C_STRINGEOL from leaking back to previous line | | 100 | | // Prevent SCE_C_STRINGEOL from leaking back to previous line which |
| | 101 | | // ends with a line continuation by locking in the state upto this position. |
81 | | sc.SetState(SCE_C_STRING); | | 102 | | sc.SetState(SCE_C_STRING); |
| | 103 | | } |
| | 104 | | // Reset states to begining of colourise so no surprises |
| | 105 | | // if different sets of lines lexed. |
| | 106 | | visibleChars = 0; |
| | 107 | | lastWordWasUUID = false; |
|
|
84 | | 110 | | // Handle line continuation generically. |
|
3 skipped lines |
88 | | 114 | | if (sc.ch == '\r' && sc.chNext == '\n') { |
|
|
|
| | 117 | | continuationLine = true; |
|
|
|
|
95 | | 122 | | // Determine if the current state should terminate. |
|
96 | | if (sc.state == SCE_C_OPERATOR) { | | 123 | | switch (sc.state) { |
97 | | sc.SetState(SCE_C_DEFAULT); | | |
98 | | } else if (sc.state == SCE_C_NUMBER) { | | |
99 | | if (!IsAWordChar(sc.ch)) { | | |
100 | | sc.SetState(SCE_C_DEFAULT); | | 124 | | case SCE_C_OPERATOR: |
101 | | } | | |
102 | | } else if (sc.state == SCE_C_IDENTIFIER) { | | |
103 | | if (!IsAWordChar(sc.ch) || (sc.ch == '.')) { | | |
104 | | char s[100]; | | |
105 | | if (caseSensitive) { | | |
106 | | sc.GetCurrent(s, sizeof(s)); | | |
107 | | } else { | | |
108 | | sc.GetCurrentLowered(s, sizeof(s)); | | |
109 | | } | | |
110 | | if (keywords.InList(s)) { | | |
111 | | lastWordWasUUID = strcmp(s, "uuid") == 0; | | |
112 | | sc.ChangeState(SCE_C_WORD); | | |
113 | | } else if (keywords2.InList(s)) { | | |
114 | | sc.ChangeState(SCE_C_WORD2); | | |
115 | | } else if (keywords4.InList(s)) { | | |
116 | | sc.ChangeState(SCE_C_GLOBALCLASS); | | |
117 | | } | | |
118 | | 125 | | sc.SetState(SCE_C_DEFAULT); |
|
119 | | } | | 126 | | break; |
120 | | } else if (sc.state == SCE_C_PREPROCESSOR) { | | 127 | | case SCE_C_NUMBER: |
121 | | if (stylingWithinPreprocessor) { | | 128 | | // We accept almost anything because of hex. and number suffixes |
122 | | if (IsASpace(sc.ch)) { | | 129 | | if (!IsAWordChar(sc.ch)) { |
123 | | 130 | | sc.SetState(SCE_C_DEFAULT); |
|
|
125 | | } else { | | 132 | | break; |
| | 133 | | case SCE_C_IDENTIFIER: |
126 | | if ((sc.ch == '\r') || (sc.ch == '\n') || (sc.Match('/', '*')) || (sc.Match('/', '/'))) { | | 134 | | if (!IsAWordChar(sc.ch) || (sc.ch == '.')) { |
| | 135 | | char s[1000]; |
| | 136 | | if (caseSensitive) { |
| | 137 | | sc.GetCurrent(s, sizeof(s)); |
| | 138 | | } else { |
| | 139 | | sc.GetCurrentLowered(s, sizeof(s)); |
| | 140 | | } |
| | 141 | | if (keywords.InList(s)) { |
| | 142 | | lastWordWasUUID = strcmp(s, "uuid") == 0; |
| | 143 | | sc.ChangeState(SCE_C_WORD); |
| | 144 | | } else if (keywords2.InList(s)) { |
| | 145 | | sc.ChangeState(SCE_C_WORD2); |
| | 146 | | } else if (keywords4.InList(s)) { |
| | 147 | | sc.ChangeState(SCE_C_GLOBALCLASS); |
| | 148 | | } |
127 | | 149 | | sc.SetState(SCE_C_DEFAULT); |
|
|
129 | | } | | 151 | | break; |
130 | | } else if (sc.state == SCE_C_COMMENT) { | | |
131 | | if (sc.Match('*', '/')) { | | |
132 | | sc.Forward(); | | |
133 | | sc.ForwardSetState(SCE_C_DEFAULT); | | |
134 | | } | | |
135 | | } else if (sc.state == SCE_C_COMMENTDOC) { | | 152 | | case SCE_C_PREPROCESSOR: |
136 | | if (sc.Match('*', '/')) { | | |
137 | | sc.Forward(); | | |
138 | | sc.ForwardSetState(SCE_C_DEFAULT); | | |
139 | | } else if (sc.ch == '@' || sc.ch == '\\') { | | 153 | | if (sc.atLineStart && !continuationLine) { |
140 | | sc.SetState(SCE_C_COMMENTDOCKEYWORD); | | |
141 | | } | | |
142 | | } else if (sc.state == SCE_C_COMMENTLINE || sc.state == SCE_C_COMMENTLINEDOC) { | | |
143 | | if (sc.ch == '\r' || sc.ch == '\n') { | | |
144 | | sc.SetState(SCE_C_DEFAULT); | | 154 | | sc.SetState(SCE_C_DEFAULT); |
145 | | visibleChars = 0; | | |
146 | | } | | |
147 | | } else if (sc.state == SCE_C_COMMENTDOCKEYWORD) { | | 155 | | } else if (stylingWithinPreprocessor) { |
148 | | if (sc.Match('*', '/')) { | | 156 | | if (IsASpace(sc.ch)) { |
149 | | sc.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR); | | |
150 | | sc.Forward(); | | |
151 | | sc.ForwardSetState(SCE_C_DEFAULT); | | 157 | | sc.SetState(SCE_C_DEFAULT); |
152 | | } else if (!IsADoxygenChar(sc.ch)) { | | 158 | | } |
153 | | char s[100]; | | |
154 | | if (caseSensitive) { | | |
155 | | sc.GetCurrent(s, sizeof(s)); | | |
|
157 | | sc.GetCurrentLowered(s, sizeof(s)); | | |
158 | | } | | |
159 | | if (!isspace(sc.ch) || !keywords3.InList(s + 1)) { | | 160 | | if (sc.Match('/', '*') || sc.Match('/', '/')) { |
160 | | sc.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR); | | 161 | | sc.SetState(SCE_C_DEFAULT); |
| | 162 | | } |
|
162 | | sc.SetState(SCE_C_COMMENTDOC); | | 164 | | break; |
163 | | } | | |
164 | | } else if (sc.state == SCE_C_STRING) { | | 165 | | case SCE_C_COMMENT: |
165 | | if (sc.ch == '\\') { | | |
166 | | if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') { | | 166 | | if (sc.Match('*', '/')) { |
|
| | 168 | | sc.ForwardSetState(SCE_C_DEFAULT); |
|
169 | | } else if (sc.ch == '\"') { | | 170 | | break; |
170 | | sc.ForwardSetState(SCE_C_DEFAULT); | | |
171 | | } else if (sc.atLineEnd) { | | |
172 | | sc.ChangeState(SCE_C_STRINGEOL); | | |
173 | | sc.ForwardSetState(SCE_C_DEFAULT); | | |
174 | | visibleChars = 0; | | |
175 | | } | | |
176 | | } else if (sc.state == SCE_C_CHARACTER) { | | |
177 | | if (sc.atLineEnd) { | | |
178 | | sc.ChangeState(SCE_C_STRINGEOL); | | |
179 | | sc.ForwardSetState(SCE_C_DEFAULT); | | 171 | | case SCE_C_COMMENTDOC: |
180 | | visibleChars = 0; | | |
181 | | } else if (sc.ch == '\\') { | | 172 | | if (sc.Match('*', '/')) { |
182 | | if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') { | | |
|
| | 174 | | sc.ForwardSetState(SCE_C_DEFAULT); |
| | 175 | | } else if (sc.ch == '@' || sc.ch == '\\') { // JavaDoc and Doxygen support |
| | 176 | | // Verify that we have the conditions to mark a comment-doc-keyword |
| | 177 | | if ((IsASpace(sc.chPrev) || sc.chPrev == '*') && (!IsASpace(sc.chNext))) { |
| | 178 | | styleBeforeDCKeyword = SCE_C_COMMENTDOC; |
| | 179 | | sc.SetState(SCE_C_COMMENTDOCKEYWORD); |
| | 180 | | } |
|
185 | | } else if (sc.ch == '\'') { | | 182 | | break; |
186 | | sc.ForwardSetState(SCE_C_DEFAULT); | | |
187 | | } | | |
188 | | } else if (sc.state == SCE_C_REGEX) { | | 183 | | case SCE_C_COMMENTLINE: |
189 | | if (sc.ch == '\r' || sc.ch == '\n' || sc.ch == '/') { | | 184 | | if (sc.atLineStart) { |
190 | | sc.ForwardSetState(SCE_C_DEFAULT); | | 185 | | sc.SetState(SCE_C_DEFAULT); |
191 | | } else if (sc.ch == '\\') { | | |
192 | | // Gobble up the quoted character | | |
193 | | if (sc.chNext == '\\' || sc.chNext == '/') { | | |
194 | | sc.Forward(); | | |
|
196 | | } | | 187 | | break; |
| | 188 | | case SCE_C_COMMENTLINEDOC: |
| | 189 | | if (sc.atLineStart) { |
| | 190 | | sc.SetState(SCE_C_DEFAULT); |
197 | | } else if (sc.state == SCE_C_VERBATIM) { | | 191 | | } else if (sc.ch == '@' || sc.ch == '\\') { // JavaDoc and Doxygen support |
| | 192 | | // Verify that we have the conditions to mark a comment-doc-keyword |
198 | | if (sc.ch == '\"') { | | 193 | | if ((IsASpace(sc.chPrev) || sc.chPrev == '/' || sc.chPrev == '!') && (!IsASpace(sc.chNext))) { |
| | 194 | | styleBeforeDCKeyword = SCE_C_COMMENTLINEDOC; |
| | 195 | | sc.SetState(SCE_C_COMMENTDOCKEYWORD); |
| | 196 | | } |
| | 197 | | } |
| | 198 | | break; |
| | 199 | | case SCE_C_COMMENTDOCKEYWORD: |
199 | | if (sc.chNext == '\"') { | | 200 | | if ((styleBeforeDCKeyword == SCE_C_COMMENTDOC) && sc.Match('*', '/')) { |
| | 201 | | sc.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR); |
|
201 | | } else { | | |
202 | | 203 | | sc.ForwardSetState(SCE_C_DEFAULT); |
|
| | 204 | | } else if (!IsADoxygenChar(sc.ch)) { |
| | 205 | | char s[100]; |
| | 206 | | if (caseSensitive) { |
| | 207 | | sc.GetCurrent(s, sizeof(s)); |
| | 208 | | } else { |
57 skipped lines |
| | 266 | | } |
| | 267 | | break; |
| | 268 | | case SCE_C_UUID: |
| | 269 | | if (sc.ch == '\r' || sc.ch == '\n' || sc.ch == ')') { |
| | 270 | | sc.SetState(SCE_C_DEFAULT); |
|
204 | | } | | |
205 | | } else if (sc.state == SCE_C_UUID) { | | |
206 | | if (sc.ch == '\r' || sc.ch == '\n' || sc.ch == ')') { | | |
207 | | sc.SetState(SCE_C_DEFAULT); | | |
208 | | } | | |
|
|
211 | | 274 | | // Determine if a new state should be entered. |
|
29 skipped lines |
|
242 | | 305 | | sc.SetState(SCE_C_COMMENTLINE); |
|
243 | | 306 | | } else if (sc.ch == '/' && IsOKBeforeRE(chPrevNonWhite)) { |
|
244 | | sc.SetState(SCE_C_REGEX); | | 307 | | sc.SetState(SCE_C_REGEX); // JavaScript's RegEx |
245 | | 308 | | } else if (sc.ch == '\"') { |
|
246 | | 309 | | sc.SetState(SCE_C_STRING); |
|
247 | | 310 | | } else if (sc.ch == '\'') { |
|
5 skipped lines |
|
|
255 | | 318 | | } while ((sc.ch == ' ' || sc.ch == '\t') && sc.More()); |
|
256 | | if (sc.ch == '\r' || sc.ch == '\n') { | | 319 | | if (sc.atLineEnd) { |
257 | | 320 | | sc.SetState(SCE_C_DEFAULT); |
|
|
259 | | 322 | | } else if (isoperator(static_cast<char>(sc.ch))) { |
|
1 skipped line |
|
|
|
264 | | if (sc.atLineEnd) { | | |
265 | | // Reset states to begining of colourise so no surprises | | |
266 | | // if different sets of lines lexed. | | |
267 | | chPrevNonWhite = ' '; | | |
268 | | visibleChars = 0; | | |
269 | | lastWordWasUUID = false; | | |
270 | | } | | |
271 | | if (!IsASpace(sc.ch)) { | | 327 | | if (!IsASpace(sc.ch) && !IsSpaceEquiv(sc.state)) { |
272 | | 328 | | chPrevNonWhite = sc.ch; |
|
|
|
| | 331 | | continuationLine = false; |
|
|
|
|
279 | | 336 | | static bool IsStreamCommentStyle(int style) { |
|
280 | | 337 | | return style == SCE_C_COMMENT || |
|
281 | | style == SCE_C_COMMENTDOC || | | 338 | | style == SCE_C_COMMENTDOC || |
282 | | style == SCE_C_COMMENTDOCKEYWORD || | | 339 | | style == SCE_C_COMMENTDOCKEYWORD || |
283 | | style == SCE_C_COMMENTDOCKEYWORDERROR; | | 340 | | style == SCE_C_COMMENTDOCKEYWORDERROR; |
|
|
286 | | 343 | | // Store both the current line's fold level and the next lines in the |
|
120 skipped lines |