Merge pull request #3196 from techee/anon_rename_fix2
[geany-mirror.git] / ctags / parsers / css.c
blob60fbb6befe673fbcca80a05dc3aee5e90dea1f87
1 /***************************************************************************
2 * css.c
3 * Token-based parser for CSS definitions
4 * Author - Colomban Wendling <colomban@geany.org>
5 * License GPL-2
6 **************************************************************************/
7 #include "general.h"
9 #include <string.h>
10 #include <ctype.h>
12 #include "entry.h"
13 #include "parse.h"
14 #include "read.h"
15 #include "routines.h"
17 #define isSelectorChar(c) \
18 /* attribute selectors are handled separately */ \
19 (isalnum (c) || \
20 (c) == '_' || /* allowed char */ \
21 (c) == '-' || /* allowed char */ \
22 (c) == '+' || /* allow all sibling in a single tag */ \
23 (c) == '>' || /* allow all child in a single tag */ \
24 (c) == '~' || /* allow general sibling combinator */ \
25 (c) == '|' || /* allow namespace separator */ \
26 (c) == '(' || /* allow pseudo-class arguments */ \
27 (c) == ')' || \
28 (c) == '.' || /* allow classes and selectors */ \
29 (c) == ':' || /* allow pseudo classes */ \
30 (c) == '*' || /* allow globs as P + * */ \
31 (c) == '#') /* allow ids */
33 typedef enum eCssKinds {
34 K_CLASS, K_SELECTOR, K_ID
35 } cssKind;
37 static kindDefinition CssKinds [] = {
38 { true, 'c', "class", "classes" },
39 { true, 's', "selector", "selectors" },
40 { true, 'i', "id", "identities" }
43 typedef enum {
44 /* any ASCII */
45 TOKEN_EOF = 257,
46 TOKEN_SELECTOR,
47 TOKEN_STRING
48 } tokenType;
50 typedef struct {
51 tokenType type;
52 vString *string;
53 } tokenInfo;
56 static void parseSelector (vString *const string, const int firstChar)
58 int c = firstChar;
61 vStringPut (string, (char) c);
62 c = getcFromInputFile ();
63 } while (isSelectorChar (c));
64 ungetcToInputFile (c);
67 static void readToken (tokenInfo *const token)
69 int c;
71 vStringClear (token->string);
73 getNextChar:
75 c = getcFromInputFile ();
76 while (isspace (c))
77 c = getcFromInputFile ();
79 token->type = c;
80 switch (c)
82 case EOF: token->type = TOKEN_EOF; break;
84 case '\'':
85 case '"':
87 const int delimiter = c;
90 vStringPut (token->string, c);
91 c = getcFromInputFile ();
92 if (c == '\\')
93 c = getcFromInputFile ();
95 while (c != EOF && c != delimiter);
96 if (c != EOF)
97 vStringPut (token->string, c);
98 token->type = TOKEN_STRING;
99 break;
102 case '/': /* maybe comment start */
104 int d = getcFromInputFile ();
105 if (d != '*')
107 ungetcToInputFile (d);
108 vStringPut (token->string, c);
109 token->type = c;
111 else
113 d = getcFromInputFile ();
116 c = d;
117 d = getcFromInputFile ();
119 while (d != EOF && ! (c == '*' && d == '/'));
120 goto getNextChar;
122 break;
125 default:
126 if (! isSelectorChar (c))
128 vStringPut (token->string, c);
129 token->type = c;
131 else
133 parseSelector (token->string, c);
134 token->type = TOKEN_SELECTOR;
136 break;
140 /* sets selector kind in @p kind if found, otherwise don't touches @p kind */
141 static cssKind classifySelector (const vString *const selector)
143 size_t i;
145 for (i = vStringLength (selector); i > 0; --i)
147 char c = vStringChar (selector, i - 1);
148 if (c == '.')
149 return K_CLASS;
150 else if (c == '#')
151 return K_ID;
153 return K_SELECTOR;
156 static void findCssTags (void)
158 bool readNextToken = true;
159 tokenInfo token;
161 token.string = vStringNew ();
165 if (readNextToken)
166 readToken (&token);
168 readNextToken = true;
170 if (token.type == '@')
171 { /* At-rules, from the "@" to the next block or semicolon */
172 bool useContents;
173 readToken (&token);
174 useContents = (strcmp (vStringValue (token.string), "media") == 0 ||
175 strcmp (vStringValue (token.string), "supports") == 0);
176 while (token.type != TOKEN_EOF &&
177 token.type != ';' && token.type != '{')
179 readToken (&token);
181 /* HACK: we *eat* the opening '{' for medias and the like so that
182 * the content is parsed as if it was at the root */
183 readNextToken = useContents && token.type == '{';
185 else if (token.type == TOKEN_SELECTOR)
186 { /* collect selectors and make a tag */
187 cssKind kind = K_SELECTOR;
188 MIOPos filePosition;
189 unsigned long lineNumber;
190 vString *selector = vStringNew ();
193 if (vStringLength (selector) > 0)
194 vStringPut (selector, ' ');
195 vStringCat (selector, token.string);
197 kind = classifySelector (token.string);
198 lineNumber = getInputLineNumber ();
199 filePosition = getInputFilePosition ();
201 readToken (&token);
203 /* handle attribute selectors */
204 if (token.type == '[')
206 int depth = 1;
207 while (depth > 0 && token.type != TOKEN_EOF)
209 vStringCat (selector, token.string);
210 readToken (&token);
211 if (token.type == '[')
212 depth++;
213 else if (token.type == ']')
214 depth--;
216 if (token.type != TOKEN_EOF)
217 vStringCat (selector, token.string);
218 readToken (&token);
221 while (token.type == TOKEN_SELECTOR);
222 /* we already consumed the next token, don't read it twice */
223 readNextToken = false;
225 if (CssKinds[kind].enabled)
227 tagEntryInfo e;
228 initTagEntry (&e, vStringValue (selector), kind);
230 e.lineNumber = lineNumber;
231 e.filePosition = filePosition;
233 makeTagEntry (&e);
235 vStringDelete (selector);
237 else if (token.type == '{')
238 { /* skip over { ... } */
239 int depth = 1;
240 while (depth > 0 && token.type != TOKEN_EOF)
242 readToken (&token);
243 if (token.type == '{')
244 depth++;
245 else if (token.type == '}')
246 depth--;
250 while (token.type != TOKEN_EOF);
252 vStringDelete (token.string);
255 /* parser definition */
256 extern parserDefinition* CssParser (void)
258 static const char *const extensions [] = { "css", NULL };
259 parserDefinition* def = parserNew ("CSS");
260 def->kindTable = CssKinds;
261 def->kindCount = ARRAY_SIZE (CssKinds);
262 def->extensions = extensions;
263 def->parser = findCssTags;
264 return def;