Add an UI to enable/disable specific overlay handlers.
[TortoiseGit.git] / ext / scintilla / src / LexCSS.cxx
blob32a0068b8398e6b9529cfa84cd9af4df15265fa6
1 // Scintilla source code edit control
2 /** @file LexCSS.cxx
3 ** Lexer for Cascading Style Sheets
4 ** Written by Jakub Vrána
5 ** Improved by Philippe Lhoste (CSS2)
6 **/
7 // Copyright 1998-2002 by Neil Hodgson <neilh@scintilla.org>
8 // The License.txt file describes the conditions under which this software may be distributed.
10 #include <stdlib.h>
11 #include <string.h>
12 #include <ctype.h>
13 #include <stdio.h>
14 #include <stdarg.h>
16 #include "Platform.h"
18 #include "PropSet.h"
19 #include "Accessor.h"
20 #include "StyleContext.h"
21 #include "KeyWords.h"
22 #include "Scintilla.h"
23 #include "SciLexer.h"
25 #ifdef SCI_NAMESPACE
26 using namespace Scintilla;
27 #endif
30 static inline bool IsAWordChar(const unsigned int ch) {
31 /* FIXME:
32 * The CSS spec allows "ISO 10646 characters U+00A1 and higher" to be treated as word chars.
33 * Unfortunately, we are only getting string bytes here, and not full unicode characters. We cannot guarantee
34 * that our byte is between U+0080 - U+00A0 (to return false), so we have to allow all characters U+0080 and higher
36 return ch >= 0x80 || isalnum(ch) || ch == '-' || ch == '_';
39 inline bool IsCssOperator(const int ch) {
40 if (!((ch < 0x80) && isalnum(ch)) &&
41 (ch == '{' || ch == '}' || ch == ':' || ch == ',' || ch == ';' ||
42 ch == '.' || ch == '#' || ch == '!' || ch == '@' ||
43 /* CSS2 */
44 ch == '*' || ch == '>' || ch == '+' || ch == '=' || ch == '~' || ch == '|' ||
45 ch == '[' || ch == ']' || ch == '(' || ch == ')')) {
46 return true;
48 return false;
51 static void ColouriseCssDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[], Accessor &styler) {
52 WordList &css1Props = *keywordlists[0];
53 WordList &pseudoClasses = *keywordlists[1];
54 WordList &css2Props = *keywordlists[2];
55 WordList &css3Props = *keywordlists[3];
56 WordList &pseudoElements = *keywordlists[4];
57 WordList &exProps = *keywordlists[5];
58 WordList &exPseudoClasses = *keywordlists[6];
59 WordList &exPseudoElements = *keywordlists[7];
61 StyleContext sc(startPos, length, initStyle, styler);
63 int lastState = -1; // before operator
64 int lastStateC = -1; // before comment
65 int op = ' '; // last operator
66 int opPrev = ' '; // last operator
68 for (; sc.More(); sc.Forward()) {
69 if (sc.state == SCE_CSS_COMMENT && sc.Match('*', '/')) {
70 if (lastStateC == -1) {
71 // backtrack to get last state:
72 // comments are like whitespace, so we must return to the previous state
73 unsigned int i = startPos;
74 for (; i > 0; i--) {
75 if ((lastStateC = styler.StyleAt(i-1)) != SCE_CSS_COMMENT) {
76 if (lastStateC == SCE_CSS_OPERATOR) {
77 op = styler.SafeGetCharAt(i-1);
78 opPrev = styler.SafeGetCharAt(i-2);
79 while (--i) {
80 lastState = styler.StyleAt(i-1);
81 if (lastState != SCE_CSS_OPERATOR && lastState != SCE_CSS_COMMENT)
82 break;
84 if (i == 0)
85 lastState = SCE_CSS_DEFAULT;
87 break;
90 if (i == 0)
91 lastStateC = SCE_CSS_DEFAULT;
93 sc.Forward();
94 sc.ForwardSetState(lastStateC);
97 if (sc.state == SCE_CSS_COMMENT)
98 continue;
100 if (sc.state == SCE_CSS_DOUBLESTRING || sc.state == SCE_CSS_SINGLESTRING) {
101 if (sc.ch != (sc.state == SCE_CSS_DOUBLESTRING ? '\"' : '\''))
102 continue;
103 unsigned int i = sc.currentPos;
104 while (i && styler[i-1] == '\\')
105 i--;
106 if ((sc.currentPos - i) % 2 == 1)
107 continue;
108 sc.ForwardSetState(SCE_CSS_VALUE);
111 if (sc.state == SCE_CSS_OPERATOR) {
112 if (op == ' ') {
113 unsigned int i = startPos;
114 op = styler.SafeGetCharAt(i-1);
115 opPrev = styler.SafeGetCharAt(i-2);
116 while (--i) {
117 lastState = styler.StyleAt(i-1);
118 if (lastState != SCE_CSS_OPERATOR && lastState != SCE_CSS_COMMENT)
119 break;
122 switch (op) {
123 case '@':
124 if (lastState == SCE_CSS_DEFAULT)
125 sc.SetState(SCE_CSS_DIRECTIVE);
126 break;
127 case '>':
128 case '+':
129 if (lastState == SCE_CSS_TAG || lastState == SCE_CSS_CLASS || lastState == SCE_CSS_ID ||
130 lastState == SCE_CSS_PSEUDOCLASS || lastState == SCE_CSS_EXTENDED_PSEUDOCLASS || lastState == SCE_CSS_UNKNOWN_PSEUDOCLASS)
131 sc.SetState(SCE_CSS_DEFAULT);
132 break;
133 case '[':
134 if (lastState == SCE_CSS_TAG || lastState == SCE_CSS_DEFAULT || lastState == SCE_CSS_CLASS || lastState == SCE_CSS_ID ||
135 lastState == SCE_CSS_PSEUDOCLASS || lastState == SCE_CSS_EXTENDED_PSEUDOCLASS || lastState == SCE_CSS_UNKNOWN_PSEUDOCLASS)
136 sc.SetState(SCE_CSS_ATTRIBUTE);
137 break;
138 case ']':
139 if (lastState == SCE_CSS_ATTRIBUTE)
140 sc.SetState(SCE_CSS_TAG);
141 break;
142 case '{':
143 if (lastState == SCE_CSS_DIRECTIVE)
144 sc.SetState(SCE_CSS_DEFAULT);
145 else if (lastState == SCE_CSS_TAG)
146 sc.SetState(SCE_CSS_IDENTIFIER);
147 break;
148 case '}':
149 if (lastState == SCE_CSS_DEFAULT || lastState == SCE_CSS_VALUE || lastState == SCE_CSS_IMPORTANT ||
150 lastState == SCE_CSS_IDENTIFIER || lastState == SCE_CSS_IDENTIFIER2 || lastState == SCE_CSS_IDENTIFIER3)
151 sc.SetState(SCE_CSS_DEFAULT);
152 break;
153 case '(':
154 if (lastState == SCE_CSS_PSEUDOCLASS)
155 sc.SetState(SCE_CSS_TAG);
156 else if (lastState == SCE_CSS_EXTENDED_PSEUDOCLASS)
157 sc.SetState(SCE_CSS_EXTENDED_PSEUDOCLASS);
158 break;
159 case ')':
160 if (lastState == SCE_CSS_TAG || lastState == SCE_CSS_DEFAULT || lastState == SCE_CSS_CLASS || lastState == SCE_CSS_ID ||
161 lastState == SCE_CSS_PSEUDOCLASS || lastState == SCE_CSS_EXTENDED_PSEUDOCLASS || lastState == SCE_CSS_UNKNOWN_PSEUDOCLASS ||
162 lastState == SCE_CSS_PSEUDOELEMENT || lastState == SCE_CSS_EXTENDED_PSEUDOELEMENT)
163 sc.SetState(SCE_CSS_TAG);
164 break;
165 case ':':
166 if (lastState == SCE_CSS_TAG || lastState == SCE_CSS_DEFAULT || lastState == SCE_CSS_CLASS || lastState == SCE_CSS_ID ||
167 lastState == SCE_CSS_PSEUDOCLASS || lastState == SCE_CSS_EXTENDED_PSEUDOCLASS || lastState == SCE_CSS_UNKNOWN_PSEUDOCLASS ||
168 lastState == SCE_CSS_PSEUDOELEMENT || lastState == SCE_CSS_EXTENDED_PSEUDOELEMENT)
169 sc.SetState(SCE_CSS_PSEUDOCLASS);
170 else if (lastState == SCE_CSS_IDENTIFIER || lastState == SCE_CSS_IDENTIFIER2 ||
171 lastState == SCE_CSS_IDENTIFIER3 || lastState == SCE_CSS_EXTENDED_IDENTIFIER ||
172 lastState == SCE_CSS_UNKNOWN_IDENTIFIER)
173 sc.SetState(SCE_CSS_VALUE);
174 break;
175 case '.':
176 if (lastState == SCE_CSS_TAG || lastState == SCE_CSS_DEFAULT || lastState == SCE_CSS_CLASS || lastState == SCE_CSS_ID ||
177 lastState == SCE_CSS_PSEUDOCLASS || lastState == SCE_CSS_EXTENDED_PSEUDOCLASS || lastState == SCE_CSS_UNKNOWN_PSEUDOCLASS)
178 sc.SetState(SCE_CSS_CLASS);
179 break;
180 case '#':
181 if (lastState == SCE_CSS_TAG || lastState == SCE_CSS_DEFAULT || lastState == SCE_CSS_CLASS || lastState == SCE_CSS_ID ||
182 lastState == SCE_CSS_PSEUDOCLASS || lastState == SCE_CSS_EXTENDED_PSEUDOCLASS || lastState == SCE_CSS_UNKNOWN_PSEUDOCLASS)
183 sc.SetState(SCE_CSS_ID);
184 break;
185 case ',':
186 case '|':
187 case '~':
188 if (lastState == SCE_CSS_TAG)
189 sc.SetState(SCE_CSS_DEFAULT);
190 break;
191 case ';':
192 if (lastState == SCE_CSS_DIRECTIVE)
193 sc.SetState(SCE_CSS_DEFAULT);
194 else if (lastState == SCE_CSS_VALUE || lastState == SCE_CSS_IMPORTANT)
195 sc.SetState(SCE_CSS_IDENTIFIER);
196 break;
197 case '!':
198 if (lastState == SCE_CSS_VALUE)
199 sc.SetState(SCE_CSS_IMPORTANT);
200 break;
204 if (IsAWordChar(sc.ch)) {
205 if (sc.state == SCE_CSS_DEFAULT)
206 sc.SetState(SCE_CSS_TAG);
207 continue;
210 if (sc.ch == '*' && sc.state == SCE_CSS_DEFAULT) {
211 sc.SetState(SCE_CSS_TAG);
212 continue;
215 if (IsAWordChar(sc.chPrev) && (
216 sc.state == SCE_CSS_IDENTIFIER || sc.state == SCE_CSS_IDENTIFIER2 ||
217 sc.state == SCE_CSS_IDENTIFIER3 || sc.state == SCE_CSS_EXTENDED_IDENTIFIER ||
218 sc.state == SCE_CSS_UNKNOWN_IDENTIFIER ||
219 sc.state == SCE_CSS_PSEUDOCLASS || sc.state == SCE_CSS_PSEUDOELEMENT ||
220 sc.state == SCE_CSS_EXTENDED_PSEUDOCLASS || sc.state == SCE_CSS_EXTENDED_PSEUDOELEMENT ||
221 sc.state == SCE_CSS_UNKNOWN_PSEUDOCLASS ||
222 sc.state == SCE_CSS_IMPORTANT
223 )) {
224 char s[100];
225 sc.GetCurrentLowered(s, sizeof(s));
226 char *s2 = s;
227 while (*s2 && !IsAWordChar(*s2))
228 s2++;
229 switch (sc.state) {
230 case SCE_CSS_IDENTIFIER:
231 case SCE_CSS_IDENTIFIER2:
232 case SCE_CSS_IDENTIFIER3:
233 case SCE_CSS_EXTENDED_IDENTIFIER:
234 case SCE_CSS_UNKNOWN_IDENTIFIER:
235 if (css1Props.InList(s2))
236 sc.ChangeState(SCE_CSS_IDENTIFIER);
237 else if (css2Props.InList(s2))
238 sc.ChangeState(SCE_CSS_IDENTIFIER2);
239 else if (css3Props.InList(s2))
240 sc.ChangeState(SCE_CSS_IDENTIFIER3);
241 else if (exProps.InList(s2))
242 sc.ChangeState(SCE_CSS_EXTENDED_IDENTIFIER);
243 else
244 sc.ChangeState(SCE_CSS_UNKNOWN_IDENTIFIER);
245 break;
246 case SCE_CSS_PSEUDOCLASS:
247 case SCE_CSS_PSEUDOELEMENT:
248 case SCE_CSS_EXTENDED_PSEUDOCLASS:
249 case SCE_CSS_EXTENDED_PSEUDOELEMENT:
250 case SCE_CSS_UNKNOWN_PSEUDOCLASS:
251 if (op == ':' && opPrev != ':' && pseudoClasses.InList(s2))
252 sc.ChangeState(SCE_CSS_PSEUDOCLASS);
253 else if (opPrev == ':' && pseudoElements.InList(s2))
254 sc.ChangeState(SCE_CSS_PSEUDOELEMENT);
255 else if ((op == ':' || (op == '(' && lastState == SCE_CSS_EXTENDED_PSEUDOCLASS)) && opPrev != ':' && exPseudoClasses.InList(s2))
256 sc.ChangeState(SCE_CSS_EXTENDED_PSEUDOCLASS);
257 else if (opPrev == ':' && exPseudoElements.InList(s2))
258 sc.ChangeState(SCE_CSS_EXTENDED_PSEUDOELEMENT);
259 else
260 sc.ChangeState(SCE_CSS_UNKNOWN_PSEUDOCLASS);
261 break;
262 case SCE_CSS_IMPORTANT:
263 if (strcmp(s2, "important") != 0)
264 sc.ChangeState(SCE_CSS_VALUE);
265 break;
269 if (sc.ch != '.' && sc.ch != ':' && sc.ch != '#' && (
270 sc.state == SCE_CSS_CLASS || sc.state == SCE_CSS_ID ||
271 (sc.ch != '(' && sc.ch != ')' && ( /* This line of the condition makes it possible to extend pseudo-classes with parentheses */
272 sc.state == SCE_CSS_PSEUDOCLASS || sc.state == SCE_CSS_PSEUDOELEMENT ||
273 sc.state == SCE_CSS_EXTENDED_PSEUDOCLASS || sc.state == SCE_CSS_EXTENDED_PSEUDOELEMENT ||
274 sc.state == SCE_CSS_UNKNOWN_PSEUDOCLASS
277 sc.SetState(SCE_CSS_TAG);
279 if (sc.Match('/', '*')) {
280 lastStateC = sc.state;
281 sc.SetState(SCE_CSS_COMMENT);
282 sc.Forward();
283 } else if (sc.state == SCE_CSS_VALUE && (sc.ch == '\"' || sc.ch == '\'')) {
284 sc.SetState((sc.ch == '\"' ? SCE_CSS_DOUBLESTRING : SCE_CSS_SINGLESTRING));
285 } else if (IsCssOperator(sc.ch)
286 && (sc.state != SCE_CSS_ATTRIBUTE || sc.ch == ']')
287 && (sc.state != SCE_CSS_VALUE || sc.ch == ';' || sc.ch == '}' || sc.ch == '!')
288 && (sc.state != SCE_CSS_DIRECTIVE || sc.ch == ';' || sc.ch == '{')
290 if (sc.state != SCE_CSS_OPERATOR)
291 lastState = sc.state;
292 sc.SetState(SCE_CSS_OPERATOR);
293 op = sc.ch;
294 opPrev = sc.chPrev;
298 sc.Complete();
301 static void FoldCSSDoc(unsigned int startPos, int length, int, WordList *[], Accessor &styler) {
302 bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
303 bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
304 unsigned int endPos = startPos + length;
305 int visibleChars = 0;
306 int lineCurrent = styler.GetLine(startPos);
307 int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
308 int levelCurrent = levelPrev;
309 char chNext = styler[startPos];
310 bool inComment = (styler.StyleAt(startPos-1) == SCE_CSS_COMMENT);
311 for (unsigned int i = startPos; i < endPos; i++) {
312 char ch = chNext;
313 chNext = styler.SafeGetCharAt(i + 1);
314 int style = styler.StyleAt(i);
315 bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
316 if (foldComment) {
317 if (!inComment && (style == SCE_CSS_COMMENT))
318 levelCurrent++;
319 else if (inComment && (style != SCE_CSS_COMMENT))
320 levelCurrent--;
321 inComment = (style == SCE_CSS_COMMENT);
323 if (style == SCE_CSS_OPERATOR) {
324 if (ch == '{') {
325 levelCurrent++;
326 } else if (ch == '}') {
327 levelCurrent--;
330 if (atEOL) {
331 int lev = levelPrev;
332 if (visibleChars == 0 && foldCompact)
333 lev |= SC_FOLDLEVELWHITEFLAG;
334 if ((levelCurrent > levelPrev) && (visibleChars > 0))
335 lev |= SC_FOLDLEVELHEADERFLAG;
336 if (lev != styler.LevelAt(lineCurrent)) {
337 styler.SetLevel(lineCurrent, lev);
339 lineCurrent++;
340 levelPrev = levelCurrent;
341 visibleChars = 0;
343 if (!isspacechar(ch))
344 visibleChars++;
346 // Fill in the real level of the next line, keeping the current flags as they will be filled in later
347 int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK;
348 styler.SetLevel(lineCurrent, levelPrev | flagsNext);
351 static const char * const cssWordListDesc[] = {
352 "CSS1 Properties",
353 "Pseudo-classes",
354 "CSS2 Properties",
355 "CSS3 Properties",
356 "Pseudo-elements",
357 "Browser-Specific CSS Properties",
358 "Browser-Specific Pseudo-classes",
359 "Browser-Specific Pseudo-elements",
363 LexerModule lmCss(SCLEX_CSS, ColouriseCssDoc, "css", FoldCSSDoc, cssWordListDesc);