1 | // Copyright (C) 2021 The Qt Company Ltd. |
---|---|
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qqmldomreformatter_p.h" |
5 | #include "qqmldomcomments_p.h" |
6 | |
7 | #include <QtQml/private/qqmljsast_p.h> |
8 | #include <QtQml/private/qqmljsastvisitor_p.h> |
9 | #include <QtQml/private/qqmljsengine_p.h> |
10 | #include <QtQml/private/qqmljslexer_p.h> |
11 | |
12 | #include <QString> |
13 | |
14 | #include <algorithm> |
15 | #include <limits> |
16 | |
17 | QT_BEGIN_NAMESPACE |
18 | namespace QQmlJS { |
19 | namespace Dom { |
20 | |
21 | using namespace AST; |
22 | |
23 | bool ScriptFormatter::preVisit(Node *n) |
24 | { |
25 | if (CommentedElement *c = comments->commentForNode(n)) { |
26 | c->writePre(lw); |
27 | postOps[n].append(t: [c, this]() { c->writePost(lw); }); |
28 | } |
29 | return true; |
30 | } |
31 | void ScriptFormatter::postVisit(Node *n) |
32 | { |
33 | for (auto &op : postOps[n]) { |
34 | op(); |
35 | } |
36 | postOps.remove(key: n); |
37 | } |
38 | |
39 | void ScriptFormatter::lnAcceptIndented(Node *node) |
40 | { |
41 | int indent = lw.increaseIndent(level: 1); |
42 | lw.ensureNewline(); |
43 | accept(node); |
44 | lw.decreaseIndent(level: 1, expectedIndent: indent); |
45 | } |
46 | |
47 | bool ScriptFormatter::acceptBlockOrIndented(Node *ast, bool finishWithSpaceOrNewline) |
48 | { |
49 | if (cast<Block *>(ast)) { |
50 | out(str: " "); |
51 | accept(node: ast); |
52 | if (finishWithSpaceOrNewline) |
53 | out(str: " "); |
54 | return true; |
55 | } else { |
56 | if (finishWithSpaceOrNewline) |
57 | postOps[ast].append(t: [this]() { this->newLine(); }); |
58 | lnAcceptIndented(node: ast); |
59 | return false; |
60 | } |
61 | } |
62 | |
63 | bool ScriptFormatter::visit(ThisExpression *ast) |
64 | { |
65 | out(loc: ast->thisToken); |
66 | return true; |
67 | } |
68 | |
69 | bool ScriptFormatter::visit(NullExpression *ast) |
70 | { |
71 | out(loc: ast->nullToken); |
72 | return true; |
73 | } |
74 | bool ScriptFormatter::visit(TrueLiteral *ast) |
75 | { |
76 | out(loc: ast->trueToken); |
77 | return true; |
78 | } |
79 | bool ScriptFormatter::visit(FalseLiteral *ast) |
80 | { |
81 | out(loc: ast->falseToken); |
82 | return true; |
83 | } |
84 | |
85 | bool ScriptFormatter::visit(IdentifierExpression *ast) |
86 | { |
87 | out(loc: ast->identifierToken); |
88 | return true; |
89 | } |
90 | bool ScriptFormatter::visit(StringLiteral *ast) |
91 | { |
92 | // correctly handle multiline literals |
93 | if (ast->literalToken.length == 0) |
94 | return true; |
95 | QStringView str = loc2Str(ast->literalToken); |
96 | if (lw.indentNextlines && str.contains(c: QLatin1Char('\n'))) { |
97 | out(str: str.mid(pos: 0, n: 1)); |
98 | lw.indentNextlines = false; |
99 | out(str: str.mid(pos: 1)); |
100 | lw.indentNextlines = true; |
101 | } else { |
102 | out(str); |
103 | } |
104 | return true; |
105 | } |
106 | bool ScriptFormatter::visit(NumericLiteral *ast) |
107 | { |
108 | out(loc: ast->literalToken); |
109 | return true; |
110 | } |
111 | bool ScriptFormatter::visit(RegExpLiteral *ast) |
112 | { |
113 | out(loc: ast->literalToken); |
114 | return true; |
115 | } |
116 | |
117 | bool ScriptFormatter::visit(ArrayPattern *ast) |
118 | { |
119 | out(loc: ast->lbracketToken); |
120 | int baseIndent = lw.increaseIndent(level: 1); |
121 | if (ast->elements) { |
122 | accept(node: ast->elements); |
123 | out(loc: ast->commaToken); |
124 | auto lastElement = lastListElement(head: ast->elements); |
125 | if (lastElement->element && cast<ObjectPattern *>(ast: lastElement->element->initializer)) { |
126 | newLine(); |
127 | } |
128 | } else { |
129 | out(loc: ast->commaToken); |
130 | } |
131 | lw.decreaseIndent(level: 1, expectedIndent: baseIndent); |
132 | out(loc: ast->rbracketToken); |
133 | return false; |
134 | } |
135 | |
136 | bool ScriptFormatter::visit(ObjectPattern *ast) |
137 | { |
138 | out(loc: ast->lbraceToken); |
139 | ++expressionDepth; |
140 | if (ast->properties) { |
141 | lnAcceptIndented(node: ast->properties); |
142 | newLine(); |
143 | } |
144 | --expressionDepth; |
145 | out(loc: ast->rbraceToken); |
146 | return false; |
147 | } |
148 | |
149 | bool ScriptFormatter::visit(PatternElementList *ast) |
150 | { |
151 | for (PatternElementList *it = ast; it; it = it->next) { |
152 | const bool isObjectInitializer = |
153 | it->element && cast<ObjectPattern *>(ast: it->element->initializer); |
154 | if (isObjectInitializer) |
155 | newLine(); |
156 | |
157 | if (it->elision) |
158 | accept(node: it->elision); |
159 | if (it->elision && it->element) |
160 | out(str: ", "); |
161 | if (it->element) |
162 | accept(node: it->element); |
163 | if (it->next) { |
164 | out(str: ", "); |
165 | if (isObjectInitializer) |
166 | newLine(); |
167 | } |
168 | } |
169 | return false; |
170 | } |
171 | |
172 | bool ScriptFormatter::visit(PatternPropertyList *ast) |
173 | { |
174 | for (PatternPropertyList *it = ast; it; it = it->next) { |
175 | accept(node: it->property); |
176 | if (it->next) { |
177 | out(str: ","); |
178 | newLine(); |
179 | } |
180 | } |
181 | return false; |
182 | } |
183 | |
184 | // https://262.ecma-international.org/7.0/#prod-PropertyDefinition |
185 | bool ScriptFormatter::visit(AST::PatternProperty *property) |
186 | { |
187 | if (property->type == PatternElement::Getter || property->type == PatternElement::Setter |
188 | || property->type == PatternElement::Method) { |
189 | // note that MethodDefinitions and FunctionDeclarations have different syntax |
190 | // https://262.ecma-international.org/7.0/#prod-MethodDefinition |
191 | // https://262.ecma-international.org/7.0/#prod-FunctionDeclaration |
192 | // hence visit(FunctionDeclaration*) is not quite appropriate here |
193 | if (property->type == PatternProperty::Getter) |
194 | out(str: "get "); |
195 | else if (property->type == PatternProperty::Setter) |
196 | out(str: "set "); |
197 | FunctionExpression *f = AST::cast<FunctionExpression *>(ast: property->initializer); |
198 | if (f->isGenerator) { |
199 | out(str: "*"); |
200 | } |
201 | accept(node: property->name); |
202 | out(loc: f->lparenToken); |
203 | accept(node: f->formals); |
204 | out(loc: f->rparenToken); |
205 | out(loc: f->lbraceToken); |
206 | const bool scoped = f->lbraceToken.isValid(); |
207 | if (scoped) |
208 | ++expressionDepth; |
209 | if (f->body) { |
210 | if (f->body->next || scoped) { |
211 | lnAcceptIndented(node: f->body); |
212 | lw.newline(); |
213 | } else { |
214 | auto baseIndent = lw.increaseIndent(level: 1); |
215 | accept(node: f->body); |
216 | lw.decreaseIndent(level: 1, expectedIndent: baseIndent); |
217 | } |
218 | } |
219 | if (scoped) |
220 | --expressionDepth; |
221 | out(loc: f->rbraceToken); |
222 | return false; |
223 | } |
224 | |
225 | // IdentifierReference[?Yield] |
226 | accept(node: property->name); |
227 | bool useInitializer = false; |
228 | const bool bindingIdentifierExist = !property->bindingIdentifier.isEmpty(); |
229 | if (property->colonToken.isValid()) { |
230 | // PropertyName[?Yield] : AssignmentExpression[In, ?Yield] |
231 | out(str: ": "); |
232 | useInitializer = true; |
233 | if (bindingIdentifierExist) |
234 | out(str: property->bindingIdentifier); |
235 | if (property->bindingTarget) |
236 | accept(node: property->bindingTarget); |
237 | } |
238 | |
239 | if (property->initializer) { |
240 | // CoverInitializedName[?Yield] |
241 | if (bindingIdentifierExist) { |
242 | out(str: " = "); |
243 | useInitializer = true; |
244 | } |
245 | if (useInitializer) |
246 | accept(node: property->initializer); |
247 | } |
248 | return false; |
249 | } |
250 | |
251 | bool ScriptFormatter::visit(NestedExpression *ast) |
252 | { |
253 | out(loc: ast->lparenToken); |
254 | int baseIndent = lw.increaseIndent(level: 1); |
255 | accept(node: ast->expression); |
256 | lw.decreaseIndent(level: 1, expectedIndent: baseIndent); |
257 | out(loc: ast->rparenToken); |
258 | return false; |
259 | } |
260 | |
261 | bool ScriptFormatter::visit(IdentifierPropertyName *ast) |
262 | { |
263 | out(str: ast->id.toString()); |
264 | return true; |
265 | } |
266 | bool ScriptFormatter::visit(StringLiteralPropertyName *ast) |
267 | { |
268 | out(loc: ast->propertyNameToken); |
269 | return true; |
270 | } |
271 | bool ScriptFormatter::visit(NumericLiteralPropertyName *ast) |
272 | { |
273 | out(str: QString::number(ast->id)); |
274 | return true; |
275 | } |
276 | |
277 | bool ScriptFormatter::visit(TemplateLiteral *ast) |
278 | { |
279 | // correctly handle multiline literals |
280 | if (ast->literalToken.length != 0) { |
281 | QStringView str = loc2Str(ast->literalToken); |
282 | if (lw.indentNextlines && str.contains(c: QLatin1Char('\n'))) { |
283 | out(str: str.mid(pos: 0, n: 1)); |
284 | lw.indentNextlines = false; |
285 | out(str: str.mid(pos: 1)); |
286 | lw.indentNextlines = true; |
287 | } else { |
288 | out(str); |
289 | } |
290 | } |
291 | accept(node: ast->expression); |
292 | return true; |
293 | } |
294 | |
295 | bool ScriptFormatter::visit(ArrayMemberExpression *ast) |
296 | { |
297 | accept(node: ast->base); |
298 | out(loc: ast->lbracketToken); |
299 | int indent = lw.increaseIndent(level: 1); |
300 | accept(node: ast->expression); |
301 | lw.decreaseIndent(level: 1, expectedIndent: indent); |
302 | out(loc: ast->rbracketToken); |
303 | return false; |
304 | } |
305 | |
306 | bool ScriptFormatter::visit(FieldMemberExpression *ast) |
307 | { |
308 | accept(node: ast->base); |
309 | out(loc: ast->dotToken); |
310 | out(loc: ast->identifierToken); |
311 | return false; |
312 | } |
313 | |
314 | bool ScriptFormatter::visit(NewMemberExpression *ast) |
315 | { |
316 | out(str: "new "); // ast->newToken |
317 | accept(node: ast->base); |
318 | out(loc: ast->lparenToken); |
319 | accept(node: ast->arguments); |
320 | out(loc: ast->rparenToken); |
321 | return false; |
322 | } |
323 | |
324 | bool ScriptFormatter::visit(NewExpression *ast) |
325 | { |
326 | out(str: "new "); // ast->newToken |
327 | accept(node: ast->expression); |
328 | return false; |
329 | } |
330 | |
331 | bool ScriptFormatter::visit(CallExpression *ast) |
332 | { |
333 | accept(node: ast->base); |
334 | out(loc: ast->lparenToken); |
335 | accept(node: ast->arguments); |
336 | out(loc: ast->rparenToken); |
337 | return false; |
338 | } |
339 | |
340 | bool ScriptFormatter::visit(PostIncrementExpression *ast) |
341 | { |
342 | accept(node: ast->base); |
343 | out(loc: ast->incrementToken); |
344 | return false; |
345 | } |
346 | |
347 | bool ScriptFormatter::visit(PostDecrementExpression *ast) |
348 | { |
349 | accept(node: ast->base); |
350 | out(loc: ast->decrementToken); |
351 | return false; |
352 | } |
353 | |
354 | bool ScriptFormatter::visit(PreIncrementExpression *ast) |
355 | { |
356 | out(loc: ast->incrementToken); |
357 | accept(node: ast->expression); |
358 | return false; |
359 | } |
360 | |
361 | bool ScriptFormatter::visit(PreDecrementExpression *ast) |
362 | { |
363 | out(loc: ast->decrementToken); |
364 | accept(node: ast->expression); |
365 | return false; |
366 | } |
367 | |
368 | bool ScriptFormatter::visit(DeleteExpression *ast) |
369 | { |
370 | out(str: "delete "); // ast->deleteToken |
371 | accept(node: ast->expression); |
372 | return false; |
373 | } |
374 | |
375 | bool ScriptFormatter::visit(VoidExpression *ast) |
376 | { |
377 | out(str: "void "); // ast->voidToken |
378 | accept(node: ast->expression); |
379 | return false; |
380 | } |
381 | |
382 | bool ScriptFormatter::visit(TypeOfExpression *ast) |
383 | { |
384 | out(str: "typeof "); // ast->typeofToken |
385 | accept(node: ast->expression); |
386 | return false; |
387 | } |
388 | |
389 | bool ScriptFormatter::visit(UnaryPlusExpression *ast) |
390 | { |
391 | out(loc: ast->plusToken); |
392 | accept(node: ast->expression); |
393 | return false; |
394 | } |
395 | |
396 | bool ScriptFormatter::visit(UnaryMinusExpression *ast) |
397 | { |
398 | out(loc: ast->minusToken); |
399 | accept(node: ast->expression); |
400 | return false; |
401 | } |
402 | |
403 | bool ScriptFormatter::visit(TildeExpression *ast) |
404 | { |
405 | out(loc: ast->tildeToken); |
406 | accept(node: ast->expression); |
407 | return false; |
408 | } |
409 | |
410 | bool ScriptFormatter::visit(NotExpression *ast) |
411 | { |
412 | out(loc: ast->notToken); |
413 | accept(node: ast->expression); |
414 | return false; |
415 | } |
416 | |
417 | bool ScriptFormatter::visit(BinaryExpression *ast) |
418 | { |
419 | accept(node: ast->left); |
420 | out(str: " "); |
421 | out(loc: ast->operatorToken); |
422 | out(str: " "); |
423 | accept(node: ast->right); |
424 | return false; |
425 | } |
426 | |
427 | bool ScriptFormatter::visit(ConditionalExpression *ast) |
428 | { |
429 | accept(node: ast->expression); |
430 | out(str: " ? "); // ast->questionToken |
431 | accept(node: ast->ok); |
432 | out(str: " : "); // ast->colonToken |
433 | accept(node: ast->ko); |
434 | return false; |
435 | } |
436 | |
437 | bool ScriptFormatter::visit(Block *ast) |
438 | { |
439 | out(loc: ast->lbraceToken); |
440 | if (ast->statements) { |
441 | ++expressionDepth; |
442 | lnAcceptIndented(node: ast->statements); |
443 | newLine(); |
444 | --expressionDepth; |
445 | } |
446 | out(loc: ast->rbraceToken); |
447 | return false; |
448 | } |
449 | |
450 | bool ScriptFormatter::visit(VariableStatement *ast) |
451 | { |
452 | out(loc: ast->declarationKindToken); |
453 | out(str: " "); |
454 | accept(node: ast->declarations); |
455 | if (addSemicolons()) |
456 | out(str: ";"); |
457 | return false; |
458 | } |
459 | |
460 | bool ScriptFormatter::visit(PatternElement *ast) |
461 | { |
462 | switch (ast->type) { |
463 | case PatternElement::Literal: |
464 | case PatternElement::Method: |
465 | case PatternElement::Binding: |
466 | break; |
467 | case PatternElement::Getter: |
468 | out(str: "get "); |
469 | break; |
470 | case PatternElement::Setter: |
471 | out(str: "set "); |
472 | break; |
473 | case PatternElement::SpreadElement: |
474 | out(str: "..."); |
475 | break; |
476 | } |
477 | |
478 | accept(node: ast->bindingTarget); |
479 | if (!ast->destructuringPattern()) |
480 | out(loc: ast->identifierToken); |
481 | if (ast->initializer) { |
482 | if (ast->isVariableDeclaration() || ast->type == AST::PatternElement::Binding) |
483 | out(str: " = "); |
484 | accept(node: ast->initializer); |
485 | } |
486 | return false; |
487 | } |
488 | |
489 | bool ScriptFormatter::visit(EmptyStatement *ast) |
490 | { |
491 | out(loc: ast->semicolonToken); |
492 | return false; |
493 | } |
494 | |
495 | bool ScriptFormatter::visit(IfStatement *ast) |
496 | { |
497 | out(loc: ast->ifToken); |
498 | out(str: " "); |
499 | out(loc: ast->lparenToken); |
500 | preVisit(n: ast->expression); |
501 | ast->expression->accept0(visitor: this); |
502 | out(loc: ast->rparenToken); |
503 | postVisit(n: ast->expression); |
504 | acceptBlockOrIndented(ast: ast->ok, finishWithSpaceOrNewline: ast->ko); |
505 | if (ast->ko) { |
506 | out(loc: ast->elseToken); |
507 | if (cast<Block *>(ast: ast->ko) || cast<IfStatement *>(ast: ast->ko)) { |
508 | out(str: " "); |
509 | accept(node: ast->ko); |
510 | } else { |
511 | lnAcceptIndented(node: ast->ko); |
512 | } |
513 | } |
514 | return false; |
515 | } |
516 | |
517 | bool ScriptFormatter::visit(DoWhileStatement *ast) |
518 | { |
519 | out(loc: ast->doToken); |
520 | acceptBlockOrIndented(ast: ast->statement, finishWithSpaceOrNewline: true); |
521 | out(loc: ast->whileToken); |
522 | out(str: " "); |
523 | out(loc: ast->lparenToken); |
524 | accept(node: ast->expression); |
525 | out(loc: ast->rparenToken); |
526 | return false; |
527 | } |
528 | |
529 | bool ScriptFormatter::visit(WhileStatement *ast) |
530 | { |
531 | out(loc: ast->whileToken); |
532 | out(str: " "); |
533 | out(loc: ast->lparenToken); |
534 | accept(node: ast->expression); |
535 | out(loc: ast->rparenToken); |
536 | acceptBlockOrIndented(ast: ast->statement); |
537 | return false; |
538 | } |
539 | |
540 | bool ScriptFormatter::visit(ForStatement *ast) |
541 | { |
542 | out(loc: ast->forToken); |
543 | out(str: " "); |
544 | out(loc: ast->lparenToken); |
545 | if (ast->initialiser) { |
546 | accept(node: ast->initialiser); |
547 | } else if (ast->declarations) { |
548 | if (auto pe = ast->declarations->declaration) { |
549 | out(loc: pe->declarationKindToken); |
550 | out(str: " "); |
551 | } |
552 | for (VariableDeclarationList *it = ast->declarations; it; it = it->next) { |
553 | accept(node: it->declaration); |
554 | } |
555 | } |
556 | out(str: "; "); // ast->firstSemicolonToken |
557 | accept(node: ast->condition); |
558 | out(str: "; "); // ast->secondSemicolonToken |
559 | accept(node: ast->expression); |
560 | out(loc: ast->rparenToken); |
561 | acceptBlockOrIndented(ast: ast->statement); |
562 | return false; |
563 | } |
564 | |
565 | bool ScriptFormatter::visit(ForEachStatement *ast) |
566 | { |
567 | out(loc: ast->forToken); |
568 | out(str: " "); |
569 | out(loc: ast->lparenToken); |
570 | if (auto pe = AST::cast<PatternElement *>(ast: ast->lhs)) { |
571 | out(loc: pe->declarationKindToken); |
572 | out(str: " "); |
573 | } |
574 | accept(node: ast->lhs); |
575 | out(str: " "); |
576 | out(loc: ast->inOfToken); |
577 | out(str: " "); |
578 | accept(node: ast->expression); |
579 | out(loc: ast->rparenToken); |
580 | acceptBlockOrIndented(ast: ast->statement); |
581 | return false; |
582 | } |
583 | |
584 | bool ScriptFormatter::visit(ContinueStatement *ast) |
585 | { |
586 | out(loc: ast->continueToken); |
587 | if (!ast->label.isNull()) { |
588 | out(str: " "); |
589 | out(loc: ast->identifierToken); |
590 | } |
591 | if (addSemicolons()) |
592 | out(str: ";"); |
593 | return false; |
594 | } |
595 | |
596 | bool ScriptFormatter::visit(BreakStatement *ast) |
597 | { |
598 | out(loc: ast->breakToken); |
599 | if (!ast->label.isNull()) { |
600 | out(str: " "); |
601 | out(loc: ast->identifierToken); |
602 | } |
603 | if (addSemicolons()) |
604 | out(str: ";"); |
605 | return false; |
606 | } |
607 | |
608 | bool ScriptFormatter::visit(ReturnStatement *ast) |
609 | { |
610 | out(loc: ast->returnToken); |
611 | if (ast->expression) { |
612 | if (ast->returnToken.length != 0) |
613 | out(str: " "); |
614 | accept(node: ast->expression); |
615 | } |
616 | if (ast->returnToken.length > 0 && addSemicolons()) |
617 | out(str: ";"); |
618 | return false; |
619 | } |
620 | |
621 | bool ScriptFormatter::visit(ThrowStatement *ast) |
622 | { |
623 | out(loc: ast->throwToken); |
624 | if (ast->expression) { |
625 | out(str: " "); |
626 | accept(node: ast->expression); |
627 | } |
628 | if (addSemicolons()) |
629 | out(str: ";"); |
630 | return false; |
631 | } |
632 | |
633 | bool ScriptFormatter::visit(WithStatement *ast) |
634 | { |
635 | out(loc: ast->withToken); |
636 | out(str: " "); |
637 | out(loc: ast->lparenToken); |
638 | accept(node: ast->expression); |
639 | out(loc: ast->rparenToken); |
640 | acceptBlockOrIndented(ast: ast->statement); |
641 | return false; |
642 | } |
643 | |
644 | bool ScriptFormatter::visit(SwitchStatement *ast) |
645 | { |
646 | out(loc: ast->switchToken); |
647 | out(str: " "); |
648 | out(loc: ast->lparenToken); |
649 | accept(node: ast->expression); |
650 | out(loc: ast->rparenToken); |
651 | out(str: " "); |
652 | accept(node: ast->block); |
653 | return false; |
654 | } |
655 | |
656 | bool ScriptFormatter::visit(CaseBlock *ast) |
657 | { |
658 | out(loc: ast->lbraceToken); |
659 | ++expressionDepth; |
660 | newLine(); |
661 | accept(node: ast->clauses); |
662 | if (ast->clauses && ast->defaultClause) |
663 | newLine(); |
664 | accept(node: ast->defaultClause); |
665 | if (ast->moreClauses) |
666 | newLine(); |
667 | accept(node: ast->moreClauses); |
668 | newLine(); |
669 | --expressionDepth; |
670 | out(loc: ast->rbraceToken); |
671 | return false; |
672 | } |
673 | |
674 | bool ScriptFormatter::visit(CaseClause *ast) |
675 | { |
676 | out(str: "case "); // ast->caseToken |
677 | accept(node: ast->expression); |
678 | out(loc: ast->colonToken); |
679 | if (ast->statements) |
680 | lnAcceptIndented(node: ast->statements); |
681 | return false; |
682 | } |
683 | |
684 | bool ScriptFormatter::visit(DefaultClause *ast) |
685 | { |
686 | out(loc: ast->defaultToken); |
687 | out(loc: ast->colonToken); |
688 | lnAcceptIndented(node: ast->statements); |
689 | return false; |
690 | } |
691 | |
692 | bool ScriptFormatter::visit(LabelledStatement *ast) |
693 | { |
694 | out(loc: ast->identifierToken); |
695 | out(str: ": "); // ast->colonToken |
696 | accept(node: ast->statement); |
697 | return false; |
698 | } |
699 | |
700 | bool ScriptFormatter::visit(TryStatement *ast) |
701 | { |
702 | out(str: "try "); // ast->tryToken |
703 | accept(node: ast->statement); |
704 | if (ast->catchExpression) { |
705 | out(str: " "); |
706 | accept(node: ast->catchExpression); |
707 | } |
708 | if (ast->finallyExpression) { |
709 | out(str: " "); |
710 | accept(node: ast->finallyExpression); |
711 | } |
712 | return false; |
713 | } |
714 | |
715 | bool ScriptFormatter::visit(Catch *ast) |
716 | { |
717 | out(loc: ast->catchToken); |
718 | out(str: " "); |
719 | out(loc: ast->lparenToken); |
720 | out(loc: ast->identifierToken); |
721 | out(str: ") "); // ast->rparenToken |
722 | accept(node: ast->statement); |
723 | return false; |
724 | } |
725 | |
726 | bool ScriptFormatter::visit(Finally *ast) |
727 | { |
728 | out(str: "finally "); // ast->finallyToken |
729 | accept(node: ast->statement); |
730 | return false; |
731 | } |
732 | |
733 | bool ScriptFormatter::visit(FunctionDeclaration *ast) |
734 | { |
735 | return ScriptFormatter::visit(ast: static_cast<FunctionExpression *>(ast)); |
736 | } |
737 | |
738 | bool ScriptFormatter::visit(FunctionExpression *ast) |
739 | { |
740 | if (!ast->isArrowFunction) { |
741 | if (ast->isGenerator) { |
742 | out(str: "function* "); |
743 | } else { |
744 | out(str: "function "); |
745 | } |
746 | if (!ast->name.isNull()) |
747 | out(loc: ast->identifierToken); |
748 | } |
749 | out(loc: ast->lparenToken); |
750 | const bool needParentheses = ast->formals |
751 | && (ast->formals->next |
752 | || (ast->formals->element && ast->formals->element->bindingTarget)); |
753 | if (ast->isArrowFunction && needParentheses) |
754 | out(str: "("); |
755 | int baseIndent = lw.increaseIndent(level: 1); |
756 | accept(node: ast->formals); |
757 | lw.decreaseIndent(level: 1, expectedIndent: baseIndent); |
758 | if (ast->isArrowFunction && needParentheses) |
759 | out(str: ")"); |
760 | out(loc: ast->rparenToken); |
761 | if (ast->isArrowFunction && !ast->formals) |
762 | out(str: "()"); |
763 | out(str: " "); |
764 | if (ast->isArrowFunction) |
765 | out(str: "=> "); |
766 | out(loc: ast->lbraceToken); |
767 | if (ast->lbraceToken.length != 0) |
768 | ++expressionDepth; |
769 | if (ast->body) { |
770 | if (ast->body->next || ast->lbraceToken.length != 0) { |
771 | lnAcceptIndented(node: ast->body); |
772 | newLine(); |
773 | } else { |
774 | // print a single statement in one line. E.g. x => x * 2 |
775 | baseIndent = lw.increaseIndent(level: 1); |
776 | accept(node: ast->body); |
777 | lw.decreaseIndent(level: 1, expectedIndent: baseIndent); |
778 | } |
779 | } |
780 | if (ast->lbraceToken.length != 0) |
781 | --expressionDepth; |
782 | out(loc: ast->rbraceToken); |
783 | return false; |
784 | } |
785 | |
786 | bool ScriptFormatter::visit(Elision *ast) |
787 | { |
788 | for (Elision *it = ast; it; it = it->next) { |
789 | if (it->next) |
790 | out(str: ", "); // ast->commaToken |
791 | } |
792 | return false; |
793 | } |
794 | |
795 | bool ScriptFormatter::visit(ArgumentList *ast) |
796 | { |
797 | for (ArgumentList *it = ast; it; it = it->next) { |
798 | if (it->isSpreadElement) |
799 | out(str: "..."); |
800 | accept(node: it->expression); |
801 | if (it->next) { |
802 | out(str: ", "); // it->commaToken |
803 | } |
804 | } |
805 | return false; |
806 | } |
807 | |
808 | bool ScriptFormatter::visit(StatementList *ast) |
809 | { |
810 | ++expressionDepth; |
811 | for (StatementList *it = ast; it; it = it->next) { |
812 | // ### work around parser bug: skip empty statements with wrong tokens |
813 | if (EmptyStatement *emptyStatement = cast<EmptyStatement *>(ast: it->statement)) { |
814 | if (loc2Str(emptyStatement->semicolonToken) != QLatin1String(";")) |
815 | continue; |
816 | } |
817 | |
818 | accept(node: it->statement); |
819 | if (it->next) { |
820 | // There might be a post-comment attached to the current |
821 | // statement or a pre-comment attached to the next |
822 | // statmente or both. |
823 | // If any of those are present they will take care of |
824 | // handling the spacing between the statements so we |
825 | // don't need to push any newline. |
826 | auto *commentForCurrentStatement = comments->commentForNode(n: it->statement); |
827 | auto *commentForNextStatement = comments->commentForNode(n: it->next->statement); |
828 | |
829 | if ( |
830 | (commentForCurrentStatement && !commentForCurrentStatement->postComments().empty()) |
831 | || (commentForNextStatement && !commentForNextStatement->preComments().empty()) |
832 | ) continue; |
833 | |
834 | quint32 lineDelta = it->next->firstSourceLocation().startLine |
835 | - it->statement->lastSourceLocation().startLine; |
836 | lineDelta = std::clamp(val: lineDelta, lo: quint32{ 1 }, hi: quint32{ 2 }); |
837 | |
838 | newLine(count: lineDelta); |
839 | } |
840 | } |
841 | --expressionDepth; |
842 | return false; |
843 | } |
844 | |
845 | bool ScriptFormatter::visit(VariableDeclarationList *ast) |
846 | { |
847 | for (VariableDeclarationList *it = ast; it; it = it->next) { |
848 | accept(node: it->declaration); |
849 | if (it->next) |
850 | out(str: ", "); // it->commaToken |
851 | } |
852 | return false; |
853 | } |
854 | |
855 | bool ScriptFormatter::visit(CaseClauses *ast) |
856 | { |
857 | for (CaseClauses *it = ast; it; it = it->next) { |
858 | accept(node: it->clause); |
859 | if (it->next) |
860 | newLine(); |
861 | } |
862 | return false; |
863 | } |
864 | |
865 | bool ScriptFormatter::visit(FormalParameterList *ast) |
866 | { |
867 | for (FormalParameterList *it = ast; it; it = it->next) { |
868 | // compare FormalParameterList::finish |
869 | if (auto id = it->element->bindingIdentifier.toString(); !id.isEmpty()) |
870 | out(str: id); |
871 | if (it->element->bindingTarget) |
872 | accept(node: it->element->bindingTarget); |
873 | if (it->next) |
874 | out(str: ", "); |
875 | } |
876 | return false; |
877 | } |
878 | |
879 | // to check |
880 | bool ScriptFormatter::visit(SuperLiteral *) |
881 | { |
882 | out(str: "super"); |
883 | return true; |
884 | } |
885 | bool ScriptFormatter::visit(ComputedPropertyName *) |
886 | { |
887 | out(str: "["); |
888 | return true; |
889 | } |
890 | bool ScriptFormatter::visit(Expression *el) |
891 | { |
892 | accept(node: el->left); |
893 | out(str: ", "); |
894 | accept(node: el->right); |
895 | return false; |
896 | } |
897 | bool ScriptFormatter::visit(ExpressionStatement *el) |
898 | { |
899 | if (addSemicolons()) |
900 | postOps[el->expression].append(t: [this]() { out(str: ";"); }); |
901 | return true; |
902 | } |
903 | |
904 | // Return false because we want to omit default function calls in accept0 implementation. |
905 | bool ScriptFormatter::visit(ClassDeclaration *ast) |
906 | { |
907 | preVisit(n: ast); |
908 | out(loc: ast->classToken); |
909 | out(str: " "); |
910 | out(str: ast->name); |
911 | if (ast->heritage) { |
912 | out(str: " extends "); |
913 | accept(node: ast->heritage); |
914 | } |
915 | out(str: " {"); |
916 | int baseIndent = lw.increaseIndent(); |
917 | for (ClassElementList *it = ast->elements; it; it = it->next) { |
918 | lw.newline(); |
919 | if (it->isStatic) |
920 | out(str: "static "); |
921 | accept(node: it->property); |
922 | lw.newline(); |
923 | } |
924 | lw.decreaseIndent(level: 1, expectedIndent: baseIndent); |
925 | out(str: "}"); |
926 | postVisit(n: ast); |
927 | return false; |
928 | } |
929 | |
930 | bool ScriptFormatter::visit(AST::ImportDeclaration *ast) |
931 | { |
932 | out(loc: ast->importToken); |
933 | lw.space(); |
934 | if (!ast->moduleSpecifier.isNull()) { |
935 | out(loc: ast->moduleSpecifierToken); |
936 | } |
937 | return true; |
938 | } |
939 | |
940 | bool ScriptFormatter::visit(AST::ImportSpecifier *ast) |
941 | { |
942 | if (!ast->identifier.isNull()) { |
943 | out(loc: ast->identifierToken); |
944 | lw.space(); |
945 | out(str: "as"); |
946 | lw.space(); |
947 | } |
948 | out(loc: ast->importedBindingToken); |
949 | return true; |
950 | } |
951 | |
952 | bool ScriptFormatter::visit(AST::NameSpaceImport *ast) |
953 | { |
954 | out(loc: ast->starToken); |
955 | lw.space(); |
956 | out(str: "as"); |
957 | lw.space(); |
958 | out(loc: ast->importedBindingToken); |
959 | return true; |
960 | } |
961 | |
962 | bool ScriptFormatter::visit(AST::ImportsList *ast) |
963 | { |
964 | for (ImportsList *it = ast; it; it = it->next) { |
965 | accept(node: it->importSpecifier); |
966 | if (it->next) { |
967 | out(str: ","); |
968 | lw.space(); |
969 | } |
970 | } |
971 | return false; |
972 | } |
973 | bool ScriptFormatter::visit(AST::NamedImports *ast) |
974 | { |
975 | out(loc: ast->leftBraceToken); |
976 | if (ast->importsList) { |
977 | lw.space(); |
978 | } |
979 | return true; |
980 | } |
981 | |
982 | bool ScriptFormatter::visit(AST::ImportClause *ast) |
983 | { |
984 | if (!ast->importedDefaultBinding.isNull()) { |
985 | out(loc: ast->importedDefaultBindingToken); |
986 | if (ast->nameSpaceImport || ast->namedImports) { |
987 | out(str: ","); |
988 | lw.space(); |
989 | } |
990 | } |
991 | return true; |
992 | } |
993 | |
994 | bool ScriptFormatter::visit(AST::ExportDeclaration *ast) |
995 | { |
996 | out(loc: ast->exportToken); |
997 | lw.space(); |
998 | if (ast->exportDefault) { |
999 | out(str: "default"); |
1000 | lw.space(); |
1001 | } |
1002 | if (ast->exportsAll()) { |
1003 | out(str: "*"); |
1004 | } |
1005 | return true; |
1006 | } |
1007 | |
1008 | bool ScriptFormatter::visit(AST::ExportClause *ast) |
1009 | { |
1010 | out(loc: ast->leftBraceToken); |
1011 | if (ast->exportsList) { |
1012 | lw.space(); |
1013 | } |
1014 | return true; |
1015 | } |
1016 | |
1017 | bool ScriptFormatter::visit(AST::ExportSpecifier *ast) |
1018 | { |
1019 | out(str: ast->identifier); |
1020 | if (ast->exportedIdentifierToken.isValid()) { |
1021 | lw.space(); |
1022 | out(str: "as"); |
1023 | lw.space(); |
1024 | out(str: ast->exportedIdentifier); |
1025 | } |
1026 | return true; |
1027 | } |
1028 | |
1029 | bool ScriptFormatter::visit(AST::ExportsList *ast) |
1030 | { |
1031 | for (ExportsList *it = ast; it; it = it->next) { |
1032 | accept(node: it->exportSpecifier); |
1033 | if (it->next) { |
1034 | out(str: ","); |
1035 | lw.space(); |
1036 | } |
1037 | } |
1038 | return false; |
1039 | } |
1040 | |
1041 | bool ScriptFormatter::visit(AST::FromClause *ast) |
1042 | { |
1043 | lw.space(); |
1044 | out(loc: ast->fromToken); |
1045 | lw.space(); |
1046 | out(loc: ast->moduleSpecifierToken); |
1047 | return true; |
1048 | } |
1049 | |
1050 | void ScriptFormatter::endVisit(ComputedPropertyName *) |
1051 | { |
1052 | out(str: "]"); |
1053 | } |
1054 | |
1055 | void ScriptFormatter::endVisit(AST::ExportDeclaration *ast) |
1056 | { |
1057 | // add a semicolon at the end of the following expressions |
1058 | // export * FromClause ; |
1059 | // export ExportClause FromClause ; |
1060 | if (ast->fromClause) { |
1061 | out(str: ";"); |
1062 | } |
1063 | |
1064 | // add a semicolon at the end of the following expressions |
1065 | // export ExportClause ; |
1066 | if (ast->exportClause && !ast->fromClause) { |
1067 | out(str: ";"); |
1068 | } |
1069 | |
1070 | // add a semicolon at the end of the following expressions |
1071 | // export default [lookahead ∉ { function, class }] AssignmentExpression; |
1072 | if (ast->exportDefault && ast->variableStatementOrDeclaration) { |
1073 | // lookahead ∉ { function, class } |
1074 | if (!(ast->variableStatementOrDeclaration->kind == Node::Kind_FunctionDeclaration |
1075 | || ast->variableStatementOrDeclaration->kind == Node::Kind_ClassDeclaration)) { |
1076 | out(str: ";"); |
1077 | } |
1078 | // ArrowFunction in QQmlJS::AST is handled with the help of FunctionDeclaration |
1079 | // and not as part of AssignmentExpression (as per ECMA |
1080 | // https://262.ecma-international.org/7.0/#prod-AssignmentExpression) |
1081 | if (ast->variableStatementOrDeclaration->kind == Node::Kind_FunctionDeclaration |
1082 | && static_cast<AST::FunctionDeclaration *>(ast->variableStatementOrDeclaration) |
1083 | ->isArrowFunction) { |
1084 | out(str: ";"); |
1085 | } |
1086 | } |
1087 | } |
1088 | |
1089 | void ScriptFormatter::endVisit(AST::ExportClause *ast) |
1090 | { |
1091 | if (ast->exportsList) { |
1092 | lw.space(); |
1093 | } |
1094 | out(loc: ast->rightBraceToken); |
1095 | } |
1096 | |
1097 | void ScriptFormatter::endVisit(AST::NamedImports *ast) |
1098 | { |
1099 | if (ast->importsList) { |
1100 | lw.space(); |
1101 | } |
1102 | out(loc: ast->rightBraceToken); |
1103 | } |
1104 | |
1105 | void ScriptFormatter::endVisit(AST::ImportDeclaration *) |
1106 | { |
1107 | out(str: ";"); |
1108 | } |
1109 | |
1110 | void ScriptFormatter::throwRecursionDepthError() |
1111 | { |
1112 | out(str: "/* ERROR: Hit recursion limit ScriptFormatter::visiting AST, rewrite failed */"); |
1113 | } |
1114 | |
1115 | void reformatAst(OutWriter &lw, const std::shared_ptr<AstComments> &comments, |
1116 | const std::function<QStringView(SourceLocation)> &loc2Str, AST::Node *n) |
1117 | { |
1118 | if (n) { |
1119 | ScriptFormatter formatter(lw, comments, loc2Str, n); |
1120 | } |
1121 | } |
1122 | |
1123 | } // namespace Dom |
1124 | } // namespace QQmlJS |
1125 | QT_END_NAMESPACE |
1126 |
Definitions
- preVisit
- postVisit
- lnAcceptIndented
- acceptBlockOrIndented
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- visit
- endVisit
- endVisit
- endVisit
- endVisit
- endVisit
- throwRecursionDepthError
Learn to use CMake with our Intro Training
Find out more