]> git.saurik.com Git - wxWidgets.git/blob - src/stc/scintilla/src/LexCSS.cxx
f5c112d6f1501815d02559fe5af177951e000e92
[wxWidgets.git] / src / stc / scintilla / src / LexCSS.cxx
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.
9
10 #include <stdlib.h>
11 #include <string.h>
12 #include <ctype.h>
13 #include <stdio.h>
14 #include <stdarg.h>
15
16 #include "Platform.h"
17
18 #include "PropSet.h"
19 #include "Accessor.h"
20 #include "StyleContext.h"
21 #include "KeyWords.h"
22 #include "Scintilla.h"
23 #include "SciLexer.h"
24
25 #ifdef SCI_NAMESPACE
26 using namespace Scintilla;
27 #endif
28
29
30 static inline bool IsAWordChar(const unsigned int ch) {
31 return (isalnum(ch) || ch == '-' || ch == '_' || ch >= 161); // _ is not in fact correct CSS word-character
32 }
33
34 inline bool IsCssOperator(const char ch) {
35 if (!isalnum(ch) &&
36 (ch == '{' || ch == '}' || ch == ':' || ch == ',' || ch == ';' ||
37 ch == '.' || ch == '#' || ch == '!' || ch == '@' ||
38 /* CSS2 */
39 ch == '*' || ch == '>' || ch == '+' || ch == '=' || ch == '~' || ch == '|' ||
40 ch == '[' || ch == ']' || ch == '(' || ch == ')')) {
41 return true;
42 }
43 return false;
44 }
45
46 static void ColouriseCssDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[], Accessor &styler) {
47 WordList &keywords = *keywordlists[0];
48 WordList &pseudoClasses = *keywordlists[1];
49 WordList &keywords2 = *keywordlists[2];
50
51 StyleContext sc(startPos, length, initStyle, styler);
52
53 int lastState = -1; // before operator
54 int lastStateC = -1; // before comment
55 int op = ' '; // last operator
56
57 for (; sc.More(); sc.Forward()) {
58 if (sc.state == SCE_CSS_COMMENT && sc.Match('*', '/')) {
59 if (lastStateC == -1) {
60 // backtrack to get last state:
61 // comments are like whitespace, so we must return to the previous state
62 unsigned int i = startPos;
63 for (; i > 0; i--) {
64 if ((lastStateC = styler.StyleAt(i-1)) != SCE_CSS_COMMENT) {
65 if (lastStateC == SCE_CSS_OPERATOR) {
66 op = styler.SafeGetCharAt(i-1);
67 while (--i) {
68 lastState = styler.StyleAt(i-1);
69 if (lastState != SCE_CSS_OPERATOR && lastState != SCE_CSS_COMMENT)
70 break;
71 }
72 if (i == 0)
73 lastState = SCE_CSS_DEFAULT;
74 }
75 break;
76 }
77 }
78 if (i == 0)
79 lastStateC = SCE_CSS_DEFAULT;
80 }
81 sc.Forward();
82 sc.ForwardSetState(lastStateC);
83 }
84
85 if (sc.state == SCE_CSS_COMMENT)
86 continue;
87
88 if (sc.state == SCE_CSS_DOUBLESTRING || sc.state == SCE_CSS_SINGLESTRING) {
89 if (sc.ch != (sc.state == SCE_CSS_DOUBLESTRING ? '\"' : '\''))
90 continue;
91 unsigned int i = sc.currentPos;
92 while (i && styler[i-1] == '\\')
93 i--;
94 if ((sc.currentPos - i) % 2 == 1)
95 continue;
96 sc.ForwardSetState(SCE_CSS_VALUE);
97 }
98
99 if (sc.state == SCE_CSS_OPERATOR) {
100 if (op == ' ') {
101 unsigned int i = startPos;
102 op = styler.SafeGetCharAt(i-1);
103 while (--i) {
104 lastState = styler.StyleAt(i-1);
105 if (lastState != SCE_CSS_OPERATOR && lastState != SCE_CSS_COMMENT)
106 break;
107 }
108 }
109 switch (op) {
110 case '@':
111 if (lastState == SCE_CSS_DEFAULT)
112 sc.SetState(SCE_CSS_DIRECTIVE);
113 break;
114 case '*':
115 if (lastState == SCE_CSS_DEFAULT)
116 sc.SetState(SCE_CSS_TAG);
117 break;
118 case '>':
119 case '+':
120 if (lastState == SCE_CSS_TAG || lastState == SCE_CSS_PSEUDOCLASS || lastState == SCE_CSS_CLASS
121 || lastState == SCE_CSS_ID || lastState == SCE_CSS_UNKNOWN_PSEUDOCLASS)
122 sc.SetState(SCE_CSS_DEFAULT);
123 break;
124 case '[':
125 if (lastState == SCE_CSS_TAG || lastState == SCE_CSS_PSEUDOCLASS || lastState == SCE_CSS_DEFAULT ||
126 lastState == SCE_CSS_CLASS || lastState == SCE_CSS_ID || lastState == SCE_CSS_UNKNOWN_PSEUDOCLASS)
127 sc.SetState(SCE_CSS_ATTRIBUTE);
128 break;
129 case ']':
130 if (lastState == SCE_CSS_ATTRIBUTE)
131 sc.SetState(SCE_CSS_TAG);
132 break;
133 case '{':
134 if (lastState == SCE_CSS_DIRECTIVE)
135 sc.SetState(SCE_CSS_DEFAULT);
136 else if (lastState == SCE_CSS_TAG)
137 sc.SetState(SCE_CSS_IDENTIFIER);
138 break;
139 case '}':
140 if (lastState == SCE_CSS_DEFAULT || lastState == SCE_CSS_VALUE || lastState == SCE_CSS_IMPORTANT ||
141 lastState == SCE_CSS_IDENTIFIER || lastState == SCE_CSS_IDENTIFIER2)
142 sc.SetState(SCE_CSS_DEFAULT);
143 break;
144 case ':':
145 if (lastState == SCE_CSS_TAG || lastState == SCE_CSS_PSEUDOCLASS || lastState == SCE_CSS_DEFAULT ||
146 lastState == SCE_CSS_CLASS || lastState == SCE_CSS_ID || lastState == SCE_CSS_UNKNOWN_PSEUDOCLASS)
147 sc.SetState(SCE_CSS_PSEUDOCLASS);
148 else if (lastState == SCE_CSS_IDENTIFIER || lastState == SCE_CSS_IDENTIFIER2 || lastState == SCE_CSS_UNKNOWN_IDENTIFIER)
149 sc.SetState(SCE_CSS_VALUE);
150 break;
151 case '.':
152 if (lastState == SCE_CSS_TAG || lastState == SCE_CSS_PSEUDOCLASS || lastState == SCE_CSS_DEFAULT ||
153 lastState == SCE_CSS_CLASS || lastState == SCE_CSS_ID || lastState == SCE_CSS_UNKNOWN_PSEUDOCLASS)
154 sc.SetState(SCE_CSS_CLASS);
155 break;
156 case '#':
157 if (lastState == SCE_CSS_TAG || lastState == SCE_CSS_PSEUDOCLASS || lastState == SCE_CSS_DEFAULT ||
158 lastState == SCE_CSS_CLASS || lastState == SCE_CSS_ID || lastState == SCE_CSS_UNKNOWN_PSEUDOCLASS)
159 sc.SetState(SCE_CSS_ID);
160 break;
161 case ',':
162 if (lastState == SCE_CSS_TAG)
163 sc.SetState(SCE_CSS_DEFAULT);
164 break;
165 case ';':
166 if (lastState == SCE_CSS_DIRECTIVE)
167 sc.SetState(SCE_CSS_DEFAULT);
168 else if (lastState == SCE_CSS_VALUE || lastState == SCE_CSS_IMPORTANT)
169 sc.SetState(SCE_CSS_IDENTIFIER);
170 break;
171 case '!':
172 if (lastState == SCE_CSS_VALUE)
173 sc.SetState(SCE_CSS_IMPORTANT);
174 break;
175 }
176 }
177
178 if (IsAWordChar(sc.ch)) {
179 if (sc.state == SCE_CSS_DEFAULT)
180 sc.SetState(SCE_CSS_TAG);
181 continue;
182 }
183
184 if (IsAWordChar(sc.chPrev) && (
185 sc.state == SCE_CSS_IDENTIFIER || sc.state == SCE_CSS_IDENTIFIER2
186 || sc.state == SCE_CSS_UNKNOWN_IDENTIFIER
187 || sc.state == SCE_CSS_PSEUDOCLASS || sc.state == SCE_CSS_UNKNOWN_PSEUDOCLASS
188 || sc.state == SCE_CSS_IMPORTANT
189 )) {
190 char s[100];
191 sc.GetCurrentLowered(s, sizeof(s));
192 char *s2 = s;
193 while (*s2 && !IsAWordChar(*s2))
194 s2++;
195 switch (sc.state) {
196 case SCE_CSS_IDENTIFIER:
197 if (!keywords.InList(s2)) {
198 if (keywords2.InList(s2)) {
199 sc.ChangeState(SCE_CSS_IDENTIFIER2);
200 } else {
201 sc.ChangeState(SCE_CSS_UNKNOWN_IDENTIFIER);
202 }
203 }
204 break;
205 case SCE_CSS_UNKNOWN_IDENTIFIER:
206 if (keywords.InList(s2))
207 sc.ChangeState(SCE_CSS_IDENTIFIER);
208 else if (keywords2.InList(s2))
209 sc.ChangeState(SCE_CSS_IDENTIFIER2);
210 break;
211 case SCE_CSS_PSEUDOCLASS:
212 if (!pseudoClasses.InList(s2))
213 sc.ChangeState(SCE_CSS_UNKNOWN_PSEUDOCLASS);
214 break;
215 case SCE_CSS_UNKNOWN_PSEUDOCLASS:
216 if (pseudoClasses.InList(s2))
217 sc.ChangeState(SCE_CSS_PSEUDOCLASS);
218 break;
219 case SCE_CSS_IMPORTANT:
220 if (strcmp(s2, "important") != 0)
221 sc.ChangeState(SCE_CSS_VALUE);
222 break;
223 }
224 }
225
226 if (sc.ch != '.' && sc.ch != ':' && sc.ch != '#' && (sc.state == SCE_CSS_CLASS || sc.state == SCE_CSS_PSEUDOCLASS || sc.state == SCE_CSS_UNKNOWN_PSEUDOCLASS || sc.state == SCE_CSS_ID))
227 sc.SetState(SCE_CSS_TAG);
228
229 if (sc.Match('/', '*')) {
230 lastStateC = sc.state;
231 sc.SetState(SCE_CSS_COMMENT);
232 sc.Forward();
233 } else if (sc.state == SCE_CSS_VALUE && (sc.ch == '\"' || sc.ch == '\'')) {
234 sc.SetState((sc.ch == '\"' ? SCE_CSS_DOUBLESTRING : SCE_CSS_SINGLESTRING));
235 } else if (IsCssOperator(static_cast<char>(sc.ch))
236 && (sc.state != SCE_CSS_ATTRIBUTE || sc.ch == ']')
237 && (sc.state != SCE_CSS_VALUE || sc.ch == ';' || sc.ch == '}' || sc.ch == '!')
238 && (sc.state != SCE_CSS_DIRECTIVE || sc.ch == ';' || sc.ch == '{')
239 ) {
240 if (sc.state != SCE_CSS_OPERATOR)
241 lastState = sc.state;
242 sc.SetState(SCE_CSS_OPERATOR);
243 op = sc.ch;
244 }
245 }
246
247 sc.Complete();
248 }
249
250 static void FoldCSSDoc(unsigned int startPos, int length, int, WordList *[], Accessor &styler) {
251 bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
252 bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
253 unsigned int endPos = startPos + length;
254 int visibleChars = 0;
255 int lineCurrent = styler.GetLine(startPos);
256 int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
257 int levelCurrent = levelPrev;
258 char chNext = styler[startPos];
259 bool inComment = (styler.StyleAt(startPos-1) == SCE_CSS_COMMENT);
260 for (unsigned int i = startPos; i < endPos; i++) {
261 char ch = chNext;
262 chNext = styler.SafeGetCharAt(i + 1);
263 int style = styler.StyleAt(i);
264 bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
265 if (foldComment) {
266 if (!inComment && (style == SCE_CSS_COMMENT))
267 levelCurrent++;
268 else if (inComment && (style != SCE_CSS_COMMENT))
269 levelCurrent--;
270 inComment = (style == SCE_CSS_COMMENT);
271 }
272 if (style == SCE_CSS_OPERATOR) {
273 if (ch == '{') {
274 levelCurrent++;
275 } else if (ch == '}') {
276 levelCurrent--;
277 }
278 }
279 if (atEOL) {
280 int lev = levelPrev;
281 if (visibleChars == 0 && foldCompact)
282 lev |= SC_FOLDLEVELWHITEFLAG;
283 if ((levelCurrent > levelPrev) && (visibleChars > 0))
284 lev |= SC_FOLDLEVELHEADERFLAG;
285 if (lev != styler.LevelAt(lineCurrent)) {
286 styler.SetLevel(lineCurrent, lev);
287 }
288 lineCurrent++;
289 levelPrev = levelCurrent;
290 visibleChars = 0;
291 }
292 if (!isspacechar(ch))
293 visibleChars++;
294 }
295 // Fill in the real level of the next line, keeping the current flags as they will be filled in later
296 int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK;
297 styler.SetLevel(lineCurrent, levelPrev | flagsNext);
298 }
299
300 static const char * const cssWordListDesc[] = {
301 "CSS1 Keywords",
302 "Pseudo classes",
303 "CSS2 Keywords",
304 0
305 };
306
307 LexerModule lmCss(SCLEX_CSS, ColouriseCssDoc, "css", FoldCSSDoc, cssWordListDesc);