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 | 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 | |
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 | 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 | |
175 | bool 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 |
188 | bool 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 | |
260 | bool 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 | |
270 | bool ScriptFormatter::visit(IdentifierPropertyName *ast) |
271 | { |
272 | out(str: ast->id.toString()); |
273 | return true; |
274 | } |
275 | bool ScriptFormatter::visit(StringLiteralPropertyName *ast) |
276 | { |
277 | out(loc: ast->propertyNameToken); |
278 | return true; |
279 | } |
280 | bool ScriptFormatter::visit(NumericLiteralPropertyName *ast) |
281 | { |
282 | out(str: QString::number(ast->id)); |
283 | return true; |
284 | } |
285 | |
286 | bool 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 | |
304 | bool 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 | |
316 | bool 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 | |
324 | bool 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 | |
335 | bool 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 | |
343 | bool 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 | |
353 | bool ScriptFormatter::visit(PostIncrementExpression *ast) |
354 | { |
355 | accept(node: ast->base); |
356 | out(loc: ast->incrementToken); |
357 | return false; |
358 | } |
359 | |
360 | bool ScriptFormatter::visit(PostDecrementExpression *ast) |
361 | { |
362 | accept(node: ast->base); |
363 | out(loc: ast->decrementToken); |
364 | return false; |
365 | } |
366 | |
367 | bool ScriptFormatter::visit(PreIncrementExpression *ast) |
368 | { |
369 | out(loc: ast->incrementToken); |
370 | accept(node: ast->expression); |
371 | return false; |
372 | } |
373 | |
374 | bool ScriptFormatter::visit(PreDecrementExpression *ast) |
375 | { |
376 | out(loc: ast->decrementToken); |
377 | accept(node: ast->expression); |
378 | return false; |
379 | } |
380 | |
381 | bool 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 | |
389 | bool 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 | |
397 | bool 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 | |
405 | bool ScriptFormatter::visit(UnaryPlusExpression *ast) |
406 | { |
407 | out(loc: ast->plusToken); |
408 | accept(node: ast->expression); |
409 | return false; |
410 | } |
411 | |
412 | bool ScriptFormatter::visit(UnaryMinusExpression *ast) |
413 | { |
414 | out(loc: ast->minusToken); |
415 | accept(node: ast->expression); |
416 | return false; |
417 | } |
418 | |
419 | bool ScriptFormatter::visit(TildeExpression *ast) |
420 | { |
421 | out(loc: ast->tildeToken); |
422 | accept(node: ast->expression); |
423 | return false; |
424 | } |
425 | |
426 | bool ScriptFormatter::visit(NotExpression *ast) |
427 | { |
428 | out(loc: ast->notToken); |
429 | accept(node: ast->expression); |
430 | return false; |
431 | } |
432 | |
433 | bool 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 | |
443 | bool 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 | |
457 | bool 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 | |
470 | bool 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 | |
480 | bool 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 | |
514 | bool ScriptFormatter::visit(EmptyStatement *ast) |
515 | { |
516 | out(loc: ast->semicolonToken); |
517 | return false; |
518 | } |
519 | |
520 | bool 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 | |
542 | bool 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 | |
554 | bool 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 | |
565 | bool 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 | |
592 | bool 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 | |
611 | bool 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 | |
623 | bool 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 | |
635 | bool 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 | |
648 | bool 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 | |
660 | bool 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 | |
671 | bool 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 | |
683 | bool 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 | |
701 | bool 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 | |
712 | bool 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 | |
720 | bool 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 | |
729 | bool 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 | |
745 | bool 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 | |
757 | bool 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 | |
765 | bool ScriptFormatter::visit(FunctionDeclaration *ast) |
766 | { |
767 | return ScriptFormatter::visit(ast: static_cast<FunctionExpression *>(ast)); |
768 | } |
769 | |
770 | bool 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 | |
822 | bool 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 | |
833 | bool 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 | |
847 | bool 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 * = comments->commentForNode(n: it->statement); |
866 | auto * = 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 | |
884 | bool 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 | |
896 | bool 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 | |
906 | bool 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 |
923 | bool ScriptFormatter::visit(SuperLiteral *) |
924 | { |
925 | out(str: "super" ); |
926 | return true; |
927 | } |
928 | bool ScriptFormatter::visit(ComputedPropertyName *) |
929 | { |
930 | out(str: "[" ); |
931 | return true; |
932 | } |
933 | bool 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 | } |
941 | bool 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. |
949 | bool 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 | |
979 | bool 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 | |
989 | bool 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 | |
1001 | bool 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 | |
1011 | bool 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 | } |
1022 | bool ScriptFormatter::visit(AST::NamedImports *ast) |
1023 | { |
1024 | out(loc: ast->leftBraceToken); |
1025 | if (ast->importsList) { |
1026 | lw.ensureSpace(); |
1027 | } |
1028 | return true; |
1029 | } |
1030 | |
1031 | bool 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 | |
1043 | bool 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 | |
1057 | bool ScriptFormatter::visit(AST::ExportClause *ast) |
1058 | { |
1059 | out(loc: ast->leftBraceToken); |
1060 | if (ast->exportsList) { |
1061 | lw.ensureSpace(); |
1062 | } |
1063 | return true; |
1064 | } |
1065 | |
1066 | bool 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 | |
1078 | bool 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 | |
1090 | bool 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 | |
1099 | void ScriptFormatter::endVisit(ComputedPropertyName *) |
1100 | { |
1101 | out(str: "]" ); |
1102 | } |
1103 | |
1104 | void 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 | |
1138 | void ScriptFormatter::endVisit(AST::ExportClause *ast) |
1139 | { |
1140 | if (ast->exportsList) { |
1141 | lw.ensureSpace(); |
1142 | } |
1143 | out(loc: ast->rightBraceToken); |
1144 | } |
1145 | |
1146 | void ScriptFormatter::endVisit(AST::NamedImports *ast) |
1147 | { |
1148 | if (ast->importsList) { |
1149 | lw.ensureSpace(); |
1150 | } |
1151 | out(loc: ast->rightBraceToken); |
1152 | } |
1153 | |
1154 | void ScriptFormatter::endVisit(AST::ImportDeclaration *) |
1155 | { |
1156 | out(str: ";" ); |
1157 | } |
1158 | |
1159 | void ScriptFormatter::throwRecursionDepthError() |
1160 | { |
1161 | out(str: "/* ERROR: Hit recursion limit ScriptFormatter::visiting AST, rewrite failed */" ); |
1162 | } |
1163 | |
1164 | void (OutWriter &lw, const std::shared_ptr<AstComments> &, |
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 |
1174 | QT_END_NAMESPACE |
1175 | |