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