1 // Scintilla source code edit control
3 ** Lexer for Cascading Style Sheets
4 ** Written by Jakub Vrána
5 ** Improved by Philippe Lhoste (CSS2)
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.
20 #include "StyleContext.h"
22 #include "Scintilla.h"
26 using namespace Scintilla
;
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
34 inline bool IsCssOperator(const char ch
) {
36 (ch
== '{' || ch
== '}' || ch
== ':' || ch
== ',' || ch
== ';' ||
37 ch
== '.' || ch
== '#' || ch
== '!' || ch
== '@' ||
39 ch
== '*' || ch
== '>' || ch
== '+' || ch
== '=' || ch
== '~' || ch
== '|' ||
40 ch
== '[' || ch
== ']' || ch
== '(' || ch
== ')')) {
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];
51 StyleContext
sc(startPos
, length
, initStyle
, styler
);
53 int lastState
= -1; // before operator
54 int lastStateC
= -1; // before comment
55 int op
= ' '; // last operator
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
;
64 if ((lastStateC
= styler
.StyleAt(i
-1)) != SCE_CSS_COMMENT
) {
65 if (lastStateC
== SCE_CSS_OPERATOR
) {
66 op
= styler
.SafeGetCharAt(i
-1);
68 lastState
= styler
.StyleAt(i
-1);
69 if (lastState
!= SCE_CSS_OPERATOR
&& lastState
!= SCE_CSS_COMMENT
)
73 lastState
= SCE_CSS_DEFAULT
;
79 lastStateC
= SCE_CSS_DEFAULT
;
82 sc
.ForwardSetState(lastStateC
);
85 if (sc
.state
== SCE_CSS_COMMENT
)
88 if (sc
.state
== SCE_CSS_DOUBLESTRING
|| sc
.state
== SCE_CSS_SINGLESTRING
) {
89 if (sc
.ch
!= (sc
.state
== SCE_CSS_DOUBLESTRING
? '\"' : '\''))
91 unsigned int i
= sc
.currentPos
;
92 while (i
&& styler
[i
-1] == '\\')
94 if ((sc
.currentPos
- i
) % 2 == 1)
96 sc
.ForwardSetState(SCE_CSS_VALUE
);
99 if (sc
.state
== SCE_CSS_OPERATOR
) {
101 unsigned int i
= startPos
;
102 op
= styler
.SafeGetCharAt(i
-1);
104 lastState
= styler
.StyleAt(i
-1);
105 if (lastState
!= SCE_CSS_OPERATOR
&& lastState
!= SCE_CSS_COMMENT
)
111 if (lastState
== SCE_CSS_DEFAULT
)
112 sc
.SetState(SCE_CSS_DIRECTIVE
);
115 if (lastState
== SCE_CSS_DEFAULT
)
116 sc
.SetState(SCE_CSS_TAG
);
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
);
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
);
130 if (lastState
== SCE_CSS_ATTRIBUTE
)
131 sc
.SetState(SCE_CSS_TAG
);
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
);
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
);
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
);
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
);
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
);
162 if (lastState
== SCE_CSS_TAG
)
163 sc
.SetState(SCE_CSS_DEFAULT
);
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
);
172 if (lastState
== SCE_CSS_VALUE
)
173 sc
.SetState(SCE_CSS_IMPORTANT
);
178 if (IsAWordChar(sc
.ch
)) {
179 if (sc
.state
== SCE_CSS_DEFAULT
)
180 sc
.SetState(SCE_CSS_TAG
);
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
191 sc
.GetCurrentLowered(s
, sizeof(s
));
193 while (*s2
&& !IsAWordChar(*s2
))
196 case SCE_CSS_IDENTIFIER
:
197 if (!keywords
.InList(s2
)) {
198 if (keywords2
.InList(s2
)) {
199 sc
.ChangeState(SCE_CSS_IDENTIFIER2
);
201 sc
.ChangeState(SCE_CSS_UNKNOWN_IDENTIFIER
);
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
);
211 case SCE_CSS_PSEUDOCLASS
:
212 if (!pseudoClasses
.InList(s2
))
213 sc
.ChangeState(SCE_CSS_UNKNOWN_PSEUDOCLASS
);
215 case SCE_CSS_UNKNOWN_PSEUDOCLASS
:
216 if (pseudoClasses
.InList(s2
))
217 sc
.ChangeState(SCE_CSS_PSEUDOCLASS
);
219 case SCE_CSS_IMPORTANT
:
220 if (strcmp(s2
, "important") != 0)
221 sc
.ChangeState(SCE_CSS_VALUE
);
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
);
229 if (sc
.Match('/', '*')) {
230 lastStateC
= sc
.state
;
231 sc
.SetState(SCE_CSS_COMMENT
);
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
== '{')
240 if (sc
.state
!= SCE_CSS_OPERATOR
)
241 lastState
= sc
.state
;
242 sc
.SetState(SCE_CSS_OPERATOR
);
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
++) {
262 chNext
= styler
.SafeGetCharAt(i
+ 1);
263 int style
= styler
.StyleAt(i
);
264 bool atEOL
= (ch
== '\r' && chNext
!= '\n') || (ch
== '\n');
266 if (!inComment
&& (style
== SCE_CSS_COMMENT
))
268 else if (inComment
&& (style
!= SCE_CSS_COMMENT
))
270 inComment
= (style
== SCE_CSS_COMMENT
);
272 if (style
== SCE_CSS_OPERATOR
) {
275 } else if (ch
== '}') {
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
);
289 levelPrev
= levelCurrent
;
292 if (!isspacechar(ch
))
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
);
300 static const char * const cssWordListDesc
[] = {
307 LexerModule
lmCss(SCLEX_CSS
, ColouriseCssDoc
, "css", FoldCSSDoc
, cssWordListDesc
);