Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmldomcodeformatter.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 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// Qt-Security score:significant
4
6
7#include <QLoggingCategory>
8#include <QMetaEnum>
9
10Q_STATIC_LOGGING_CATEGORY(formatterLog, "qt.qmldom.formatter", QtWarningMsg);
11
12QT_BEGIN_NAMESPACE
13namespace QQmlJS {
14namespace Dom {
15
18
19State FormatTextStatus::state(int belowTop) const
20{
21 if (belowTop < states.size())
22 return states.at(states.size() - 1 - belowTop);
23 else
24 return State();
25}
26
27QString FormatTextStatus::stateToString(StateType type)
28{
29 const QMetaEnum &metaEnum =
30 staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("StateType"));
31 return QString::fromUtf8(metaEnum.valueToKey(int(type)));
32}
33
34void FormatPartialStatus::enterState(StateType newState)
35{
36 int savedIndentDepth = currentIndent;
37 defaultOnEnter(newState, &currentIndent, &savedIndentDepth);
38 currentStatus.pushState(newState, savedIndentDepth);
39 qCDebug(formatterLog) << "enter state" << FormatTextStatus::stateToString(newState);
40
41 if (newState == StateType::BracketOpen)
42 enterState(StateType::BracketElementStart);
43}
44
45void FormatPartialStatus::leaveState(bool statementDone)
46{
47 Q_ASSERT(currentStatus.size() > 1);
48 if (currentStatus.state().type == StateType::TopmostIntro)
49 return;
50
51 // restore indent depth
52 State poppedState = currentStatus.popState();
53 currentIndent = poppedState.savedIndentDepth;
54
55 StateType topState = currentStatus.state().type;
56
57 qCDebug(formatterLog) << "left state" << FormatTextStatus::stateToString(poppedState.type)
58 << ", now in state" << FormatTextStatus::stateToString(topState);
59
60 // if statement is done, may need to leave recursively
61 if (statementDone) {
62 if (topState == StateType::IfStatement) {
63 if (poppedState.type != StateType::MaybeElse)
64 enterState(StateType::MaybeElse);
65 else
66 leaveState(true);
67 } else if (topState == StateType::ElseClause) {
68 // leave the else *and* the surrounding if, to prevent another else
69 leaveState(false);
70 leaveState(true);
71 } else if (topState == StateType::TryStatement) {
72 if (poppedState.type != StateType::MaybeCatchOrFinally
73 && poppedState.type != StateType::FinallyStatement) {
74 enterState(StateType::MaybeCatchOrFinally);
75 } else {
76 leaveState(true);
77 }
78 } else if (!FormatTextStatus::isExpressionEndState(topState)) {
79 leaveState(true);
80 }
81 }
82}
83
84void FormatPartialStatus::turnIntoState(StateType newState)
85{
86 leaveState(false);
87 enterState(newState);
88}
89
90const Token &FormatPartialStatus::tokenAt(int idx) const
91{
92 static const Token empty;
93 if (idx < 0 || idx >= lineTokens.size())
94 return empty;
95 else
96 return lineTokens.at(idx);
97}
98
99int FormatPartialStatus::column(int index) const
100{
101 if (index > line.size())
102 index = line.size();
103 IndentInfo indent(QStringView(line).mid(0, index), options.tabSize, indentOffset);
104 return indent.column;
105}
106
108{
109 return line.mid(token.begin(), token.length);
110}
111
112bool FormatPartialStatus::tryInsideExpression(bool alsoExpression)
113{
114 StateType newState = StateType::Invalid;
115 const int kind = tokenAt(tokenIndex).lexKind;
116 switch (kind) {
117 case QQmlJSGrammar::T_LPAREN:
118 newState = StateType::ParenOpen;
119 break;
120 case QQmlJSGrammar::T_LBRACKET:
121 newState = StateType::BracketOpen;
122 break;
123 case QQmlJSGrammar::T_LBRACE:
124 newState = StateType::ObjectliteralOpen;
125 break;
126 case QQmlJSGrammar::T_FUNCTION:
127 newState = StateType::FunctionStart;
128 break;
129 case QQmlJSGrammar::T_ARROW:
130 newState = StateType::LambdaStart;
131 break;
132 case QQmlJSGrammar::T_QUESTION:
133 newState = StateType::TernaryOp;
134 break;
135 }
136
137 if (newState != StateType::Invalid) {
138 if (alsoExpression)
139 enterState(StateType::Expression);
140 enterState(newState);
141 return true;
142 }
143
144 return false;
145}
146
148{
150 const int kind = t.lexKind;
151 switch (kind) {
152 case QQmlJSGrammar::T_AUTOMATIC_SEMICOLON:
153 case QQmlJSGrammar::T_COMPATIBILITY_SEMICOLON:
154 case QQmlJSGrammar::T_SEMICOLON:
155 enterState(StateType::EmptyStatement);
156 leaveState(true);
157 return true;
158 case QQmlJSGrammar::T_BREAK:
159 case QQmlJSGrammar::T_CONTINUE:
160 enterState(StateType::BreakcontinueStatement);
161 return true;
162 case QQmlJSGrammar::T_THROW:
163 enterState(StateType::ThrowStatement);
164 enterState(StateType::Expression);
165 return true;
166 case QQmlJSGrammar::T_RETURN:
167 enterState(StateType::ReturnStatement);
168 enterState(StateType::Expression);
169 return true;
170 case QQmlJSGrammar::T_WHILE:
171 case QQmlJSGrammar::T_FOR:
172 case QQmlJSGrammar::T_CATCH:
173 enterState(StateType::StatementWithCondition);
174 return true;
175 case QQmlJSGrammar::T_SWITCH:
176 enterState(StateType::SwitchStatement);
177 return true;
178 case QQmlJSGrammar::T_IF:
179 enterState(StateType::IfStatement);
180 return true;
181 case QQmlJSGrammar::T_DO:
182 enterState(StateType::DoStatement);
183 enterState(StateType::Substatement);
184 return true;
185 case QQmlJSGrammar::T_CASE:
186 case QQmlJSGrammar::T_DEFAULT:
187 enterState(StateType::CaseStart);
188 return true;
189 case QQmlJSGrammar::T_TRY:
190 enterState(StateType::TryStatement);
191 return true;
192 case QQmlJSGrammar::T_LBRACE:
193 enterState(StateType::JsblockOpen);
194 return true;
195 case QQmlJSGrammar::T_VAR:
196 case QQmlJSGrammar::T_PLUS_PLUS:
197 case QQmlJSGrammar::T_MINUS_MINUS:
198 case QQmlJSGrammar::T_IMPORT:
199 case QQmlJSGrammar::T_SIGNAL:
200 case QQmlJSGrammar::T_ON:
201 case QQmlJSGrammar::T_AS:
202 case QQmlJSGrammar::T_PROPERTY:
203 case QQmlJSGrammar::T_REQUIRED:
204 case QQmlJSGrammar::T_READONLY:
205 case QQmlJSGrammar::T_FUNCTION:
206 case QQmlJSGrammar::T_NUMERIC_LITERAL:
207 case QQmlJSGrammar::T_LPAREN:
208 enterState(StateType::Expression);
209 // look at the token again
210 tokenIndex -= 1;
211 return true;
212 default:
214 enterState(StateType::ExpressionOrLabel);
215 return true;
216 } else if (Token::lexKindIsDelimiter(kind) || Token::lexKindIsStringType(kind)) {
217 enterState(StateType::Expression);
218 // look at the token again
219 tokenIndex -= 1;
220 return true;
221 }
222 }
223 return false;
224}
225
227{
228 qCDebug(formatterLog) << "Current token index" << tokenIndex;
229 qCDebug(formatterLog) << "Current state:";
230 for (const State &s : currentStatus.states)
231 qCDebug(formatterLog) << FormatTextStatus::stateToString(s.type) << s.savedIndentDepth;
232 qCDebug(formatterLog) << "Current lexerState:" << currentStatus.lexerState.state;
233 qCDebug(formatterLog) << "Current indent:" << currentIndent;
234}
235
237{
238 auto enter = [this](StateType newState) { this->enterState(newState); };
239
240 auto leave = [this](bool statementDone = false) { this->leaveState(statementDone); };
241
242 auto turnInto = [this](StateType newState) { this->turnIntoState(newState); };
243
244 qCDebug(formatterLog) << "Starting to look at " << line;
245
246 for (; tokenIndex < lineTokens.size();) {
247 Token currentToken = tokenAt(tokenIndex);
248 const int kind = currentToken.lexKind;
249
250 qCDebug(formatterLog) << "Token: " << tokenText(currentToken);
251
252 if (Token::lexKindIsComment(kind)
253 && currentStatus.state().type != StateType::MultilineCommentCont
254 && currentStatus.state().type != StateType::MultilineCommentStart) {
255 tokenIndex += 1;
256 continue;
257 }
258
259 switch (currentStatus.state().type) {
260 case StateType::TopmostIntro:
261 switch (kind) {
262 case QQmlJSGrammar::T_IDENTIFIER:
263 enter(StateType::ObjectdefinitionOrJs);
264 continue;
265 case QQmlJSGrammar::T_IMPORT:
266 enter(StateType::TopQml);
267 continue;
268 case QQmlJSGrammar::T_LBRACE:
269 enter(StateType::TopJs);
270 enter(StateType::Expression);
271 continue; // if a file starts with {, it's likely json
272 default:
273 enter(StateType::TopJs);
274 continue;
275 }
276 break;
277
278 case StateType::TopQml:
279 switch (kind) {
280 case QQmlJSGrammar::T_IMPORT:
281 enter(StateType::ImportStart);
282 break;
283 case QQmlJSGrammar::T_IDENTIFIER:
284 enter(StateType::BindingOrObjectdefinition);
285 break;
286 default:
288 enter(StateType::BindingOrObjectdefinition);
289 break;
290 }
291 break;
292
293 case StateType::TopJs:
295 break;
296
297 case StateType::ObjectdefinitionOrJs:
298 switch (kind) {
299 case QQmlJSGrammar::T_DOT:
300 break;
301 case QQmlJSGrammar::T_LBRACE:
302 turnInto(StateType::BindingOrObjectdefinition);
303 continue;
304 default:
305 if (!Token::lexKindIsIdentifier(kind) || !line.at(currentToken.begin()).isUpper()) {
306 turnInto(StateType::TopJs);
307 continue;
308 }
309 }
310 break;
311
312 case StateType::ImportStart:
313 enter(StateType::ImportMaybeDotOrVersionOrAs);
314 break;
315
316 case StateType::ImportMaybeDotOrVersionOrAs:
317 switch (kind) {
318 case QQmlJSGrammar::T_DOT:
319 turnInto(StateType::ImportDot);
320 break;
321 case QQmlJSGrammar::T_AS:
322 turnInto(StateType::ImportAs);
323 break;
324 case QQmlJSGrammar::T_NUMERIC_LITERAL:
325 case QQmlJSGrammar::T_VERSION_NUMBER:
326 turnInto(StateType::ImportMaybeAs);
327 break;
328 default:
329 leave();
330 leave();
331 continue;
332 }
333 break;
334
335 case StateType::ImportMaybeAs:
336 switch (kind) {
337 case QQmlJSGrammar::T_AS:
338 turnInto(StateType::ImportAs);
339 break;
340 default:
341 leave();
342 leave();
343 continue;
344 }
345 break;
346
347 case StateType::ImportDot:
349 turnInto(StateType::ImportMaybeDotOrVersionOrAs);
350 } else {
351 leave();
352 leave();
353 continue;
354 }
355 break;
356
357 case StateType::ImportAs:
359 leave();
360 leave();
361 }
362 break;
363
364 case StateType::BindingOrObjectdefinition:
365 switch (kind) {
366 case QQmlJSGrammar::T_COLON:
367 enter(StateType::BindingAssignment);
368 break;
369 case QQmlJSGrammar::T_LBRACE:
370 enter(StateType::ObjectdefinitionOpen);
371 break;
372 }
373 break;
374
375 case StateType::BindingAssignment:
376 switch (kind) {
377 case QQmlJSGrammar::T_AUTOMATIC_SEMICOLON:
378 case QQmlJSGrammar::T_COMPATIBILITY_SEMICOLON:
379 case QQmlJSGrammar::T_SEMICOLON:
380 leave(true);
381 break;
382 case QQmlJSGrammar::T_IF:
383 enter(StateType::IfStatement);
384 break;
385 case QQmlJSGrammar::T_WITH:
386 enter(StateType::StatementWithCondition);
387 break;
388 case QQmlJSGrammar::T_TRY:
389 enter(StateType::TryStatement);
390 break;
391 case QQmlJSGrammar::T_SWITCH:
392 enter(StateType::SwitchStatement);
393 break;
394 case QQmlJSGrammar::T_LBRACE:
395 enter(StateType::JsblockOpen);
396 break;
397 case QQmlJSGrammar::T_ON:
398 case QQmlJSGrammar::T_AS:
399 case QQmlJSGrammar::T_IMPORT:
400 case QQmlJSGrammar::T_SIGNAL:
401 case QQmlJSGrammar::T_PROPERTY:
402 case QQmlJSGrammar::T_REQUIRED:
403 case QQmlJSGrammar::T_READONLY:
404 case QQmlJSGrammar::T_IDENTIFIER:
405 enter(StateType::ExpressionOrObjectdefinition);
406 break;
407
408 // error recovery
409 case QQmlJSGrammar::T_RBRACKET:
410 case QQmlJSGrammar::T_RPAREN:
411 leave(true);
412 break;
413
414 default:
415 enter(StateType::Expression);
416 continue;
417 }
418 break;
419
420 case StateType::ObjectdefinitionOpen:
421 switch (kind) {
422 case QQmlJSGrammar::T_RBRACE:
423 leave(true);
424 break;
425 case QQmlJSGrammar::T_DEFAULT:
426 case QQmlJSGrammar::T_READONLY:
427 enter(StateType::PropertyModifiers);
428 break;
429 case QQmlJSGrammar::T_PROPERTY:
430 enter(StateType::PropertyStart);
431 break;
432 case QQmlJSGrammar::T_REQUIRED:
433 enter(StateType::RequiredProperty);
434 break;
435 case QQmlJSGrammar::T_COMPONENT:
436 enter(StateType::ComponentStart);
437 break;
438 case QQmlJSGrammar::T_FUNCTION:
439 enter(StateType::FunctionStart);
440 break;
441 case QQmlJSGrammar::T_SIGNAL:
442 enter(StateType::SignalStart);
443 break;
444 case QQmlJSGrammar::T_ENUM:
445 enter(StateType::EnumStart);
446 break;
447 case QQmlJSGrammar::T_ON:
448 case QQmlJSGrammar::T_AS:
449 case QQmlJSGrammar::T_IMPORT:
450 enter(StateType::BindingOrObjectdefinition);
451 break;
452 default:
454 enter(StateType::BindingOrObjectdefinition);
455 break;
456 }
457 break;
458
459 case StateType::PropertyModifiers:
460 switch (kind) {
461 case QQmlJSGrammar::T_PROPERTY:
462 turnInto(StateType::PropertyStart);
463 break;
464 case QQmlJSGrammar::T_DEFAULT:
465 case QQmlJSGrammar::T_READONLY:
466 break;
467 case QQmlJSGrammar::T_REQUIRED:
468 turnInto(StateType::RequiredProperty);
469 break;
470 default:
471 leave(true);
472 break;
473 }
474 break;
475
476 case StateType::PropertyStart:
477 switch (kind) {
478 case QQmlJSGrammar::T_COLON:
479 enter(StateType::BindingAssignment);
480 break; // oops, was a binding
481 case QQmlJSGrammar::T_VAR:
482 case QQmlJSGrammar::T_IDENTIFIER:
483 enter(StateType::PropertyName);
484 break;
485 default:
486 bool kindIsIdentifier = Token::lexKindIsIdentifier(kind);
487 if (kindIsIdentifier && tokenText(currentToken) == u"list") {
488 enter(StateType::PropertyListOpen);
489 } else if (kindIsIdentifier) {
490 enter(StateType::PropertyName);
491 } else {
492 leave(true);
493 continue;
494 }
495 }
496 break;
497
498 case StateType::RequiredProperty:
499 switch (kind) {
500 case QQmlJSGrammar::T_PROPERTY:
501 turnInto(StateType::PropertyStart);
502 break;
503 case QQmlJSGrammar::T_DEFAULT:
504 case QQmlJSGrammar::T_READONLY:
505 turnInto(StateType::PropertyModifiers);
506 break;
507 case QQmlJSGrammar::T_IDENTIFIER:
508 leave(true);
509 break;
510 default:
511 leave(true);
512 continue;
513 }
514 break;
515
516 case StateType::ComponentStart:
517 switch (kind) {
518 case QQmlJSGrammar::T_IDENTIFIER:
519 turnInto(StateType::ComponentName);
520 break;
521 default:
522 leave(true);
523 continue;
524 }
525 break;
526
527 case StateType::ComponentName:
528 switch (kind) {
529 case QQmlJSGrammar::T_COLON:
530 enter(StateType::BindingAssignment);
531 break;
532 default:
533 leave(true);
534 continue;
535 }
536 break;
537
538 case StateType::PropertyName:
539 turnInto(StateType::PropertyMaybeInitializer);
540 break;
541
542 case StateType::PropertyListOpen: {
543 const QStringView tok = tokenText(currentToken);
544 if (tok == u">")
545 turnInto(StateType::PropertyName);
546 break;
547 }
548 case StateType::PropertyMaybeInitializer:
549 switch (kind) {
550 case QQmlJSGrammar::T_COLON:
551 turnInto(StateType::BindingAssignment);
552 break;
553 default:
554 leave(true);
555 continue;
556 }
557 break;
558
559 case StateType::EnumStart:
560 switch (kind) {
561 case QQmlJSGrammar::T_LBRACE:
562 enter(StateType::ObjectliteralOpen);
563 break;
564 }
565 break;
566
567 case StateType::SignalStart:
568 switch (kind) {
569 case QQmlJSGrammar::T_COLON:
570 enter(StateType::BindingAssignment);
571 break; // oops, was a binding
572 default:
573 enter(StateType::SignalMaybeArglist);
574 break;
575 }
576 break;
577
578 case StateType::SignalMaybeArglist:
579 switch (kind) {
580 case QQmlJSGrammar::T_LPAREN:
581 turnInto(StateType::SignalArglistOpen);
582 break;
583 default:
584 leave(true);
585 continue;
586 }
587 break;
588
589 case StateType::SignalArglistOpen:
590 switch (kind) {
591 case QQmlJSGrammar::T_RPAREN:
592 leave(true);
593 break;
594 }
595 break;
596
597 case StateType::FunctionStart:
598 switch (kind) {
599 case QQmlJSGrammar::T_LPAREN:
600 enter(StateType::FunctionArglistOpen);
601 break;
602 }
603 break;
604
605 case StateType::FunctionArglistOpen:
606 switch (kind) {
607 case QQmlJSGrammar::T_COLON:
608 enter(StateType::TypeAnnotation);
609 break;
610 case QQmlJSGrammar::T_RPAREN:
611 turnInto(StateType::FunctionArglistClosed);
612 break;
613 }
614 break;
615
616 case StateType::FunctionArglistClosed:
617 switch (kind) {
618 case QQmlJSGrammar::T_COLON:
619 enter(StateType::TypeAnnotation);
620 break;
621 case QQmlJSGrammar::T_LBRACE:
622 turnInto(StateType::JsblockOpen);
623 break;
624 default:
625 leave(true);
626 continue; // error recovery
627 }
628 break;
629
630 case StateType::LambdaStart:
631 switch (kind) {
632 case QQmlJSGrammar::T_LBRACE:
633 turnInto(StateType::JsblockOpen);
634 break;
635 default:
636 turnInto(StateType::Expression);
637 }
638 break;
639
640 case StateType::TypeAnnotation:
641 switch (kind) {
642 case QQmlJSGrammar::T_IDENTIFIER:
643 case QQmlJSGrammar::T_DOT:
644 break;
645 case QQmlJSGrammar::T_LT:
646 turnInto(StateType::TypeParameter);
647 break;
648 default:
649 leave();
650 continue; // error recovery
651 }
652 break;
653
654 case StateType::TypeParameter:
655 switch (kind) {
656 case QQmlJSGrammar::T_LT:
657 enter(StateType::TypeParameter);
658 break;
659 case QQmlJSGrammar::T_GT:
660 leave();
661 break;
662 }
663 break;
664
665 case StateType::ExpressionOrObjectdefinition:
666 switch (kind) {
667 case QQmlJSGrammar::T_DOT:
668 break; // need to become an objectdefinition_open in cases like "width: Qt.Foo
669 // {"
670 case QQmlJSGrammar::T_LBRACE:
671 turnInto(StateType::ObjectdefinitionOpen);
672 break;
673
674 // propagate 'leave' from expression state
675 case QQmlJSGrammar::T_RBRACKET:
676 case QQmlJSGrammar::T_RPAREN:
677 leave();
678 continue;
679
680 default:
682 break; // need to become an objectdefinition_open in cases like "width:
683 // Qt.Foo
684 enter(StateType::Expression);
685 continue; // really? identifier and more tokens might already be gone
686 }
687 break;
688
689 case StateType::ExpressionOrLabel:
690 switch (kind) {
691 case QQmlJSGrammar::T_COLON:
692 turnInto(StateType::LabelledStatement);
693 break;
694
695 // propagate 'leave' from expression state
696 case QQmlJSGrammar::T_RBRACKET:
697 case QQmlJSGrammar::T_RPAREN:
698 leave();
699 continue;
700
701 default:
702 enter(StateType::Expression);
703 continue;
704 }
705 break;
706
707 case StateType::TernaryOp:
708 if (kind == QQmlJSGrammar::T_COLON) {
709 enter(StateType::TernaryOpAfterColon);
710 enter(StateType::ExpressionContinuation);
711 break;
712 }
713 Q_FALLTHROUGH();
714 case StateType::TernaryOpAfterColon:
715 case StateType::Expression:
716 if (tryInsideExpression(false))
717 break;
718 switch (kind) {
719 case QQmlJSGrammar::T_COMMA:
720 leave(true);
721 break;
722 case QQmlJSGrammar::T_RBRACKET:
723 case QQmlJSGrammar::T_RPAREN:
724 leave();
725 continue;
726 case QQmlJSGrammar::T_RBRACE:
727 leave(true);
728 continue;
729 case QQmlJSGrammar::T_AUTOMATIC_SEMICOLON:
730 case QQmlJSGrammar::T_COMPATIBILITY_SEMICOLON:
731 case QQmlJSGrammar::T_SEMICOLON:
732 leave(true);
733 break;
734 default:
735 if (Token::lexKindIsDelimiter(kind))
736 enter(StateType::ExpressionContinuation);
737 break;
738 }
739 break;
740
741 case StateType::ExpressionContinuation:
742 leave();
743 continue;
744
745 case StateType::ExpressionMaybeContinuation:
746 switch (kind) {
747 case QQmlJSGrammar::T_QUESTION:
748 case QQmlJSGrammar::T_LBRACKET:
749 case QQmlJSGrammar::T_LPAREN:
750 case QQmlJSGrammar::T_LBRACE:
751 leave();
752 continue;
753 default:
754 leave(!Token::lexKindIsDelimiter(kind));
755 continue;
756 }
757 break;
758
759 case StateType::ParenOpen:
760 if (tryInsideExpression(false))
761 break;
762 switch (kind) {
763 case QQmlJSGrammar::T_RPAREN:
764 leave();
765 break;
766 }
767 break;
768
769 case StateType::BracketOpen:
770 if (tryInsideExpression(false))
771 break;
772 switch (kind) {
773 case QQmlJSGrammar::T_COMMA:
774 enter(StateType::BracketElementStart);
775 break;
776 case QQmlJSGrammar::T_RBRACKET:
777 leave();
778 break;
779 }
780 break;
781
782 case StateType::ObjectliteralOpen:
783 if (tryInsideExpression(false))
784 break;
785 switch (kind) {
786 case QQmlJSGrammar::T_COLON:
787 enter(StateType::ObjectliteralAssignment);
788 break;
789 case QQmlJSGrammar::T_RBRACKET:
790 case QQmlJSGrammar::T_RPAREN:
791 leave();
792 continue; // error recovery
793 case QQmlJSGrammar::T_RBRACE:
794 leave(true);
795 break;
796 }
797 break;
798
799 // pretty much like expression, but ends with , or }
800 case StateType::ObjectliteralAssignment:
801 if (tryInsideExpression(false))
802 break;
803 switch (kind) {
804 case QQmlJSGrammar::T_COMMA:
805 leave();
806 break;
807 case QQmlJSGrammar::T_RBRACKET:
808 case QQmlJSGrammar::T_RPAREN:
809 leave();
810 continue; // error recovery
811 case QQmlJSGrammar::T_RBRACE:
812 leave();
813 continue; // so we also leave objectliteral_open
814 default:
815 if (Token::lexKindIsDelimiter(kind))
816 enter(StateType::ExpressionContinuation);
817 break;
818 }
819 break;
820
821 case StateType::BracketElementStart:
823 turnInto(StateType::BracketElementMaybeObjectdefinition);
824 } else {
825 leave();
826 continue;
827 }
828 break;
829
830 case StateType::BracketElementMaybeObjectdefinition:
831 switch (kind) {
832 case QQmlJSGrammar::T_LBRACE:
833 turnInto(StateType::ObjectdefinitionOpen);
834 break;
835 default:
836 leave();
837 continue;
838 }
839 break;
840
841 case StateType::JsblockOpen:
842 case StateType::SubstatementOpen:
843 if (tryStatement())
844 break;
845 switch (kind) {
846 case QQmlJSGrammar::T_RBRACE:
847 leave(true);
848 break;
849 }
850 break;
851
852 case StateType::LabelledStatement:
853 if (tryStatement())
854 break;
855 leave(true); // error recovery
856 break;
857
858 case StateType::Substatement:
859 // prefer substatement_open over block_open
860 if (kind != QQmlJSGrammar::T_LBRACE) {
861 if (tryStatement())
862 break;
863 }
864 switch (kind) {
865 case QQmlJSGrammar::T_LBRACE:
866 turnInto(StateType::SubstatementOpen);
867 break;
868 }
869 break;
870
871 case StateType::IfStatement:
872 switch (kind) {
873 case QQmlJSGrammar::T_LPAREN:
874 enter(StateType::ConditionOpen);
875 break;
876 default:
877 leave(true);
878 break; // error recovery
879 }
880 break;
881
882 case StateType::MaybeElse:
883 switch (kind) {
884 case QQmlJSGrammar::T_ELSE:
885 turnInto(StateType::ElseClause);
886 enter(StateType::Substatement);
887 break;
888 default:
889 leave(true);
890 continue;
891 }
892 break;
893
894 case StateType::MaybeCatchOrFinally:
895 switch (kind) {
896 case QQmlJSGrammar::T_CATCH:
897 turnInto(StateType::CatchStatement);
898 break;
899 case QQmlJSGrammar::T_FINALLY:
900 turnInto(StateType::FinallyStatement);
901 break;
902 default:
903 leave(true);
904 continue;
905 }
906 break;
907
908 case StateType::ElseClause:
909 // ### shouldn't happen
910 dump();
911 Q_ASSERT(false);
912 leave(true);
913 break;
914
915 case StateType::ConditionOpen:
916 if (tryInsideExpression(false))
917 break;
918 switch (kind) {
919 case QQmlJSGrammar::T_RPAREN:
920 turnInto(StateType::Substatement);
921 break;
922 }
923 break;
924
925 case StateType::SwitchStatement:
926 case StateType::CatchStatement:
927 case StateType::StatementWithCondition:
928 switch (kind) {
929 case QQmlJSGrammar::T_LPAREN:
930 enter(StateType::StatementWithConditionParenOpen);
931 break;
932 default:
933 leave(true);
934 }
935 break;
936
937 case StateType::StatementWithConditionParenOpen:
938 if (tryInsideExpression(false))
939 break;
940 switch (kind) {
941 case QQmlJSGrammar::T_RPAREN:
942 turnInto(StateType::Substatement);
943 break;
944 }
945 break;
946
947 case StateType::TryStatement:
948 case StateType::FinallyStatement:
949 switch (kind) {
950 case QQmlJSGrammar::T_LBRACE:
951 enter(StateType::JsblockOpen);
952 break;
953 default:
954 leave(true);
955 break;
956 }
957 break;
958
959 case StateType::DoStatement:
960 switch (kind) {
961 case QQmlJSGrammar::T_WHILE:
962 break;
963 case QQmlJSGrammar::T_LPAREN:
964 enter(StateType::DoStatementWhileParenOpen);
965 break;
966 default:
967 leave(true);
968 continue; // error recovery
969 }
970 break;
971
972 case StateType::DoStatementWhileParenOpen:
973 if (tryInsideExpression(false))
974 break;
975 switch (kind) {
976 case QQmlJSGrammar::T_RPAREN:
977 leave();
978 leave(true);
979 break;
980 }
981 break;
982
983 case StateType::BreakcontinueStatement:
985 leave(true);
986 } else {
987 leave(true);
988 continue; // try again
989 }
990 break;
991
992 case StateType::CaseStart:
993 switch (kind) {
994 case QQmlJSGrammar::T_COLON:
995 turnInto(StateType::CaseCont);
996 break;
997 }
998 break;
999
1000 case StateType::CaseCont:
1001 if (kind != QQmlJSGrammar::T_CASE && kind != QQmlJSGrammar::T_DEFAULT && tryStatement())
1002 break;
1003 switch (kind) {
1004 case QQmlJSGrammar::T_RBRACE:
1005 leave();
1006 continue;
1007 case QQmlJSGrammar::T_DEFAULT:
1008 case QQmlJSGrammar::T_CASE:
1009 leave();
1010 continue;
1011 }
1012 break;
1013
1014 case StateType::MultilineCommentStart:
1015 case StateType::MultilineCommentCont:
1016 if (!Token::lexKindIsComment(kind)) {
1017 leave();
1018 continue;
1019 } else if (tokenIndex == lineTokens.size() - 1
1020 && !currentStatus.lexerState.isMultiline()) {
1021 leave();
1022 } else if (tokenIndex == 0) {
1023 // to allow enter/leave to update the indentDepth
1024 turnInto(StateType::MultilineCommentCont);
1025 }
1026 break;
1027
1028 default:
1029 qWarning() << "Unhandled state" << currentStatus.state().typeStr();
1030 break;
1031 } // end of state switch
1032
1033 ++tokenIndex;
1034 }
1035
1036 StateType topState = currentStatus.state().type;
1037
1038 // if there's no colon on the same line, it's not a label
1039 if (topState == StateType::ExpressionOrLabel)
1040 enterState(StateType::Expression);
1041 // if not followed by an identifier on the same line, it's done
1042 else if (topState == StateType::BreakcontinueStatement)
1043 leaveState(true);
1044
1045 topState = currentStatus.state().type;
1046
1047 // some states might be continued on the next line
1048 if (topState == StateType::Expression || topState == StateType::ExpressionOrObjectdefinition
1049 || topState == StateType::ObjectliteralAssignment
1050 || topState == StateType::TernaryOpAfterColon) {
1051 enterState(StateType::ExpressionMaybeContinuation);
1052 }
1053 // multi-line comment start?
1054 if (topState != StateType::MultilineCommentStart && topState != StateType::MultilineCommentCont
1055 && currentStatus.lexerState.state.tokenKind == QQmlJSGrammar::T_PARTIAL_COMMENT) {
1056 enterState(StateType::MultilineCommentStart);
1057 }
1058 currentStatus.finalIndent = currentIndent;
1059}
1060
1061// adjusts the indentation of the current line based on the status of the previous one, and what
1062// it starts with
1064 int tokenKind)
1065{
1066 State topState = oldStatus.state();
1067 State previousState = oldStatus.state(1);
1068 int indentDepth = oldStatus.finalIndent;
1069
1070 // keep user-adjusted indent in multiline comments
1071 if (topState.type == StateType::MultilineCommentStart
1072 || topState.type == StateType::MultilineCommentCont) {
1073 if (!Token::lexKindIsInvalid(tokenKind))
1074 return -1;
1075 }
1076 // don't touch multi-line strings at all
1077 if (oldStatus.lexerState.state.tokenKind == QQmlJSGrammar::T_PARTIAL_DOUBLE_QUOTE_STRING_LITERAL
1078 || oldStatus.lexerState.state.tokenKind == QQmlJSGrammar::T_PARTIAL_SINGLE_QUOTE_STRING_LITERAL
1079 || oldStatus.lexerState.state.tokenKind == QQmlJSGrammar::T_PARTIAL_TEMPLATE_HEAD
1080 || oldStatus.lexerState.state.tokenKind == QQmlJSGrammar::T_PARTIAL_TEMPLATE_MIDDLE) {
1081 return -1;
1082 }
1083
1084 switch (tokenKind) {
1085 case QQmlJSGrammar::T_LBRACE:
1086 if (topState.type == StateType::Substatement
1087 || topState.type == StateType::BindingAssignment
1088 || topState.type == StateType::CaseCont) {
1089 return topState.savedIndentDepth;
1090 }
1091 break;
1092 case QQmlJSGrammar::T_RBRACE: {
1093 if (topState.type == StateType::JsblockOpen && previousState.type == StateType::CaseCont) {
1094 return previousState.savedIndentDepth;
1095 }
1096 for (int i = 0; oldStatus.state(i).type != StateType::TopmostIntro; ++i) {
1097 const StateType type = oldStatus.state(i).type;
1098 if (type == StateType::ObjectdefinitionOpen || type == StateType::JsblockOpen
1099 || type == StateType::SubstatementOpen || type == StateType::ObjectliteralOpen) {
1100 return oldStatus.state(i).savedIndentDepth;
1101 }
1102 }
1103 break;
1104 }
1105 case QQmlJSGrammar::T_RBRACKET:
1106 for (int i = 0; oldStatus.state(i).type != StateType::TopmostIntro; ++i) {
1107 const StateType type = oldStatus.state(i).type;
1108 if (type == StateType::BracketOpen) {
1109 return oldStatus.state(i).savedIndentDepth;
1110 }
1111 }
1112 break;
1113 case QQmlJSGrammar::T_LBRACKET:
1114 case QQmlJSGrammar::T_LPAREN:
1115 if (topState.type == StateType::ExpressionMaybeContinuation)
1116 return topState.savedIndentDepth;
1117 break;
1118 case QQmlJSGrammar::T_ELSE:
1119 if (topState.type == StateType::MaybeElse) {
1120 return oldStatus.state(1).savedIndentDepth;
1121 } else if (topState.type == StateType::ExpressionMaybeContinuation) {
1122 bool hasElse = false;
1123 for (int i = 1; oldStatus.state(i).type != StateType::TopmostIntro; ++i) {
1124 const StateType type = oldStatus.state(i).type;
1125 if (type == StateType::ElseClause)
1126 hasElse = true;
1127 if (type == StateType::IfStatement) {
1128 if (hasElse) {
1129 hasElse = false;
1130 } else {
1131 return oldStatus.state(i).savedIndentDepth;
1132 }
1133 }
1134 }
1135 }
1136 break;
1137 case QQmlJSGrammar::T_CATCH:
1138 case QQmlJSGrammar::T_FINALLY:
1139 if (topState.type == StateType::MaybeCatchOrFinally)
1140 return oldStatus.state(1).savedIndentDepth;
1141 break;
1142 case QQmlJSGrammar::T_COLON:
1143 if (topState.type == StateType::TernaryOp)
1144 return indentDepth - 2;
1145 break;
1146 case QQmlJSGrammar::T_QUESTION:
1147 if (topState.type == StateType::ExpressionMaybeContinuation)
1148 return topState.savedIndentDepth;
1149 break;
1150
1151 case QQmlJSGrammar::T_DEFAULT:
1152 case QQmlJSGrammar::T_CASE:
1153 for (int i = 0; oldStatus.state(i).type != StateType::TopmostIntro; ++i) {
1154 const StateType type = oldStatus.state(i).type;
1155 if (type == StateType::SwitchStatement || type == StateType::CaseCont) {
1156 return oldStatus.state(i).savedIndentDepth;
1157 } else if (type == StateType::TopmostIntro) {
1158 break;
1159 }
1160 }
1161 break;
1162 default:
1163 if (Token::lexKindIsDelimiter(tokenKind)
1164 && topState.type == StateType::ExpressionMaybeContinuation)
1165 return topState.savedIndentDepth;
1166
1167 break;
1168 }
1169 return indentDepth;
1170}
1171
1172// sets currentIndent to the correct indent for the current line
1174{
1175 Q_ASSERT(currentStatus.size() >= 1);
1176 int firstToken = (lineTokens.isEmpty() ? QQmlJSGrammar::T_NONE : tokenAt(0).lexKind);
1177 int indent = indentForLineStartingWithToken(initialStatus, options, firstToken);
1179 return indent;
1180}
1181
1183{
1184 // should be just currentIndent?
1185 int indent = indentForLineStartingWithToken(currentStatus, options, QQmlJSGrammar::T_NONE);
1186 if (indent < 0)
1187 return currentIndent;
1188 return indent;
1189}
1190
1192{
1193 if (indent >= 0) {
1194 indentOffset = 0;
1195 int i = 0;
1196 while (i < line.size() && line.at(i).isSpace())
1197 ++i;
1198 indentOffset = indent - column(i);
1199 }
1200 currentIndent = initialStatus.finalIndent;
1201 auto lexerState = currentStatus.lexerState;
1202 currentStatus = initialStatus;
1203 currentStatus.lexerState = lexerState;
1204 tokenIndex = 0;
1206}
1207
1208FormatPartialStatus formatCodeLine(QStringView line, const FormatOptions &options,
1209 const FormatTextStatus &initialStatus)
1210{
1211 FormatPartialStatus status(line, options, initialStatus);
1212
1213 status.handleTokens();
1214
1215 return status;
1216}
1217
1218void FormatPartialStatus::defaultOnEnter(StateType newState, int *indentDepth,
1219 int *savedIndentDepth) const
1220{
1221 const State &parentState = currentStatus.state();
1222 const Token &tk = tokenAt(tokenIndex);
1223 const int tokenPosition = column(tk.begin());
1224 const bool firstToken = (tokenIndex == 0);
1225 const bool lastToken = (tokenIndex == lineTokens.size() - 1);
1226
1227 switch (newState) {
1228 case StateType::ObjectdefinitionOpen: {
1229 // special case for things like "gradient: Gradient {"
1230 if (parentState.type == StateType::BindingAssignment)
1231 *savedIndentDepth = currentStatus.state(1).savedIndentDepth;
1232
1233 if (firstToken)
1234 *savedIndentDepth = tokenPosition;
1235
1236 *indentDepth = *savedIndentDepth + options.indentSize;
1237 break;
1238 }
1239
1240 case StateType::BindingOrObjectdefinition:
1241 if (firstToken)
1242 *indentDepth = *savedIndentDepth = tokenPosition;
1243 break;
1244
1245 case StateType::BindingAssignment:
1246 case StateType::ObjectliteralAssignment:
1247 if (lastToken)
1248 *indentDepth = *savedIndentDepth + options.indentSize;
1249 else
1250 *indentDepth = column(tokenAt(tokenIndex + 1).begin());
1251 break;
1252
1253 case StateType::ExpressionOrObjectdefinition:
1254 *indentDepth = tokenPosition;
1255 break;
1256
1257 case StateType::ExpressionOrLabel:
1258 if (*indentDepth == tokenPosition)
1259 *indentDepth += 2 * options.indentSize;
1260 else
1261 *indentDepth = tokenPosition;
1262 break;
1263
1264 case StateType::Expression:
1265 if (*indentDepth == tokenPosition) {
1266 // expression_or_objectdefinition doesn't want the indent
1267 // expression_or_label already has it
1268 if (parentState.type != StateType::ExpressionOrObjectdefinition
1269 && parentState.type != StateType::ExpressionOrLabel
1270 && parentState.type != StateType::BindingAssignment) {
1271 *indentDepth += 2 * options.indentSize;
1272 }
1273 }
1274 // expression_or_objectdefinition and expression_or_label have already consumed the
1275 // first token
1276 else if (parentState.type != StateType::ExpressionOrObjectdefinition
1277 && parentState.type != StateType::ExpressionOrLabel) {
1278 *indentDepth = tokenPosition;
1279 }
1280 break;
1281
1282 case StateType::ExpressionMaybeContinuation:
1283 // set indent depth to indent we'd get if the expression ended here
1284 for (int i = 1; currentStatus.state(i).type != StateType::TopmostIntro; ++i) {
1285 const StateType type = currentStatus.state(i).type;
1286 if (FormatTextStatus::isExpressionEndState(type)
1287 && !FormatTextStatus::isBracelessState(type)) {
1288 *indentDepth = currentStatus.state(i - 1).savedIndentDepth;
1289 break;
1290 }
1291 }
1292 break;
1293
1294 case StateType::BracketOpen:
1295 if (parentState.type == StateType::Expression
1296 && currentStatus.state(1).type == StateType::BindingAssignment) {
1297 *savedIndentDepth = currentStatus.state(2).savedIndentDepth;
1298 *indentDepth = *savedIndentDepth + options.indentSize;
1299 } else if (parentState.type == StateType::ObjectliteralAssignment) {
1300 *savedIndentDepth = parentState.savedIndentDepth;
1301 *indentDepth = *savedIndentDepth + options.indentSize;
1302 } else if (!lastToken) {
1303 *indentDepth = tokenPosition + 1;
1304 } else {
1305 *indentDepth = *savedIndentDepth + options.indentSize;
1306 }
1307 break;
1308
1309 case StateType::LambdaStart:
1310 case StateType::FunctionStart:
1311 // align to the beginning of the line
1312 *savedIndentDepth = *indentDepth = column(tokenAt(0).begin());
1313 break;
1314
1315 case StateType::DoStatementWhileParenOpen:
1316 case StateType::StatementWithConditionParenOpen:
1317 case StateType::SignalArglistOpen:
1318 case StateType::FunctionArglistOpen:
1319 case StateType::ParenOpen:
1320 if (!lastToken)
1321 *indentDepth = tokenPosition + 1;
1322 else
1323 *indentDepth += options.indentSize;
1324 break;
1325
1326 case StateType::TernaryOp:
1327 if (!lastToken)
1328 *indentDepth = tokenPosition + tk.length + 1;
1329 else
1330 *indentDepth += options.indentSize;
1331 break;
1332
1333 case StateType::JsblockOpen:
1334 // closing brace should be aligned to case
1335 if (parentState.type == StateType::CaseCont) {
1336 *savedIndentDepth = parentState.savedIndentDepth;
1337 break;
1338 }
1339 Q_FALLTHROUGH();
1340 case StateType::SubstatementOpen:
1341 // special case for "foo: {" and "property int foo: {"
1342 if (parentState.type == StateType::BindingAssignment)
1343 *savedIndentDepth = currentStatus.state(1).savedIndentDepth;
1344 *indentDepth = *savedIndentDepth + options.indentSize;
1345 break;
1346
1347 case StateType::Substatement:
1348 *indentDepth += options.indentSize;
1349 break;
1350
1351 case StateType::ObjectliteralOpen:
1352 if (parentState.type == StateType::Expression
1353 || parentState.type == StateType::ObjectliteralAssignment) {
1354 // undo the continuation indent of the expression
1355 if (currentStatus.state(1).type == StateType::ExpressionOrLabel)
1356 *indentDepth = currentStatus.state(1).savedIndentDepth;
1357 else
1358 *indentDepth = parentState.savedIndentDepth;
1359 *savedIndentDepth = *indentDepth;
1360 }
1361 *indentDepth += options.indentSize;
1362 break;
1363
1364 case StateType::StatementWithCondition:
1365 case StateType::TryStatement:
1366 case StateType::CatchStatement:
1367 case StateType::FinallyStatement:
1368 case StateType::IfStatement:
1369 case StateType::DoStatement:
1370 case StateType::SwitchStatement:
1371 if (firstToken || parentState.type == StateType::BindingAssignment)
1372 *savedIndentDepth = tokenPosition;
1373 // ### continuation
1374 *indentDepth = *savedIndentDepth; // + 2*options.indentSize;
1375 // special case for 'else if'
1376 if (!firstToken && newState == StateType::IfStatement
1377 && parentState.type == StateType::Substatement
1378 && currentStatus.state(1).type == StateType::ElseClause) {
1379 *indentDepth = currentStatus.state(1).savedIndentDepth;
1380 *savedIndentDepth = *indentDepth;
1381 }
1382 break;
1383
1384 case StateType::MaybeElse:
1385 case StateType::MaybeCatchOrFinally: {
1386 // set indent to where leave(true) would put it
1387 int lastNonEndState = 0;
1388 while (!FormatTextStatus::isExpressionEndState(
1389 currentStatus.state(lastNonEndState + 1).type))
1390 ++lastNonEndState;
1391 *indentDepth = currentStatus.state(lastNonEndState).savedIndentDepth;
1392 break;
1393 }
1394
1395 case StateType::ConditionOpen:
1396 // fixed extra indent when continuing 'if (', but not for 'else if ('
1397 if (tokenPosition <= *indentDepth + options.indentSize)
1398 *indentDepth += 2 * options.indentSize;
1399 else
1400 *indentDepth = tokenPosition + 1;
1401 break;
1402
1403 case StateType::CaseStart:
1404 *savedIndentDepth = tokenPosition;
1405 break;
1406
1407 case StateType::CaseCont:
1408 *indentDepth += options.indentSize;
1409 break;
1410
1411 case StateType::MultilineCommentStart:
1412 *indentDepth = tokenPosition + 2;
1413 break;
1414
1415 case StateType::MultilineCommentCont:
1416 *indentDepth = tokenPosition;
1417 break;
1418 default:
1419 break;
1420 }
1421}
1422
1423} // namespace Dom
1424} // namespace QQmlJS
1425QT_END_NAMESPACE
const Token & tokenAt(int idx) const
QStringView tokenText(const Token &token) const
void enterState(FormatTextStatus::StateType newState)
bool tryInsideExpression(bool alsoExpression)
void defaultOnEnter(FormatTextStatus::StateType newState, int *indentDepth, int *savedIndentDepth) const
void turnIntoState(FormatTextStatus::StateType newState)
State state(int belowTop=0) const
static bool lexKindIsIdentifier(int kind)
static bool lexKindIsComment(int kind)
static bool lexKindIsInvalid(int kind)
FormatPartialStatus formatCodeLine(QStringView line, const FormatOptions &options, const FormatTextStatus &initialStatus)
int indentForLineStartingWithToken(const FormatTextStatus &oldStatus, const FormatOptions &, int tokenKind)
FormatTextStatus::State State
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)