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

source code of qtdeclarative/src/qmldom/qqmldomreformatter.cpp