1 // Scintilla source code edit control
3 ** Lexer for Cascade Style Sheets
4 ** Written by Jakub Vrána
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.
19 #include "StyleContext.h"
21 #include "Scintilla.h"
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
28 inline bool IsCssOperator(const char ch
) {
29 if (!isalnum(ch
) && (ch
== '{' || ch
== '}' || ch
== ':' || ch
== ',' || ch
== ';' || ch
== '.' || ch
== '#' || ch
== '!' || ch
== '@'))
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];
38 StyleContext
sc(startPos
, length
, initStyle
, styler
);
40 int lastState
= -1; // before operator
41 int lastStateC
= -1; // before comment
42 int op
= ' '; // last operator
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
;
50 if ((lastStateC
= styler
.StyleAt(i
-1)) != SCE_CSS_COMMENT
) {
51 if (lastStateC
== SCE_CSS_OPERATOR
) {
52 op
= styler
.SafeGetCharAt(i
-1);
54 lastState
= styler
.StyleAt(i
-1);
55 if (lastState
!= SCE_CSS_OPERATOR
&& lastState
!= SCE_CSS_COMMENT
)
59 lastState
= SCE_CSS_DEFAULT
;
65 lastStateC
= SCE_CSS_DEFAULT
;
68 sc
.ForwardSetState(lastStateC
);
71 if (sc
.state
== SCE_CSS_COMMENT
)
74 if (sc
.state
== SCE_CSS_DOUBLESTRING
|| sc
.state
== SCE_CSS_SINGLESTRING
) {
75 if (sc
.ch
!= (sc
.state
== SCE_CSS_DOUBLESTRING
? '\"' : '\''))
77 unsigned int i
= sc
.currentPos
;
78 while (i
&& styler
[i
-1] == '\\')
80 if ((sc
.currentPos
- i
) % 2 == 1)
82 sc
.ForwardSetState(SCE_CSS_VALUE
);
85 if (sc
.state
== SCE_CSS_OPERATOR
) {
87 unsigned int i
= startPos
;
88 op
= styler
.SafeGetCharAt(i
-1);
90 lastState
= styler
.StyleAt(i
-1);
91 if (lastState
!= SCE_CSS_OPERATOR
&& lastState
!= SCE_CSS_COMMENT
)
97 if (lastState
== SCE_CSS_DEFAULT
)
98 sc
.SetState(SCE_CSS_DIRECTIVE
);
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
);
107 if (lastState
== SCE_CSS_DEFAULT
|| lastState
== SCE_CSS_VALUE
|| lastState
== SCE_CSS_IMPORTANT
|| lastState
== SCE_CSS_IDENTIFIER
)
108 sc
.SetState(SCE_CSS_DEFAULT
);
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
);
117 if (lastState
== SCE_CSS_TAG
|| lastState
== SCE_CSS_DEFAULT
)
118 sc
.SetState(SCE_CSS_CLASS
);
121 if (lastState
== SCE_CSS_TAG
|| lastState
== SCE_CSS_DEFAULT
)
122 sc
.SetState(SCE_CSS_ID
);
125 if (lastState
== SCE_CSS_TAG
)
126 sc
.SetState(SCE_CSS_DEFAULT
);
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
);
135 if (lastState
== SCE_CSS_VALUE
)
136 sc
.SetState(SCE_CSS_IMPORTANT
);
141 if (IsAWordChar(sc
.ch
)) {
142 if (sc
.state
== SCE_CSS_DEFAULT
)
143 sc
.SetState(SCE_CSS_TAG
);
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
153 sc
.GetCurrentLowered(s
, sizeof(s
));
155 while (*s2
&& !IsAWordChar(*s2
))
158 case SCE_CSS_IDENTIFIER
:
159 if (!keywords
.InList(s2
))
160 sc
.ChangeState(SCE_CSS_UNKNOWN_IDENTIFIER
);
162 case SCE_CSS_UNKNOWN_IDENTIFIER
:
163 if (keywords
.InList(s2
))
164 sc
.ChangeState(SCE_CSS_IDENTIFIER
);
166 case SCE_CSS_PSEUDOCLASS
:
167 if (!pseudoClasses
.InList(s2
))
168 sc
.ChangeState(SCE_CSS_UNKNOWN_PSEUDOCLASS
);
170 case SCE_CSS_UNKNOWN_PSEUDOCLASS
:
171 if (pseudoClasses
.InList(s2
))
172 sc
.ChangeState(SCE_CSS_PSEUDOCLASS
);
174 case SCE_CSS_IMPORTANT
:
175 if (strcmp(s2
, "important") != 0)
176 sc
.ChangeState(SCE_CSS_VALUE
);
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
);
184 if (sc
.Match('/', '*')) {
185 lastStateC
= sc
.state
;
186 sc
.SetState(SCE_CSS_COMMENT
);
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
== '{')
194 if (sc
.state
!= SCE_CSS_OPERATOR
)
195 lastState
= sc
.state
;
196 sc
.SetState(SCE_CSS_OPERATOR
);
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
++) {
216 chNext
= styler
.SafeGetCharAt(i
+ 1);
217 int style
= styler
.StyleAt(i
);
218 bool atEOL
= (ch
== '\r' && chNext
!= '\n') || (ch
== '\n');
220 if (!inComment
&& (style
== SCE_CSS_COMMENT
))
222 else if (inComment
&& (style
!= SCE_CSS_COMMENT
))
224 inComment
= (style
== SCE_CSS_COMMENT
);
226 if (style
== SCE_CSS_OPERATOR
) {
229 } else if (ch
== '}') {
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
);
243 levelPrev
= levelCurrent
;
246 if (!isspacechar(ch
))
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
);
254 static const char * const cssWordListDesc
[] = {
260 LexerModule
lmCss(SCLEX_CSS
, ColouriseCssDoc
, "css", FoldCSSDoc
, cssWordListDesc
);