0
|
1 /* Simple parser for CSS */
|
|
2
|
|
3 var CSSParser = Editor.Parser = (function() {
|
|
4 var tokenizeCSS = (function() {
|
|
5 function normal(source, setState) {
|
|
6 var ch = source.next();
|
|
7 if (ch == "@") {
|
|
8 source.nextWhileMatches(/\w/);
|
|
9 return "css-at";
|
|
10 }
|
|
11 else if (ch == "/" && source.equals("*")) {
|
|
12 setState(inCComment);
|
|
13 return null;
|
|
14 }
|
|
15 else if (ch == "<" && source.equals("!")) {
|
|
16 setState(inSGMLComment);
|
|
17 return null;
|
|
18 }
|
|
19 else if (ch == "=") {
|
|
20 return "css-compare";
|
|
21 }
|
|
22 else if (source.equals("=") && (ch == "~" || ch == "|")) {
|
|
23 source.next();
|
|
24 return "css-compare";
|
|
25 }
|
|
26 else if (ch == "\"" || ch == "'") {
|
|
27 setState(inString(ch));
|
|
28 return null;
|
|
29 }
|
|
30 else if (ch == "#") {
|
|
31 source.nextWhileMatches(/\w/);
|
|
32 return "css-hash";
|
|
33 }
|
|
34 else if (ch == "!") {
|
|
35 source.nextWhileMatches(/[ \t]/);
|
|
36 source.nextWhileMatches(/\w/);
|
|
37 return "css-important";
|
|
38 }
|
|
39 else if (/\d/.test(ch)) {
|
|
40 source.nextWhileMatches(/[\w.%]/);
|
|
41 return "css-unit";
|
|
42 }
|
|
43 else if (/[,.+>*\/]/.test(ch)) {
|
|
44 return "css-select-op";
|
|
45 }
|
|
46 else if (/[;{}:\[\]]/.test(ch)) {
|
|
47 return "css-punctuation";
|
|
48 }
|
|
49 else {
|
|
50 source.nextWhileMatches(/[\w\\\-_]/);
|
|
51 return "css-identifier";
|
|
52 }
|
|
53 }
|
|
54
|
|
55 function inCComment(source, setState) {
|
|
56 var maybeEnd = false;
|
|
57 while (!source.endOfLine()) {
|
|
58 var ch = source.next();
|
|
59 if (maybeEnd && ch == "/") {
|
|
60 setState(normal);
|
|
61 break;
|
|
62 }
|
|
63 maybeEnd = (ch == "*");
|
|
64 }
|
|
65 return "css-comment";
|
|
66 }
|
|
67
|
|
68 function inSGMLComment(source, setState) {
|
|
69 var dashes = 0;
|
|
70 while (!source.endOfLine()) {
|
|
71 var ch = source.next();
|
|
72 if (dashes >= 2 && ch == ">") {
|
|
73 setState(normal);
|
|
74 break;
|
|
75 }
|
|
76 dashes = (ch == "-") ? dashes + 1 : 0;
|
|
77 }
|
|
78 return "css-comment";
|
|
79 }
|
|
80
|
|
81 function inString(quote) {
|
|
82 return function(source, setState) {
|
|
83 var escaped = false;
|
|
84 while (!source.endOfLine()) {
|
|
85 var ch = source.next();
|
|
86 if (ch == quote && !escaped)
|
|
87 break;
|
|
88 escaped = !escaped && ch == "\\";
|
|
89 }
|
|
90 if (!escaped)
|
|
91 setState(normal);
|
|
92 return "css-string";
|
|
93 };
|
|
94 }
|
|
95
|
|
96 return function(source, startState) {
|
|
97 return tokenizer(source, startState || normal);
|
|
98 };
|
|
99 })();
|
|
100
|
|
101 function indentCSS(inBraces, inRule, base) {
|
|
102 return function(nextChars) {
|
|
103 if (!inBraces || /^\}/.test(nextChars)) return base;
|
|
104 else if (inRule) return base + indentUnit * 2;
|
|
105 else return base + indentUnit;
|
|
106 };
|
|
107 }
|
|
108
|
|
109 // This is a very simplistic parser -- since CSS does not really
|
|
110 // nest, it works acceptably well, but some nicer colouroing could
|
|
111 // be provided with a more complicated parser.
|
|
112 function parseCSS(source, basecolumn) {
|
|
113 basecolumn = basecolumn || 0;
|
|
114 var tokens = tokenizeCSS(source);
|
|
115 var inBraces = false, inRule = false, inDecl = false;;
|
|
116
|
|
117 var iter = {
|
|
118 next: function() {
|
|
119 var token = tokens.next(), style = token.style, content = token.content;
|
|
120
|
|
121 if (style == "css-hash")
|
|
122 style = token.style = inRule ? "css-colorcode" : "css-identifier";
|
|
123 if (style == "css-identifier") {
|
|
124 if (inRule) token.style = "css-value";
|
|
125 else if (!inBraces && !inDecl) token.style = "css-selector";
|
|
126 }
|
|
127
|
|
128 if (content == "\n")
|
|
129 token.indentation = indentCSS(inBraces, inRule, basecolumn);
|
|
130
|
|
131 if (content == "{" && inDecl == "@media")
|
|
132 inDecl = false;
|
|
133 else if (content == "{")
|
|
134 inBraces = true;
|
|
135 else if (content == "}")
|
|
136 inBraces = inRule = inDecl = false;
|
|
137 else if (content == ";")
|
|
138 inRule = inDecl = false;
|
|
139 else if (inBraces && style != "css-comment" && style != "whitespace")
|
|
140 inRule = true;
|
|
141 else if (!inBraces && style == "css-at")
|
|
142 inDecl = content;
|
|
143
|
|
144 return token;
|
|
145 },
|
|
146
|
|
147 copy: function() {
|
|
148 var _inBraces = inBraces, _inRule = inRule, _tokenState = tokens.state;
|
|
149 return function(source) {
|
|
150 tokens = tokenizeCSS(source, _tokenState);
|
|
151 inBraces = _inBraces;
|
|
152 inRule = _inRule;
|
|
153 return iter;
|
|
154 };
|
|
155 }
|
|
156 };
|
|
157 return iter;
|
|
158 }
|
|
159
|
|
160 return {make: parseCSS, electricChars: "}"};
|
|
161 })();
|