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