OSDN Git Service

Merge X64 Build
[tortoisegit/TortoiseGitJp.git] / ext / scintilla / src / LexCSS.cxx
1 // Scintilla source code edit control\r
2 /** @file LexCSS.cxx\r
3  ** Lexer for Cascading Style Sheets\r
4  ** Written by Jakub Vrána\r
5  ** Improved by Philippe Lhoste (CSS2)\r
6  **/\r
7 // Copyright 1998-2002 by Neil Hodgson <neilh@scintilla.org>\r
8 // The License.txt file describes the conditions under which this software may be distributed.\r
9 \r
10 #include <stdlib.h>\r
11 #include <string.h>\r
12 #include <ctype.h>\r
13 #include <stdio.h>\r
14 #include <stdarg.h>\r
15 \r
16 #include "Platform.h"\r
17 \r
18 #include "PropSet.h"\r
19 #include "Accessor.h"\r
20 #include "StyleContext.h"\r
21 #include "KeyWords.h"\r
22 #include "Scintilla.h"\r
23 #include "SciLexer.h"\r
24 \r
25 #ifdef SCI_NAMESPACE\r
26 using namespace Scintilla;\r
27 #endif\r
28 \r
29 \r
30 static inline bool IsAWordChar(const unsigned int ch) {\r
31         /* FIXME:\r
32          * The CSS spec allows "ISO 10646 characters U+00A1 and higher" to be treated as word chars.\r
33          * Unfortunately, we are only getting string bytes here, and not full unicode characters. We cannot guarantee\r
34          * that our byte is between U+0080 - U+00A0 (to return false), so we have to allow all characters U+0080 and higher\r
35          */\r
36         return ch >= 0x80 || isalnum(ch) || ch == '-' || ch == '_';\r
37 }\r
38 \r
39 inline bool IsCssOperator(const int ch) {\r
40         if (!((ch < 0x80) && isalnum(ch)) &&\r
41                 (ch == '{' || ch == '}' || ch == ':' || ch == ',' || ch == ';' ||\r
42                  ch == '.' || ch == '#' || ch == '!' || ch == '@' ||\r
43                  /* CSS2 */\r
44                  ch == '*' || ch == '>' || ch == '+' || ch == '=' || ch == '~' || ch == '|' ||\r
45                  ch == '[' || ch == ']' || ch == '(' || ch == ')')) {\r
46                 return true;\r
47         }\r
48         return false;\r
49 }\r
50 \r
51 static void ColouriseCssDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[], Accessor &styler) {\r
52         WordList &css1Props = *keywordlists[0];\r
53         WordList &pseudoClasses = *keywordlists[1];\r
54         WordList &css2Props = *keywordlists[2];\r
55         WordList &css3Props = *keywordlists[3];\r
56         WordList &pseudoElements = *keywordlists[4];\r
57         WordList &exProps = *keywordlists[5];\r
58         WordList &exPseudoClasses = *keywordlists[6];\r
59         WordList &exPseudoElements = *keywordlists[7];\r
60 \r
61         StyleContext sc(startPos, length, initStyle, styler);\r
62 \r
63         int lastState = -1; // before operator\r
64         int lastStateC = -1; // before comment\r
65         int op = ' '; // last operator\r
66         int opPrev = ' '; // last operator\r
67 \r
68         for (; sc.More(); sc.Forward()) {\r
69                 if (sc.state == SCE_CSS_COMMENT && sc.Match('*', '/')) {\r
70                         if (lastStateC == -1) {\r
71                                 // backtrack to get last state:\r
72                                 // comments are like whitespace, so we must return to the previous state\r
73                                 unsigned int i = startPos;\r
74                                 for (; i > 0; i--) {\r
75                                         if ((lastStateC = styler.StyleAt(i-1)) != SCE_CSS_COMMENT) {\r
76                                                 if (lastStateC == SCE_CSS_OPERATOR) {\r
77                                                         op = styler.SafeGetCharAt(i-1);\r
78                                                         opPrev = styler.SafeGetCharAt(i-2);\r
79                                                         while (--i) {\r
80                                                                 lastState = styler.StyleAt(i-1);\r
81                                                                 if (lastState != SCE_CSS_OPERATOR && lastState != SCE_CSS_COMMENT)\r
82                                                                         break;\r
83                                                         }\r
84                                                         if (i == 0)\r
85                                                                 lastState = SCE_CSS_DEFAULT;\r
86                                                 }\r
87                                                 break;\r
88                                         }\r
89                                 }\r
90                                 if (i == 0)\r
91                                         lastStateC = SCE_CSS_DEFAULT;\r
92                         }\r
93                         sc.Forward();\r
94                         sc.ForwardSetState(lastStateC);\r
95                 }\r
96 \r
97                 if (sc.state == SCE_CSS_COMMENT)\r
98                         continue;\r
99 \r
100                 if (sc.state == SCE_CSS_DOUBLESTRING || sc.state == SCE_CSS_SINGLESTRING) {\r
101                         if (sc.ch != (sc.state == SCE_CSS_DOUBLESTRING ? '\"' : '\''))\r
102                                 continue;\r
103                         unsigned int i = sc.currentPos;\r
104                         while (i && styler[i-1] == '\\')\r
105                                 i--;\r
106                         if ((sc.currentPos - i) % 2 == 1)\r
107                                 continue;\r
108                         sc.ForwardSetState(SCE_CSS_VALUE);\r
109                 }\r
110 \r
111                 if (sc.state == SCE_CSS_OPERATOR) {\r
112                         if (op == ' ') {\r
113                                 unsigned int i = startPos;\r
114                                 op = styler.SafeGetCharAt(i-1);\r
115                                 opPrev = styler.SafeGetCharAt(i-2);\r
116                                 while (--i) {\r
117                                         lastState = styler.StyleAt(i-1);\r
118                                         if (lastState != SCE_CSS_OPERATOR && lastState != SCE_CSS_COMMENT)\r
119                                                 break;\r
120                                 }\r
121                         }\r
122                         switch (op) {\r
123                         case '@':\r
124                                 if (lastState == SCE_CSS_DEFAULT)\r
125                                         sc.SetState(SCE_CSS_DIRECTIVE);\r
126                                 break;\r
127                         case '>':\r
128                         case '+':\r
129                                 if (lastState == SCE_CSS_TAG || lastState == SCE_CSS_CLASS || lastState == SCE_CSS_ID ||\r
130                                         lastState == SCE_CSS_PSEUDOCLASS || lastState == SCE_CSS_EXTENDED_PSEUDOCLASS || lastState == SCE_CSS_UNKNOWN_PSEUDOCLASS)\r
131                                         sc.SetState(SCE_CSS_DEFAULT);\r
132                                 break;\r
133                         case '[':\r
134                                 if (lastState == SCE_CSS_TAG || lastState == SCE_CSS_DEFAULT || lastState == SCE_CSS_CLASS || lastState == SCE_CSS_ID ||\r
135                                         lastState == SCE_CSS_PSEUDOCLASS || lastState == SCE_CSS_EXTENDED_PSEUDOCLASS || lastState == SCE_CSS_UNKNOWN_PSEUDOCLASS)\r
136                                         sc.SetState(SCE_CSS_ATTRIBUTE);\r
137                                 break;\r
138                         case ']':\r
139                                 if (lastState == SCE_CSS_ATTRIBUTE)\r
140                                         sc.SetState(SCE_CSS_TAG);\r
141                                 break;\r
142                         case '{':\r
143                                 if (lastState == SCE_CSS_DIRECTIVE)\r
144                                         sc.SetState(SCE_CSS_DEFAULT);\r
145                                 else if (lastState == SCE_CSS_TAG)\r
146                                         sc.SetState(SCE_CSS_IDENTIFIER);\r
147                                 break;\r
148                         case '}':\r
149                                 if (lastState == SCE_CSS_DEFAULT || lastState == SCE_CSS_VALUE || lastState == SCE_CSS_IMPORTANT ||\r
150                                         lastState == SCE_CSS_IDENTIFIER || lastState == SCE_CSS_IDENTIFIER2 || lastState == SCE_CSS_IDENTIFIER3)\r
151                                         sc.SetState(SCE_CSS_DEFAULT);\r
152                                 break;\r
153                         case '(':\r
154                                 if (lastState == SCE_CSS_PSEUDOCLASS)\r
155                                         sc.SetState(SCE_CSS_TAG);\r
156                                 else if (lastState == SCE_CSS_EXTENDED_PSEUDOCLASS)\r
157                                         sc.SetState(SCE_CSS_EXTENDED_PSEUDOCLASS);\r
158                                 break;\r
159                         case ')':\r
160                                 if (lastState == SCE_CSS_TAG || lastState == SCE_CSS_DEFAULT || lastState == SCE_CSS_CLASS || lastState == SCE_CSS_ID ||\r
161                                         lastState == SCE_CSS_PSEUDOCLASS || lastState == SCE_CSS_EXTENDED_PSEUDOCLASS || lastState == SCE_CSS_UNKNOWN_PSEUDOCLASS ||\r
162                                         lastState == SCE_CSS_PSEUDOELEMENT || lastState == SCE_CSS_EXTENDED_PSEUDOELEMENT)\r
163                                         sc.SetState(SCE_CSS_TAG);\r
164                                 break;\r
165                         case ':':\r
166                                 if (lastState == SCE_CSS_TAG || lastState == SCE_CSS_DEFAULT || lastState == SCE_CSS_CLASS || lastState == SCE_CSS_ID ||\r
167                                         lastState == SCE_CSS_PSEUDOCLASS || lastState == SCE_CSS_EXTENDED_PSEUDOCLASS || lastState == SCE_CSS_UNKNOWN_PSEUDOCLASS ||\r
168                                         lastState == SCE_CSS_PSEUDOELEMENT || lastState == SCE_CSS_EXTENDED_PSEUDOELEMENT)\r
169                                         sc.SetState(SCE_CSS_PSEUDOCLASS);\r
170                                 else if (lastState == SCE_CSS_IDENTIFIER || lastState == SCE_CSS_IDENTIFIER2 ||\r
171                                         lastState == SCE_CSS_IDENTIFIER3 || lastState == SCE_CSS_EXTENDED_IDENTIFIER ||\r
172                                         lastState == SCE_CSS_UNKNOWN_IDENTIFIER)\r
173                                         sc.SetState(SCE_CSS_VALUE);\r
174                                 break;\r
175                         case '.':\r
176                                 if (lastState == SCE_CSS_TAG || lastState == SCE_CSS_DEFAULT || lastState == SCE_CSS_CLASS || lastState == SCE_CSS_ID ||\r
177                                         lastState == SCE_CSS_PSEUDOCLASS || lastState == SCE_CSS_EXTENDED_PSEUDOCLASS || lastState == SCE_CSS_UNKNOWN_PSEUDOCLASS)\r
178                                         sc.SetState(SCE_CSS_CLASS);\r
179                                 break;\r
180                         case '#':\r
181                                 if (lastState == SCE_CSS_TAG || lastState == SCE_CSS_DEFAULT || lastState == SCE_CSS_CLASS || lastState == SCE_CSS_ID ||\r
182                                         lastState == SCE_CSS_PSEUDOCLASS || lastState == SCE_CSS_EXTENDED_PSEUDOCLASS || lastState == SCE_CSS_UNKNOWN_PSEUDOCLASS)\r
183                                         sc.SetState(SCE_CSS_ID);\r
184                                 break;\r
185                         case ',':\r
186                         case '|':\r
187                         case '~':\r
188                                 if (lastState == SCE_CSS_TAG)\r
189                                         sc.SetState(SCE_CSS_DEFAULT);\r
190                                 break;\r
191                         case ';':\r
192                                 if (lastState == SCE_CSS_DIRECTIVE)\r
193                                         sc.SetState(SCE_CSS_DEFAULT);\r
194                                 else if (lastState == SCE_CSS_VALUE || lastState == SCE_CSS_IMPORTANT)\r
195                                         sc.SetState(SCE_CSS_IDENTIFIER);\r
196                                 break;\r
197                         case '!':\r
198                                 if (lastState == SCE_CSS_VALUE)\r
199                                         sc.SetState(SCE_CSS_IMPORTANT);\r
200                                 break;\r
201                         }\r
202                 }\r
203 \r
204                 if (IsAWordChar(sc.ch)) {\r
205                         if (sc.state == SCE_CSS_DEFAULT)\r
206                                 sc.SetState(SCE_CSS_TAG);\r
207                         continue;\r
208                 }\r
209 \r
210                 if (sc.ch == '*' && sc.state == SCE_CSS_DEFAULT) {\r
211                         sc.SetState(SCE_CSS_TAG);\r
212                         continue;\r
213                 }\r
214 \r
215                 if (IsAWordChar(sc.chPrev) && (\r
216                         sc.state == SCE_CSS_IDENTIFIER || sc.state == SCE_CSS_IDENTIFIER2 ||\r
217                         sc.state == SCE_CSS_IDENTIFIER3 || sc.state == SCE_CSS_EXTENDED_IDENTIFIER ||\r
218                         sc.state == SCE_CSS_UNKNOWN_IDENTIFIER ||\r
219                         sc.state == SCE_CSS_PSEUDOCLASS || sc.state == SCE_CSS_PSEUDOELEMENT ||\r
220                         sc.state == SCE_CSS_EXTENDED_PSEUDOCLASS || sc.state == SCE_CSS_EXTENDED_PSEUDOELEMENT ||\r
221                         sc.state == SCE_CSS_UNKNOWN_PSEUDOCLASS ||\r
222                         sc.state == SCE_CSS_IMPORTANT\r
223                 )) {\r
224                         char s[100];\r
225                         sc.GetCurrentLowered(s, sizeof(s));\r
226                         char *s2 = s;\r
227                         while (*s2 && !IsAWordChar(*s2))\r
228                                 s2++;\r
229                         switch (sc.state) {\r
230                         case SCE_CSS_IDENTIFIER:\r
231                         case SCE_CSS_IDENTIFIER2:\r
232                         case SCE_CSS_IDENTIFIER3:\r
233                         case SCE_CSS_EXTENDED_IDENTIFIER:\r
234                         case SCE_CSS_UNKNOWN_IDENTIFIER:\r
235                                 if (css1Props.InList(s2))\r
236                                         sc.ChangeState(SCE_CSS_IDENTIFIER);\r
237                                 else if (css2Props.InList(s2))\r
238                                         sc.ChangeState(SCE_CSS_IDENTIFIER2);\r
239                                 else if (css3Props.InList(s2))\r
240                                         sc.ChangeState(SCE_CSS_IDENTIFIER3);\r
241                                 else if (exProps.InList(s2))\r
242                                         sc.ChangeState(SCE_CSS_EXTENDED_IDENTIFIER);\r
243                                 else\r
244                                         sc.ChangeState(SCE_CSS_UNKNOWN_IDENTIFIER);\r
245                                 break;\r
246                         case SCE_CSS_PSEUDOCLASS:\r
247                         case SCE_CSS_PSEUDOELEMENT:\r
248                         case SCE_CSS_EXTENDED_PSEUDOCLASS:\r
249                         case SCE_CSS_EXTENDED_PSEUDOELEMENT:\r
250                         case SCE_CSS_UNKNOWN_PSEUDOCLASS:\r
251                                 if (op == ':' && opPrev != ':' && pseudoClasses.InList(s2))\r
252                                         sc.ChangeState(SCE_CSS_PSEUDOCLASS);\r
253                                 else if (opPrev == ':' && pseudoElements.InList(s2))\r
254                                         sc.ChangeState(SCE_CSS_PSEUDOELEMENT);\r
255                                 else if ((op == ':' || (op == '(' && lastState == SCE_CSS_EXTENDED_PSEUDOCLASS)) && opPrev != ':' && exPseudoClasses.InList(s2))\r
256                                         sc.ChangeState(SCE_CSS_EXTENDED_PSEUDOCLASS);\r
257                                 else if (opPrev == ':' && exPseudoElements.InList(s2))\r
258                                         sc.ChangeState(SCE_CSS_EXTENDED_PSEUDOELEMENT);\r
259                                 else\r
260                                         sc.ChangeState(SCE_CSS_UNKNOWN_PSEUDOCLASS);\r
261                                 break;\r
262                         case SCE_CSS_IMPORTANT:\r
263                                 if (strcmp(s2, "important") != 0)\r
264                                         sc.ChangeState(SCE_CSS_VALUE);\r
265                                 break;\r
266                         }\r
267                 }\r
268 \r
269                 if (sc.ch != '.' && sc.ch != ':' && sc.ch != '#' && (\r
270                         sc.state == SCE_CSS_CLASS || sc.state == SCE_CSS_ID ||\r
271                         (sc.ch != '(' && sc.ch != ')' && ( /* This line of the condition makes it possible to extend pseudo-classes with parentheses */\r
272                                 sc.state == SCE_CSS_PSEUDOCLASS || sc.state == SCE_CSS_PSEUDOELEMENT ||\r
273                                 sc.state == SCE_CSS_EXTENDED_PSEUDOCLASS || sc.state == SCE_CSS_EXTENDED_PSEUDOELEMENT ||\r
274                                 sc.state == SCE_CSS_UNKNOWN_PSEUDOCLASS\r
275                         ))\r
276                 ))\r
277                         sc.SetState(SCE_CSS_TAG);\r
278 \r
279                 if (sc.Match('/', '*')) {\r
280                         lastStateC = sc.state;\r
281                         sc.SetState(SCE_CSS_COMMENT);\r
282                         sc.Forward();\r
283                 } else if (sc.state == SCE_CSS_VALUE && (sc.ch == '\"' || sc.ch == '\'')) {\r
284                         sc.SetState((sc.ch == '\"' ? SCE_CSS_DOUBLESTRING : SCE_CSS_SINGLESTRING));\r
285                 } else if (IsCssOperator(sc.ch)\r
286                         && (sc.state != SCE_CSS_ATTRIBUTE || sc.ch == ']')\r
287                         && (sc.state != SCE_CSS_VALUE || sc.ch == ';' || sc.ch == '}' || sc.ch == '!')\r
288                         && (sc.state != SCE_CSS_DIRECTIVE || sc.ch == ';' || sc.ch == '{')\r
289                 ) {\r
290                         if (sc.state != SCE_CSS_OPERATOR)\r
291                                 lastState = sc.state;\r
292                         sc.SetState(SCE_CSS_OPERATOR);\r
293                         op = sc.ch;\r
294                         opPrev = sc.chPrev;\r
295                 }\r
296         }\r
297 \r
298         sc.Complete();\r
299 }\r
300 \r
301 static void FoldCSSDoc(unsigned int startPos, int length, int, WordList *[], Accessor &styler) {\r
302         bool foldComment = styler.GetPropertyInt("fold.comment") != 0;\r
303         bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;\r
304         unsigned int endPos = startPos + length;\r
305         int visibleChars = 0;\r
306         int lineCurrent = styler.GetLine(startPos);\r
307         int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;\r
308         int levelCurrent = levelPrev;\r
309         char chNext = styler[startPos];\r
310         bool inComment = (styler.StyleAt(startPos-1) == SCE_CSS_COMMENT);\r
311         for (unsigned int i = startPos; i < endPos; i++) {\r
312                 char ch = chNext;\r
313                 chNext = styler.SafeGetCharAt(i + 1);\r
314                 int style = styler.StyleAt(i);\r
315                 bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');\r
316                 if (foldComment) {\r
317                         if (!inComment && (style == SCE_CSS_COMMENT))\r
318                                 levelCurrent++;\r
319                         else if (inComment && (style != SCE_CSS_COMMENT))\r
320                                 levelCurrent--;\r
321                         inComment = (style == SCE_CSS_COMMENT);\r
322                 }\r
323                 if (style == SCE_CSS_OPERATOR) {\r
324                         if (ch == '{') {\r
325                                 levelCurrent++;\r
326                         } else if (ch == '}') {\r
327                                 levelCurrent--;\r
328                         }\r
329                 }\r
330                 if (atEOL) {\r
331                         int lev = levelPrev;\r
332                         if (visibleChars == 0 && foldCompact)\r
333                                 lev |= SC_FOLDLEVELWHITEFLAG;\r
334                         if ((levelCurrent > levelPrev) && (visibleChars > 0))\r
335                                 lev |= SC_FOLDLEVELHEADERFLAG;\r
336                         if (lev != styler.LevelAt(lineCurrent)) {\r
337                                 styler.SetLevel(lineCurrent, lev);\r
338                         }\r
339                         lineCurrent++;\r
340                         levelPrev = levelCurrent;\r
341                         visibleChars = 0;\r
342                 }\r
343                 if (!isspacechar(ch))\r
344                         visibleChars++;\r
345         }\r
346         // Fill in the real level of the next line, keeping the current flags as they will be filled in later\r
347         int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK;\r
348         styler.SetLevel(lineCurrent, levelPrev | flagsNext);\r
349 }\r
350 \r
351 static const char * const cssWordListDesc[] = {\r
352         "CSS1 Properties",\r
353         "Pseudo-classes",\r
354         "CSS2 Properties",\r
355         "CSS3 Properties",\r
356         "Pseudo-elements",\r
357         "Browser-Specific CSS Properties",\r
358         "Browser-Specific Pseudo-classes",\r
359         "Browser-Specific Pseudo-elements",\r
360         0\r
361 };\r
362 \r
363 LexerModule lmCss(SCLEX_CSS, ColouriseCssDoc, "css", FoldCSSDoc, cssWordListDesc);\r