Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(484)

Side by Side Diff: csslib/lib/src/css_printer.dart

Issue 1400473008: Roll Observatory packages and add a roll script (Closed) Base URL: git@github.com:dart-lang/observatory_pub_packages.git@master
Patch Set: Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « csslib/lib/src/analyzer.dart ('k') | csslib/lib/src/messages.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 part of csslib.visitor;
6
7 /**
8 * Visitor that produces a formatted string representation of the CSS tree.
9 */
10 class CssPrinter extends Visitor {
11 StringBuffer _buff = new StringBuffer();
12 bool prettyPrint = true;
13
14 /**
15 * Walk the [tree] Stylesheet. [pretty] if true emits line breaks, extra
16 * spaces, friendly property values, etc., if false emits compacted output.
17 */
18 void visitTree(StyleSheet tree, {bool pretty: false}) {
19 prettyPrint = pretty;
20 _buff = new StringBuffer();
21 visitStyleSheet(tree);
22 }
23
24 /** Appends [str] to the output buffer. */
25 void emit(String str) {
26 _buff.write(str);
27 }
28
29 /** Returns the output buffer. */
30 String toString() => _buff.toString().trim();
31
32 String get _newLine => prettyPrint ? '\n' : ' ';
33 String get _sp => prettyPrint ? ' ' : '';
34
35 // TODO(terry): When adding obfuscation we'll need isOptimized (compact w/
36 // obufuscation) and have isTesting (compact no obfuscation) and
37 // isCompact would be !prettyPrint. We'll need another boolean
38 // flag for obfuscation.
39 bool get _isTesting => !prettyPrint;
40
41 void visitCssComment(CssComment node) {
42 emit('/* ${node.comment} */');
43 }
44
45 void visitCommentDefinition(CommentDefinition node) {
46 emit('<!-- ${node.comment} -->');
47 }
48
49 void visitMediaExpression(MediaExpression node) {
50 emit(node.andOperator ? ' AND ' : ' ');
51 emit('(${node.mediaFeature}:');
52 visitExpressions(node.exprs);
53 emit(')');
54 }
55
56 void visitMediaQuery(MediaQuery query) {
57 var unary = query.hasUnary ? ' ${query.unary}' : '';
58 var mediaType = query.hasMediaType ? ' ${query.mediaType}' : '';
59 emit('$unary$mediaType');
60 for (var expression in query.expressions) {
61 visitMediaExpression(expression);
62 }
63 }
64
65 void emitMediaQueries(queries) {
66 var queriesLen = queries.length;
67 for (var i = 0; i < queriesLen; i++) {
68 var query = queries[i];
69 if (query.hasMediaType && i > 0) emit(',');
70 visitMediaQuery(query);
71 }
72 }
73
74 void visitMediaDirective(MediaDirective node) {
75 emit(' @media');
76 emitMediaQueries(node.mediaQueries);
77 emit(' {');
78 for (var ruleset in node.rulesets) {
79 ruleset.visit(this);
80 }
81 emit('$_newLine\}');
82 }
83
84 void visitHostDirective(HostDirective node) {
85 emit('\n@host {');
86 for (var ruleset in node.rulesets) {
87 ruleset.visit(this);
88 }
89 emit('$_newLine\}');
90 }
91
92 /**
93 * @page : pseudoPage {
94 * decls
95 * }
96 */
97 void visitPageDirective(PageDirective node) {
98 emit('$_newLine@page');
99 if (node.hasIdent || node.hasPseudoPage) {
100 if (node.hasIdent) emit(' ');
101 emit(node._ident);
102 emit(node.hasPseudoPage ? ':${node._pseudoPage}' : '');
103 }
104 emit(' ');
105
106 var declsMargin = node._declsMargin;
107 var declsMarginLength = declsMargin.length;
108 for (var i = 0; i < declsMarginLength; i++) {
109 if (i > 0) emit(_newLine);
110 emit('{$_newLine');
111 declsMargin[i].visit(this);
112 emit('}');
113 }
114 }
115
116 /** @charset "charset encoding" */
117 void visitCharsetDirective(CharsetDirective node) {
118 emit('$_newLine@charset "${node.charEncoding}";');
119 }
120
121 void visitImportDirective(ImportDirective node) {
122 bool isStartingQuote(String ch) => ('\'"'.indexOf(ch[0]) >= 0);
123
124 if (_isTesting) {
125 // Emit assuming url() was parsed; most suite tests use url function.
126 emit(' @import url(${node.import})');
127 } else if (isStartingQuote(node.import)) {
128 emit(' @import ${node.import}');
129 } else {
130 // url(...) isn't needed only a URI can follow an @import directive; emit
131 // url as a string.
132 emit(' @import "${node.import}"');
133 }
134 emitMediaQueries(node.mediaQueries);
135 emit(';');
136 }
137
138 void visitKeyFrameDirective(KeyFrameDirective node) {
139 emit('$_newLine${node.keyFrameName} ');
140 node.name.visit(this);
141 emit('$_sp{$_newLine');
142 for (final block in node._blocks) {
143 block.visit(this);
144 }
145 emit('}');
146 }
147
148 void visitFontFaceDirective(FontFaceDirective node) {
149 emit('$_newLine@font-face ');
150 emit('$_sp{$_newLine');
151 node._declarations.visit(this);
152 emit('}');
153 }
154
155 void visitKeyFrameBlock(KeyFrameBlock node) {
156 emit('$_sp$_sp');
157 node._blockSelectors.visit(this);
158 emit('$_sp{$_newLine');
159 node._declarations.visit(this);
160 emit('$_sp$_sp}$_newLine');
161 }
162
163 void visitStyletDirective(StyletDirective node) {
164 emit('/* @stylet export as ${node.dartClassName} */\n');
165 }
166
167 void visitNamespaceDirective(NamespaceDirective node) {
168 bool isStartingQuote(String ch) => ('\'"'.indexOf(ch) >= 0);
169
170 if (isStartingQuote(node._uri)) {
171 emit(' @namespace ${node.prefix}"${node._uri}"');
172 } else {
173 if (_isTesting) {
174 // Emit exactly was we parsed.
175 emit(' @namespace ${node.prefix}url(${node._uri})');
176 } else {
177 // url(...) isn't needed only a URI can follow a:
178 // @namespace prefix directive.
179 emit(' @namespace ${node.prefix}${node._uri}');
180 }
181 }
182 emit(';');
183 }
184
185 void visitVarDefinitionDirective(VarDefinitionDirective node) {
186 visitVarDefinition(node.def);
187 emit(';$_newLine');
188 }
189
190 void visitMixinRulesetDirective(MixinRulesetDirective node) {
191 emit('@mixin ${node.name} {');
192 for (var ruleset in node.rulesets) {
193 ruleset.visit(this);
194 }
195 emit('}');
196 }
197
198 void visitMixinDeclarationDirective(MixinDeclarationDirective node) {
199 emit('@mixin ${node.name} {\n');
200 visitDeclarationGroup(node.declarations);
201 emit('}');
202 }
203
204 /**
205 * Added optional newLine for handling @include at top-level vs/ inside of
206 * a declaration group.
207 */
208 void visitIncludeDirective(IncludeDirective node, [bool topLevel = true]) {
209 if (topLevel) emit(_newLine);
210 emit('@include ${node.name}');
211 emit(';');
212 }
213
214 void visitContentDirective(ContentDirective node) {
215 // TODO(terry): TBD
216 }
217
218 void visitRuleSet(RuleSet node) {
219 emit("$_newLine");
220 node._selectorGroup.visit(this);
221 emit(" {$_newLine");
222 node._declarationGroup.visit(this);
223 emit("}");
224 }
225
226 void visitDeclarationGroup(DeclarationGroup node) {
227 var declarations = node.declarations;
228 var declarationsLength = declarations.length;
229 for (var i = 0; i < declarationsLength; i++) {
230 if (i > 0) emit(_newLine);
231 emit("$_sp$_sp");
232 declarations[i].visit(this);
233 emit(";");
234 }
235 if (declarationsLength > 0) emit(_newLine);
236 }
237
238 void visitMarginGroup(MarginGroup node) {
239 var margin_sym_name =
240 TokenKind.idToValue(TokenKind.MARGIN_DIRECTIVES, node.margin_sym);
241
242 emit("@$margin_sym_name {$_newLine");
243
244 visitDeclarationGroup(node);
245
246 emit("}$_newLine");
247 }
248
249 void visitDeclaration(Declaration node) {
250 String importantAsString() => node.important ? '$_sp!important' : '';
251
252 emit("${node.property}: ");
253 node._expression.visit(this);
254
255 emit("${importantAsString()}");
256 }
257
258 void visitVarDefinition(VarDefinition node) {
259 emit("var-${node.definedName}: ");
260 node._expression.visit(this);
261 }
262
263 void visitIncludeMixinAtDeclaration(IncludeMixinAtDeclaration node) {
264 // Don't emit a new line we're inside of a declaration group.
265 visitIncludeDirective(node.include, false);
266 }
267
268 void visitExtendDeclaration(ExtendDeclaration node) {
269 emit("@extend ");
270 for (var selector in node.selectors) {
271 selector.visit(this);
272 }
273 }
274
275 void visitSelectorGroup(SelectorGroup node) {
276 var selectors = node.selectors;
277 var selectorsLength = selectors.length;
278 for (var i = 0; i < selectorsLength; i++) {
279 if (i > 0) emit(',$_sp');
280 selectors[i].visit(this);
281 }
282 }
283
284 void visitSimpleSelectorSequence(SimpleSelectorSequence node) {
285 emit('${node._combinatorToString}');
286 node.simpleSelector.visit(this);
287 }
288
289 void visitSimpleSelector(SimpleSelector node) {
290 emit(node.name);
291 }
292
293 void visitNamespaceSelector(NamespaceSelector node) {
294 emit(node.toString());
295 }
296
297 void visitElementSelector(ElementSelector node) {
298 emit(node.toString());
299 }
300
301 void visitAttributeSelector(AttributeSelector node) {
302 emit(node.toString());
303 }
304
305 void visitIdSelector(IdSelector node) {
306 emit(node.toString());
307 }
308
309 void visitClassSelector(ClassSelector node) {
310 emit(node.toString());
311 }
312
313 void visitPseudoClassSelector(PseudoClassSelector node) {
314 emit(node.toString());
315 }
316
317 void visitPseudoElementSelector(PseudoElementSelector node) {
318 emit(node.toString());
319 }
320
321 void visitPseudoClassFunctionSelector(PseudoClassFunctionSelector node) {
322 emit(":${node.name}(");
323 node.expression.visit(this);
324 emit(')');
325 }
326
327 void visitPseudoElementFunctionSelector(PseudoElementFunctionSelector node) {
328 emit("::${node.name}(");
329 node.expression.visit(this);
330 emit(')');
331 }
332
333 void visitNegationSelector(NegationSelector node) {
334 emit(':not(');
335 node.negationArg.visit(this);
336 emit(')');
337 }
338
339 void visitSelectorExpression(SelectorExpression node) {
340 var expressions = node.expressions;
341 var expressionsLength = expressions.length;
342 for (var i = 0; i < expressionsLength; i++) {
343 // Add space seperator between terms without an operator.
344 var expression = expressions[i];
345 expression.visit(this);
346 }
347 }
348
349 void visitUnicodeRangeTerm(UnicodeRangeTerm node) {
350 if (node.hasSecond) {
351 emit("U+${node.first}-${node.second}");
352 } else {
353 emit("U+${node.first}");
354 }
355 }
356
357 void visitLiteralTerm(LiteralTerm node) {
358 emit(node.text);
359 }
360
361 void visitHexColorTerm(HexColorTerm node) {
362 var mappedName;
363 if (_isTesting && (node.value is! BAD_HEX_VALUE)) {
364 mappedName = TokenKind.hexToColorName(node.value);
365 }
366 if (mappedName == null) {
367 mappedName = '#${node.text}';
368 }
369
370 emit(mappedName);
371 }
372
373 void visitNumberTerm(NumberTerm node) {
374 visitLiteralTerm(node);
375 }
376
377 void visitUnitTerm(UnitTerm node) {
378 emit(node.toString());
379 }
380
381 void visitLengthTerm(LengthTerm node) {
382 emit(node.toString());
383 }
384
385 void visitPercentageTerm(PercentageTerm node) {
386 emit('${node.text}%');
387 }
388
389 void visitEmTerm(EmTerm node) {
390 emit('${node.text}em');
391 }
392
393 void visitExTerm(ExTerm node) {
394 emit('${node.text}ex');
395 }
396
397 void visitAngleTerm(AngleTerm node) {
398 emit(node.toString());
399 }
400
401 void visitTimeTerm(TimeTerm node) {
402 emit(node.toString());
403 }
404
405 void visitFreqTerm(FreqTerm node) {
406 emit(node.toString());
407 }
408
409 void visitFractionTerm(FractionTerm node) {
410 emit('${node.text}fr');
411 }
412
413 void visitUriTerm(UriTerm node) {
414 emit('url("${node.text}")');
415 }
416
417 void visitResolutionTerm(ResolutionTerm node) {
418 emit(node.toString());
419 }
420
421 void visitViewportTerm(ViewportTerm node) {
422 emit(node.toString());
423 }
424
425 void visitFunctionTerm(FunctionTerm node) {
426 // TODO(terry): Optimize rgb to a hexcolor.
427 emit('${node.text}(');
428 node._params.visit(this);
429 emit(')');
430 }
431
432 void visitGroupTerm(GroupTerm node) {
433 emit('(');
434 var terms = node._terms;
435 var termsLength = terms.length;
436 for (var i = 0; i < termsLength; i++) {
437 if (i > 0) emit('$_sp');
438 terms[i].visit(this);
439 }
440 emit(')');
441 }
442
443 void visitItemTerm(ItemTerm node) {
444 emit('[${node.text}]');
445 }
446
447 void visitIE8Term(IE8Term node) {
448 visitLiteralTerm(node);
449 }
450
451 void visitOperatorSlash(OperatorSlash node) {
452 emit('/');
453 }
454
455 void visitOperatorComma(OperatorComma node) {
456 emit(',');
457 }
458
459 void visitOperatorPlus(OperatorPlus node) {
460 emit('+');
461 }
462
463 void visitOperatorMinus(OperatorMinus node) {
464 emit('-');
465 }
466
467 void visitVarUsage(VarUsage node) {
468 emit('var(${node.name}');
469 if (!node.defaultValues.isEmpty) {
470 emit(',');
471 for (var defaultValue in node.defaultValues) {
472 emit(' ');
473 defaultValue.visit(this);
474 }
475 }
476 emit(')');
477 }
478
479 void visitExpressions(Expressions node) {
480 var expressions = node.expressions;
481 var expressionsLength = expressions.length;
482 for (var i = 0; i < expressionsLength; i++) {
483 // Add space seperator between terms without an operator.
484 // TODO(terry): Should have a BinaryExpression to solve this problem.
485 var expression = expressions[i];
486 if (i > 0 &&
487 !(expression is OperatorComma || expression is OperatorSlash)) {
488 emit(' ');
489 }
490 expression.visit(this);
491 }
492 }
493
494 void visitBinaryExpression(BinaryExpression node) {
495 // TODO(terry): TBD
496 throw UnimplementedError;
497 }
498
499 void visitUnaryExpression(UnaryExpression node) {
500 // TODO(terry): TBD
501 throw UnimplementedError;
502 }
503
504 void visitIdentifier(Identifier node) {
505 emit(node.name);
506 }
507
508 void visitWildcard(Wildcard node) {
509 emit('*');
510 }
511
512 void visitDartStyleExpression(DartStyleExpression node) {
513 // TODO(terry): TBD
514 throw UnimplementedError;
515 }
516 }
OLDNEW
« no previous file with comments | « csslib/lib/src/analyzer.dart ('k') | csslib/lib/src/messages.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698