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

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

Issue 816693004: Add observatory_pub_packages snapshot to third_party (Closed) Base URL: http://dart.googlecode.com/svn/third_party/
Patch Set: Created 6 years 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 | Annotate | Revision Log
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
276 void visitSelectorGroup(SelectorGroup node) {
277 var selectors = node.selectors;
278 var selectorsLength = selectors.length;
279 for (var i = 0; i < selectorsLength; i++) {
280 if (i > 0) emit(',$_sp');
281 selectors[i].visit(this);
282 }
283 }
284
285 void visitSimpleSelectorSequence(SimpleSelectorSequence node) {
286 emit('${node._combinatorToString}');
287 node.simpleSelector.visit(this);
288 }
289
290 void visitSimpleSelector(SimpleSelector node) {
291 emit(node.name);
292 }
293
294 void visitNamespaceSelector(NamespaceSelector node) {
295 emit(node.toString());
296 }
297
298 void visitElementSelector(ElementSelector node) {
299 emit(node.toString());
300 }
301
302 void visitAttributeSelector(AttributeSelector node) {
303 emit(node.toString());
304 }
305
306 void visitIdSelector(IdSelector node) {
307 emit(node.toString());
308 }
309
310 void visitClassSelector(ClassSelector node) {
311 emit(node.toString());
312 }
313
314 void visitPseudoClassSelector(PseudoClassSelector node) {
315 emit(node.toString());
316 }
317
318 void visitPseudoElementSelector(PseudoElementSelector node) {
319 emit(node.toString());
320 }
321
322 void visitPseudoClassFunctionSelector(PseudoClassFunctionSelector node) {
323 emit(":${node.name}(");
324 node.expression.visit(this);
325 emit(')');
326 }
327
328 void visitPseudoElementFunctionSelector(PseudoElementFunctionSelector node) {
329 emit("::${node.name}(");
330 node.expression.visit(this);
331 emit(')');
332 }
333
334 void visitNegationSelector(NegationSelector node) {
335 emit(':not(');
336 node.negationArg.visit(this);
337 emit(')');
338 }
339
340 void visitSelectorExpression(SelectorExpression node) {
341 var expressions = node.expressions;
342 var expressionsLength = expressions.length;
343 for (var i = 0; i < expressionsLength; i++) {
344 // Add space seperator between terms without an operator.
345 var expression = expressions[i];
346 expression.visit(this);
347 }
348 }
349
350 void visitUnicodeRangeTerm(UnicodeRangeTerm node) {
351 if (node.hasSecond) {
352 emit("U+${node.first}-${node.second}");
353 } else {
354 emit("U+${node.first}");
355 }
356 }
357
358 void visitLiteralTerm(LiteralTerm node) {
359 emit(node.text);
360 }
361
362 void visitHexColorTerm(HexColorTerm node) {
363 var mappedName;
364 if (_isTesting && (node.value is! BAD_HEX_VALUE)) {
365 mappedName = TokenKind.hexToColorName(node.value);
366 }
367 if (mappedName == null) {
368 mappedName = '#${node.text}';
369 }
370
371 emit(mappedName);
372 }
373
374 void visitNumberTerm(NumberTerm node) {
375 visitLiteralTerm(node);
376 }
377
378 void visitUnitTerm(UnitTerm node) {
379 emit(node.toString());
380 }
381
382 void visitLengthTerm(LengthTerm node) {
383 emit(node.toString());
384 }
385
386 void visitPercentageTerm(PercentageTerm node) {
387 emit('${node.text}%');
388 }
389
390 void visitEmTerm(EmTerm node) {
391 emit('${node.text}em');
392 }
393
394 void visitExTerm(ExTerm node) {
395 emit('${node.text}ex');
396 }
397
398 void visitAngleTerm(AngleTerm node) {
399 emit(node.toString());
400 }
401
402 void visitTimeTerm(TimeTerm node) {
403 emit(node.toString());
404 }
405
406 void visitFreqTerm(FreqTerm node) {
407 emit(node.toString());
408 }
409
410 void visitFractionTerm(FractionTerm node) {
411 emit('${node.text}fr');
412 }
413
414 void visitUriTerm(UriTerm node) {
415 emit('url("${node.text}")');
416 }
417
418 void visitResolutionTerm(ResolutionTerm node) {
419 emit(node.toString());
420 }
421
422 void visitViewportTerm(ViewportTerm node) {
423 emit(node.toString());
424 }
425
426 void visitFunctionTerm(FunctionTerm node) {
427 // TODO(terry): Optimize rgb to a hexcolor.
428 emit('${node.text}(');
429 node._params.visit(this);
430 emit(')');
431 }
432
433 void visitGroupTerm(GroupTerm node) {
434 emit('(');
435 var terms = node._terms;
436 var termsLength = terms.length;
437 for (var i = 0; i < termsLength; i++) {
438 if (i > 0) emit('$_sp');
439 terms[i].visit(this);
440 }
441 emit(')');
442 }
443
444 void visitItemTerm(ItemTerm node) {
445 emit('[${node.text}]');
446 }
447
448 void visitIE8Term(IE8Term node) {
449 visitLiteralTerm(node);
450 }
451
452 void visitOperatorSlash(OperatorSlash node) {
453 emit('/');
454 }
455
456 void visitOperatorComma(OperatorComma node) {
457 emit(',');
458 }
459
460 void visitOperatorPlus(OperatorPlus node) {
461 emit('+');
462 }
463
464 void visitOperatorMinus(OperatorMinus node) {
465 emit('-');
466 }
467
468 void visitVarUsage(VarUsage node) {
469 emit('var(${node.name}');
470 if (!node.defaultValues.isEmpty) {
471 emit(',');
472 for (var defaultValue in node.defaultValues) {
473 emit(' ');
474 defaultValue.visit(this);
475 }
476 }
477 emit(')');
478 }
479
480 void visitExpressions(Expressions node) {
481 var expressions = node.expressions;
482 var expressionsLength = expressions.length;
483 for (var i = 0; i < expressionsLength; i++) {
484 // Add space seperator between terms without an operator.
485 // TODO(terry): Should have a BinaryExpression to solve this problem.
486 var expression = expressions[i];
487 if (i > 0 &&
488 !(expression is OperatorComma || expression is OperatorSlash)) {
489 emit(' ');
490 }
491 expression.visit(this);
492 }
493 }
494
495 void visitBinaryExpression(BinaryExpression node) {
496 // TODO(terry): TBD
497 throw UnimplementedError;
498 }
499
500 void visitUnaryExpression(UnaryExpression node) {
501 // TODO(terry): TBD
502 throw UnimplementedError;
503 }
504
505 void visitIdentifier(Identifier node) {
506 emit(node.name);
507 }
508
509 void visitWildcard(Wildcard node) {
510 emit('*');
511 }
512
513 void visitDartStyleExpression(DartStyleExpression node) {
514 // TODO(terry): TBD
515 throw UnimplementedError;
516 }
517 }
OLDNEW
« no previous file with comments | « observatory_pub_packages/csslib/src/analyzer.dart ('k') | observatory_pub_packages/csslib/src/messages.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698