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 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
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 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
172bool 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
185bool 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
251bool 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
261bool ScriptFormatter::visit(IdentifierPropertyName *ast)
262{
263 out(str: ast->id.toString());
264 return true;
265}
266bool ScriptFormatter::visit(StringLiteralPropertyName *ast)
267{
268 out(loc: ast->propertyNameToken);
269 return true;
270}
271bool ScriptFormatter::visit(NumericLiteralPropertyName *ast)
272{
273 out(str: QString::number(ast->id));
274 return true;
275}
276
277bool 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
295bool 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
306bool 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
314bool 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
324bool ScriptFormatter::visit(NewExpression *ast)
325{
326 out(str: "new "); // ast->newToken
327 accept(node: ast->expression);
328 return false;
329}
330
331bool 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
340bool ScriptFormatter::visit(PostIncrementExpression *ast)
341{
342 accept(node: ast->base);
343 out(loc: ast->incrementToken);
344 return false;
345}
346
347bool ScriptFormatter::visit(PostDecrementExpression *ast)
348{
349 accept(node: ast->base);
350 out(loc: ast->decrementToken);
351 return false;
352}
353
354bool ScriptFormatter::visit(PreIncrementExpression *ast)
355{
356 out(loc: ast->incrementToken);
357 accept(node: ast->expression);
358 return false;
359}
360
361bool ScriptFormatter::visit(PreDecrementExpression *ast)
362{
363 out(loc: ast->decrementToken);
364 accept(node: ast->expression);
365 return false;
366}
367
368bool ScriptFormatter::visit(DeleteExpression *ast)
369{
370 out(str: "delete "); // ast->deleteToken
371 accept(node: ast->expression);
372 return false;
373}
374
375bool ScriptFormatter::visit(VoidExpression *ast)
376{
377 out(str: "void "); // ast->voidToken
378 accept(node: ast->expression);
379 return false;
380}
381
382bool ScriptFormatter::visit(TypeOfExpression *ast)
383{
384 out(str: "typeof "); // ast->typeofToken
385 accept(node: ast->expression);
386 return false;
387}
388
389bool ScriptFormatter::visit(UnaryPlusExpression *ast)
390{
391 out(loc: ast->plusToken);
392 accept(node: ast->expression);
393 return false;
394}
395
396bool ScriptFormatter::visit(UnaryMinusExpression *ast)
397{
398 out(loc: ast->minusToken);
399 accept(node: ast->expression);
400 return false;
401}
402
403bool ScriptFormatter::visit(TildeExpression *ast)
404{
405 out(loc: ast->tildeToken);
406 accept(node: ast->expression);
407 return false;
408}
409
410bool ScriptFormatter::visit(NotExpression *ast)
411{
412 out(loc: ast->notToken);
413 accept(node: ast->expression);
414 return false;
415}
416
417bool 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
427bool 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
437bool 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
450bool 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
460bool 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
489bool ScriptFormatter::visit(EmptyStatement *ast)
490{
491 out(loc: ast->semicolonToken);
492 return false;
493}
494
495bool 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
517bool 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
529bool 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
540bool 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
565bool 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
584bool 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
596bool 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
608bool 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
621bool 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
633bool 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
644bool 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
656bool 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
674bool 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
684bool 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
692bool 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
700bool 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
715bool 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
726bool ScriptFormatter::visit(Finally *ast)
727{
728 out(str: "finally "); // ast->finallyToken
729 accept(node: ast->statement);
730 return false;
731}
732
733bool ScriptFormatter::visit(FunctionDeclaration *ast)
734{
735 return ScriptFormatter::visit(ast: static_cast<FunctionExpression *>(ast));
736}
737
738bool 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
786bool 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
795bool 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
808bool 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
845bool 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
855bool 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
865bool 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
880bool ScriptFormatter::visit(SuperLiteral *)
881{
882 out(str: "super");
883 return true;
884}
885bool ScriptFormatter::visit(ComputedPropertyName *)
886{
887 out(str: "[");
888 return true;
889}
890bool ScriptFormatter::visit(Expression *el)
891{
892 accept(node: el->left);
893 out(str: ", ");
894 accept(node: el->right);
895 return false;
896}
897bool 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.
905bool 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
930bool 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
940bool 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
952bool 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
962bool 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}
973bool ScriptFormatter::visit(AST::NamedImports *ast)
974{
975 out(loc: ast->leftBraceToken);
976 if (ast->importsList) {
977 lw.space();
978 }
979 return true;
980}
981
982bool 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
994bool 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
1008bool ScriptFormatter::visit(AST::ExportClause *ast)
1009{
1010 out(loc: ast->leftBraceToken);
1011 if (ast->exportsList) {
1012 lw.space();
1013 }
1014 return true;
1015}
1016
1017bool 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
1029bool 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
1041bool 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
1050void ScriptFormatter::endVisit(ComputedPropertyName *)
1051{
1052 out(str: "]");
1053}
1054
1055void 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
1089void ScriptFormatter::endVisit(AST::ExportClause *ast)
1090{
1091 if (ast->exportsList) {
1092 lw.space();
1093 }
1094 out(loc: ast->rightBraceToken);
1095}
1096
1097void ScriptFormatter::endVisit(AST::NamedImports *ast)
1098{
1099 if (ast->importsList) {
1100 lw.space();
1101 }
1102 out(loc: ast->rightBraceToken);
1103}
1104
1105void ScriptFormatter::endVisit(AST::ImportDeclaration *)
1106{
1107 out(str: ";");
1108}
1109
1110void ScriptFormatter::throwRecursionDepthError()
1111{
1112 out(str: "/* ERROR: Hit recursion limit ScriptFormatter::visiting AST, rewrite failed */");
1113}
1114
1115void 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
1125QT_END_NAMESPACE
1126

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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