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_PRAGMA:
266 case QQmlJSGrammar::T_IMPORT:
267 enter(StateType::TopQml);
268 continue;
269 case QQmlJSGrammar::T_LBRACE:
270 enter(StateType::TopJs);
271 enter(StateType::Expression);
272 continue; // if a file starts with {, it's likely json
273 default:
274 enter(StateType::TopJs);
275 continue;
276 }
277 break;
278
279 case StateType::TopQml:
280 switch (kind) {
281 case QQmlJSGrammar::T_PRAGMA:
282 enter(StateType::PragmaStart);
283 break;
284 case QQmlJSGrammar::T_IMPORT:
285 enter(StateType::ImportStart);
286 break;
287 case QQmlJSGrammar::T_IDENTIFIER:
288 enter(StateType::BindingOrObjectdefinition);
289 break;
290 default:
292 enter(StateType::BindingOrObjectdefinition);
293 break;
294 }
295 break;
296
297 case StateType::TopJs:
299 break;
300
301 case StateType::ObjectdefinitionOrJs:
302 switch (kind) {
303 case QQmlJSGrammar::T_DOT:
304 break;
305 case QQmlJSGrammar::T_LBRACE:
306 turnInto(StateType::BindingOrObjectdefinition);
307 continue;
308 default:
309 if (!Token::lexKindIsIdentifier(kind) || !line.at(currentToken.begin()).isUpper()) {
310 turnInto(StateType::TopJs);
311 continue;
312 }
313 }
314 break;
315
316 case StateType::PragmaStart:
317 switch (kind) {
318 case QQmlJSGrammar::T_IDENTIFIER:
319 turnInto(StateType::PragmaMaybeValue);
320 break;
321 default:
322 leave();
323 continue;
324 }
325 break;
326 case StateType::PragmaMaybeValue:
327 switch (kind) {
328 case QQmlJSGrammar::T_COLON:
329 turnInto(StateType::PragmaValue);
330 break;
331 default:
332 leave();
333 continue;
334 }
335 break;
336 case StateType::PragmaValue:
337 switch (kind) {
338 case QQmlJSGrammar::T_IDENTIFIER:
339 case QQmlJSGrammar::T_STRING_LITERAL:
340 turnInto(StateType::PragmaMaybeMoreValues);
341 break;
342 default:
343 leave();
344 continue;
345 }
346 break;
347 case StateType::PragmaMaybeMoreValues:
348 switch (kind) {
349 case QQmlJSGrammar::T_COMMA:
350 turnInto(StateType::PragmaValue);
351 break;
352 default:
353 leave();
354 continue;
355 }
356 break;
357
358 case StateType::ImportStart:
359 enter(StateType::ImportMaybeDotOrVersionOrAs);
360 break;
361
362 case StateType::ImportMaybeDotOrVersionOrAs:
363 switch (kind) {
364 case QQmlJSGrammar::T_DOT:
365 turnInto(StateType::ImportDot);
366 break;
367 case QQmlJSGrammar::T_AS:
368 turnInto(StateType::ImportAs);
369 break;
370 case QQmlJSGrammar::T_NUMERIC_LITERAL:
371 case QQmlJSGrammar::T_VERSION_NUMBER:
372 turnInto(StateType::ImportMaybeAs);
373 break;
374 default:
375 leave();
376 leave();
377 continue;
378 }
379 break;
380
381 case StateType::ImportMaybeAs:
382 switch (kind) {
383 case QQmlJSGrammar::T_AS:
384 turnInto(StateType::ImportAs);
385 break;
386 default:
387 leave();
388 leave();
389 continue;
390 }
391 break;
392
393 case StateType::ImportDot:
395 turnInto(StateType::ImportMaybeDotOrVersionOrAs);
396 } else {
397 leave();
398 leave();
399 continue;
400 }
401 break;
402
403 case StateType::ImportAs:
405 leave();
406 leave();
407 }
408 break;
409
410 case StateType::BindingOrObjectdefinition:
411 switch (kind) {
412 case QQmlJSGrammar::T_COLON:
413 enter(StateType::BindingAssignment);
414 break;
415 case QQmlJSGrammar::T_LBRACE:
416 enter(StateType::ObjectdefinitionOpen);
417 break;
418 }
419 break;
420
421 case StateType::BindingAssignment:
422 switch (kind) {
423 case QQmlJSGrammar::T_AUTOMATIC_SEMICOLON:
424 case QQmlJSGrammar::T_COMPATIBILITY_SEMICOLON:
425 case QQmlJSGrammar::T_SEMICOLON:
426 leave(true);
427 break;
428 case QQmlJSGrammar::T_IF:
429 enter(StateType::IfStatement);
430 break;
431 case QQmlJSGrammar::T_WITH:
432 enter(StateType::StatementWithCondition);
433 break;
434 case QQmlJSGrammar::T_TRY:
435 enter(StateType::TryStatement);
436 break;
437 case QQmlJSGrammar::T_SWITCH:
438 enter(StateType::SwitchStatement);
439 break;
440 case QQmlJSGrammar::T_LBRACE:
441 enter(StateType::JsblockOpen);
442 break;
443 case QQmlJSGrammar::T_ON:
444 case QQmlJSGrammar::T_AS:
445 case QQmlJSGrammar::T_IMPORT:
446 case QQmlJSGrammar::T_SIGNAL:
447 case QQmlJSGrammar::T_PROPERTY:
448 case QQmlJSGrammar::T_REQUIRED:
449 case QQmlJSGrammar::T_READONLY:
450 case QQmlJSGrammar::T_IDENTIFIER:
451 enter(StateType::ExpressionOrObjectdefinition);
452 break;
453
454 // error recovery
455 case QQmlJSGrammar::T_RBRACKET:
456 case QQmlJSGrammar::T_RPAREN:
457 leave(true);
458 break;
459
460 default:
461 enter(StateType::Expression);
462 continue;
463 }
464 break;
465
466 case StateType::ObjectdefinitionOpen:
467 switch (kind) {
468 case QQmlJSGrammar::T_RBRACE:
469 leave(true);
470 break;
471 case QQmlJSGrammar::T_DEFAULT:
472 case QQmlJSGrammar::T_READONLY:
473 enter(StateType::PropertyModifiers);
474 break;
475 case QQmlJSGrammar::T_PROPERTY:
476 enter(StateType::PropertyStart);
477 break;
478 case QQmlJSGrammar::T_REQUIRED:
479 enter(StateType::RequiredProperty);
480 break;
481 case QQmlJSGrammar::T_COMPONENT:
482 enter(StateType::ComponentStart);
483 break;
484 case QQmlJSGrammar::T_FUNCTION:
485 enter(StateType::FunctionStart);
486 break;
487 case QQmlJSGrammar::T_SIGNAL:
488 enter(StateType::SignalStart);
489 break;
490 case QQmlJSGrammar::T_ENUM:
491 enter(StateType::EnumStart);
492 break;
493 case QQmlJSGrammar::T_ON:
494 case QQmlJSGrammar::T_AS:
495 case QQmlJSGrammar::T_IMPORT:
496 enter(StateType::BindingOrObjectdefinition);
497 break;
498 default:
500 enter(StateType::BindingOrObjectdefinition);
501 break;
502 }
503 break;
504
505 case StateType::PropertyModifiers:
506 switch (kind) {
507 case QQmlJSGrammar::T_PROPERTY:
508 turnInto(StateType::PropertyStart);
509 break;
510 case QQmlJSGrammar::T_DEFAULT:
511 case QQmlJSGrammar::T_READONLY:
512 break;
513 case QQmlJSGrammar::T_REQUIRED:
514 turnInto(StateType::RequiredProperty);
515 break;
516 default:
517 leave(true);
518 break;
519 }
520 break;
521
522 case StateType::PropertyStart:
523 switch (kind) {
524 case QQmlJSGrammar::T_COLON:
525 enter(StateType::BindingAssignment);
526 break; // oops, was a binding
527 case QQmlJSGrammar::T_VAR:
528 enter(StateType::PropertyName);
529 break;
530 default:
531 bool kindIsIdentifier = Token::lexKindIsIdentifier(kind);
532 if (kindIsIdentifier && tokenText(currentToken) == u"list") {
533 enter(StateType::PropertyListOpen);
534 } else if (kindIsIdentifier) {
535 enter(StateType::PropertyName);
536 } else {
537 leave(true);
538 continue;
539 }
540 }
541 break;
542
543 case StateType::RequiredProperty:
544 switch (kind) {
545 case QQmlJSGrammar::T_PROPERTY:
546 turnInto(StateType::PropertyStart);
547 break;
548 case QQmlJSGrammar::T_DEFAULT:
549 case QQmlJSGrammar::T_READONLY:
550 turnInto(StateType::PropertyModifiers);
551 break;
552 case QQmlJSGrammar::T_IDENTIFIER:
553 leave(true);
554 break;
555 default:
556 leave(true);
557 continue;
558 }
559 break;
560
561 case StateType::ComponentStart:
562 switch (kind) {
563 case QQmlJSGrammar::T_IDENTIFIER:
564 turnInto(StateType::ComponentName);
565 break;
566 default:
567 leave(true);
568 continue;
569 }
570 break;
571
572 case StateType::ComponentName:
573 switch (kind) {
574 case QQmlJSGrammar::T_COLON:
575 enter(StateType::BindingAssignment);
576 break;
577 default:
578 leave(true);
579 continue;
580 }
581 break;
582
583 case StateType::PropertyName:
584 turnInto(StateType::PropertyMaybeInitializer);
585 break;
586
587 case StateType::PropertyListOpen: {
588 const QStringView tok = tokenText(currentToken);
589 if (tok == u">")
590 turnInto(StateType::PropertyName);
591 break;
592 }
593 case StateType::PropertyMaybeInitializer:
594 switch (kind) {
595 case QQmlJSGrammar::T_COLON:
596 turnInto(StateType::BindingAssignment);
597 break;
598 default:
599 leave(true);
600 continue;
601 }
602 break;
603
604 case StateType::EnumStart:
605 switch (kind) {
606 case QQmlJSGrammar::T_LBRACE:
607 enter(StateType::ObjectliteralOpen);
608 break;
609 }
610 break;
611
612 case StateType::SignalStart:
613 switch (kind) {
614 case QQmlJSGrammar::T_COLON:
615 enter(StateType::BindingAssignment);
616 break; // oops, was a binding
617 default:
618 enter(StateType::SignalMaybeArglist);
619 break;
620 }
621 break;
622
623 case StateType::SignalMaybeArglist:
624 switch (kind) {
625 case QQmlJSGrammar::T_LPAREN:
626 turnInto(StateType::SignalArglistOpen);
627 break;
628 default:
629 leave(true);
630 continue;
631 }
632 break;
633
634 case StateType::SignalArglistOpen:
635 switch (kind) {
636 case QQmlJSGrammar::T_RPAREN:
637 leave(true);
638 break;
639 }
640 break;
641
642 case StateType::FunctionStart:
643 switch (kind) {
644 case QQmlJSGrammar::T_LPAREN:
645 enter(StateType::FunctionArglistOpen);
646 break;
647 }
648 break;
649
650 case StateType::FunctionArglistOpen:
651 switch (kind) {
652 case QQmlJSGrammar::T_COLON:
653 enter(StateType::TypeAnnotation);
654 break;
655 case QQmlJSGrammar::T_RPAREN:
656 turnInto(StateType::FunctionArglistClosed);
657 break;
658 }
659 break;
660
661 case StateType::FunctionArglistClosed:
662 switch (kind) {
663 case QQmlJSGrammar::T_COLON:
664 enter(StateType::TypeAnnotation);
665 break;
666 case QQmlJSGrammar::T_LBRACE:
667 turnInto(StateType::JsblockOpen);
668 break;
669 default:
670 leave(true);
671 continue; // error recovery
672 }
673 break;
674
675 case StateType::LambdaStart:
676 switch (kind) {
677 case QQmlJSGrammar::T_LBRACE:
678 turnInto(StateType::JsblockOpen);
679 break;
680 default:
681 turnInto(StateType::Expression);
682 }
683 break;
684
685 case StateType::TypeAnnotation:
686 switch (kind) {
687 case QQmlJSGrammar::T_IDENTIFIER:
688 case QQmlJSGrammar::T_VOID:
689 case QQmlJSGrammar::T_VAR:
690 case QQmlJSGrammar::T_DOT:
691 break;
692 case QQmlJSGrammar::T_LT:
693 turnInto(StateType::TypeParameter);
694 break;
695 default:
696 leave();
697 continue; // error recovery
698 }
699 break;
700
701 case StateType::TypeParameter:
702 switch (kind) {
703 case QQmlJSGrammar::T_LT:
704 enter(StateType::TypeParameter);
705 break;
706 case QQmlJSGrammar::T_GT:
707 leave();
708 break;
709 }
710 break;
711
712 case StateType::ExpressionOrObjectdefinition:
713 switch (kind) {
714 case QQmlJSGrammar::T_DOT:
715 break; // need to become an objectdefinition_open in cases like "width: Qt.Foo
716 // {"
717 case QQmlJSGrammar::T_LBRACE:
718 turnInto(StateType::ObjectdefinitionOpen);
719 break;
720
721 // propagate 'leave' from expression state
722 case QQmlJSGrammar::T_RBRACKET:
723 case QQmlJSGrammar::T_RPAREN:
724 leave();
725 continue;
726
727 default:
729 break; // need to become an objectdefinition_open in cases like "width:
730 // Qt.Foo
731 enter(StateType::Expression);
732 continue; // really? identifier and more tokens might already be gone
733 }
734 break;
735
736 case StateType::ExpressionOrLabel:
737 switch (kind) {
738 case QQmlJSGrammar::T_COLON:
739 turnInto(StateType::LabelledStatement);
740 break;
741
742 // propagate 'leave' from expression state
743 case QQmlJSGrammar::T_RBRACKET:
744 case QQmlJSGrammar::T_RPAREN:
745 leave();
746 continue;
747
748 default:
749 enter(StateType::Expression);
750 continue;
751 }
752 break;
753
754 case StateType::TernaryOp:
755 if (kind == QQmlJSGrammar::T_COLON) {
756 enter(StateType::TernaryOpAfterColon);
757 enter(StateType::ExpressionContinuation);
758 break;
759 }
760 Q_FALLTHROUGH();
761 case StateType::TernaryOpAfterColon:
762 case StateType::Expression:
763 if (tryInsideExpression(false))
764 break;
765 switch (kind) {
766 case QQmlJSGrammar::T_COMMA:
767 leave(true);
768 break;
769 case QQmlJSGrammar::T_RBRACKET:
770 case QQmlJSGrammar::T_RPAREN:
771 leave();
772 continue;
773 case QQmlJSGrammar::T_RBRACE:
774 leave(true);
775 continue;
776 case QQmlJSGrammar::T_AUTOMATIC_SEMICOLON:
777 case QQmlJSGrammar::T_COMPATIBILITY_SEMICOLON:
778 case QQmlJSGrammar::T_SEMICOLON:
779 leave(true);
780 break;
781 default:
782 if (Token::lexKindIsDelimiter(kind))
783 enter(StateType::ExpressionContinuation);
784 break;
785 }
786 break;
787
788 case StateType::ExpressionContinuation:
789 leave();
790 continue;
791
792 case StateType::ExpressionMaybeContinuation:
793 switch (kind) {
794 case QQmlJSGrammar::T_QUESTION:
795 case QQmlJSGrammar::T_LBRACKET:
796 case QQmlJSGrammar::T_LPAREN:
797 case QQmlJSGrammar::T_LBRACE:
798 leave();
799 continue;
800 default:
801 leave(!Token::lexKindIsDelimiter(kind));
802 continue;
803 }
804 break;
805
806 case StateType::ParenOpen:
807 if (tryInsideExpression(false))
808 break;
809 switch (kind) {
810 case QQmlJSGrammar::T_RPAREN:
811 leave();
812 break;
813 }
814 break;
815
816 case StateType::BracketOpen:
817 if (tryInsideExpression(false))
818 break;
819 switch (kind) {
820 case QQmlJSGrammar::T_COMMA:
821 enter(StateType::BracketElementStart);
822 break;
823 case QQmlJSGrammar::T_RBRACKET:
824 leave();
825 break;
826 }
827 break;
828
829 case StateType::ObjectliteralOpen:
830 if (tryInsideExpression(false))
831 break;
832 switch (kind) {
833 case QQmlJSGrammar::T_COLON:
834 enter(StateType::ObjectliteralAssignment);
835 break;
836 case QQmlJSGrammar::T_RBRACKET:
837 case QQmlJSGrammar::T_RPAREN:
838 leave();
839 continue; // error recovery
840 case QQmlJSGrammar::T_RBRACE:
841 leave(true);
842 break;
843 }
844 break;
845
846 // pretty much like expression, but ends with , or }
847 case StateType::ObjectliteralAssignment:
848 if (tryInsideExpression(false))
849 break;
850 switch (kind) {
851 case QQmlJSGrammar::T_COMMA:
852 leave();
853 break;
854 case QQmlJSGrammar::T_RBRACKET:
855 case QQmlJSGrammar::T_RPAREN:
856 leave();
857 continue; // error recovery
858 case QQmlJSGrammar::T_RBRACE:
859 leave();
860 continue; // so we also leave objectliteral_open
861 default:
862 if (Token::lexKindIsDelimiter(kind))
863 enter(StateType::ExpressionContinuation);
864 break;
865 }
866 break;
867
868 case StateType::BracketElementStart:
870 turnInto(StateType::BracketElementMaybeObjectdefinition);
871 } else {
872 leave();
873 continue;
874 }
875 break;
876
877 case StateType::BracketElementMaybeObjectdefinition:
878 switch (kind) {
879 case QQmlJSGrammar::T_LBRACE:
880 turnInto(StateType::ObjectdefinitionOpen);
881 break;
882 default:
883 leave();
884 continue;
885 }
886 break;
887
888 case StateType::JsblockOpen:
889 case StateType::SubstatementOpen:
890 if (tryStatement())
891 break;
892 switch (kind) {
893 case QQmlJSGrammar::T_RBRACE:
894 leave(true);
895 break;
896 }
897 break;
898
899 case StateType::LabelledStatement:
900 if (tryStatement())
901 break;
902 leave(true); // error recovery
903 break;
904
905 case StateType::Substatement:
906 // prefer substatement_open over block_open
907 if (kind != QQmlJSGrammar::T_LBRACE) {
908 if (tryStatement())
909 break;
910 }
911 switch (kind) {
912 case QQmlJSGrammar::T_LBRACE:
913 turnInto(StateType::SubstatementOpen);
914 break;
915 }
916 break;
917
918 case StateType::IfStatement:
919 switch (kind) {
920 case QQmlJSGrammar::T_LPAREN:
921 enter(StateType::ConditionOpen);
922 break;
923 default:
924 leave(true);
925 break; // error recovery
926 }
927 break;
928
929 case StateType::MaybeElse:
930 switch (kind) {
931 case QQmlJSGrammar::T_ELSE:
932 turnInto(StateType::ElseClause);
933 enter(StateType::Substatement);
934 break;
935 default:
936 leave(true);
937 continue;
938 }
939 break;
940
941 case StateType::MaybeCatchOrFinally:
942 switch (kind) {
943 case QQmlJSGrammar::T_CATCH:
944 turnInto(StateType::CatchStatement);
945 break;
946 case QQmlJSGrammar::T_FINALLY:
947 turnInto(StateType::FinallyStatement);
948 break;
949 default:
950 leave(true);
951 continue;
952 }
953 break;
954
955 case StateType::ElseClause:
956 // ### shouldn't happen
957 dump();
958 Q_ASSERT(false);
959 leave(true);
960 break;
961
962 case StateType::ConditionOpen:
963 if (tryInsideExpression(false))
964 break;
965 switch (kind) {
966 case QQmlJSGrammar::T_RPAREN:
967 turnInto(StateType::Substatement);
968 break;
969 }
970 break;
971
972 case StateType::SwitchStatement:
973 case StateType::CatchStatement:
974 case StateType::StatementWithCondition:
975 switch (kind) {
976 case QQmlJSGrammar::T_LPAREN:
977 enter(StateType::StatementWithConditionParenOpen);
978 break;
979 default:
980 leave(true);
981 }
982 break;
983
984 case StateType::StatementWithConditionParenOpen:
985 if (tryInsideExpression(false))
986 break;
987 switch (kind) {
988 case QQmlJSGrammar::T_RPAREN:
989 turnInto(StateType::Substatement);
990 break;
991 }
992 break;
993
994 case StateType::TryStatement:
995 case StateType::FinallyStatement:
996 switch (kind) {
997 case QQmlJSGrammar::T_LBRACE:
998 enter(StateType::JsblockOpen);
999 break;
1000 default:
1001 leave(true);
1002 break;
1003 }
1004 break;
1005
1006 case StateType::DoStatement:
1007 switch (kind) {
1008 case QQmlJSGrammar::T_WHILE:
1009 break;
1010 case QQmlJSGrammar::T_LPAREN:
1011 enter(StateType::DoStatementWhileParenOpen);
1012 break;
1013 default:
1014 leave(true);
1015 continue; // error recovery
1016 }
1017 break;
1018
1019 case StateType::DoStatementWhileParenOpen:
1020 if (tryInsideExpression(false))
1021 break;
1022 switch (kind) {
1023 case QQmlJSGrammar::T_RPAREN:
1024 leave();
1025 leave(true);
1026 break;
1027 }
1028 break;
1029
1030 case StateType::BreakcontinueStatement:
1032 leave(true);
1033 } else {
1034 leave(true);
1035 continue; // try again
1036 }
1037 break;
1038
1039 case StateType::CaseStart:
1040 switch (kind) {
1041 case QQmlJSGrammar::T_COLON:
1042 turnInto(StateType::CaseCont);
1043 break;
1044 }
1045 break;
1046
1047 case StateType::CaseCont:
1048 if (kind != QQmlJSGrammar::T_CASE && kind != QQmlJSGrammar::T_DEFAULT && tryStatement())
1049 break;
1050 switch (kind) {
1051 case QQmlJSGrammar::T_RBRACE:
1052 leave();
1053 continue;
1054 case QQmlJSGrammar::T_DEFAULT:
1055 case QQmlJSGrammar::T_CASE:
1056 leave();
1057 continue;
1058 }
1059 break;
1060
1061 case StateType::MultilineCommentStart:
1062 case StateType::MultilineCommentCont:
1063 if (!Token::lexKindIsComment(kind)) {
1064 leave();
1065 continue;
1066 } else if (tokenIndex == lineTokens.size() - 1
1067 && !currentStatus.lexerState.isMultiline()) {
1068 leave();
1069 } else if (tokenIndex == 0) {
1070 // to allow enter/leave to update the indentDepth
1071 turnInto(StateType::MultilineCommentCont);
1072 }
1073 break;
1074
1075 default:
1076 qWarning() << "Unhandled state" << currentStatus.state().typeStr();
1077 break;
1078 } // end of state switch
1079
1080 ++tokenIndex;
1081 }
1082
1083 StateType topState = currentStatus.state().type;
1084
1085 // if there's no colon on the same line, it's not a label
1086 if (topState == StateType::ExpressionOrLabel)
1087 enterState(StateType::Expression);
1088 // if not followed by an identifier on the same line, it's done
1089 else if (topState == StateType::BreakcontinueStatement)
1090 leaveState(true);
1091
1092 topState = currentStatus.state().type;
1093
1094 // some states might be continued on the next line
1095 if (topState == StateType::Expression || topState == StateType::ExpressionOrObjectdefinition
1096 || topState == StateType::ObjectliteralAssignment
1097 || topState == StateType::TernaryOpAfterColon) {
1098 enterState(StateType::ExpressionMaybeContinuation);
1099 }
1100 // multi-line comment start?
1101 if (topState != StateType::MultilineCommentStart && topState != StateType::MultilineCommentCont
1102 && currentStatus.lexerState.state.tokenKind == QQmlJSGrammar::T_PARTIAL_COMMENT) {
1103 enterState(StateType::MultilineCommentStart);
1104 }
1105 currentStatus.finalIndent = currentIndent;
1106}
1107
1108// adjusts the indentation of the current line based on the status of the previous one, and what
1109// it starts with
1111 int tokenKind)
1112{
1113 State topState = oldStatus.state();
1114 State previousState = oldStatus.state(1);
1115 int indentDepth = oldStatus.finalIndent;
1116
1117 // keep user-adjusted indent in multiline comments
1118 if (topState.type == StateType::MultilineCommentStart
1119 || topState.type == StateType::MultilineCommentCont) {
1120 if (!Token::lexKindIsInvalid(tokenKind))
1121 return -1;
1122 }
1123 // don't touch multi-line strings at all
1124 if (oldStatus.lexerState.state.tokenKind == QQmlJSGrammar::T_PARTIAL_DOUBLE_QUOTE_STRING_LITERAL
1125 || oldStatus.lexerState.state.tokenKind == QQmlJSGrammar::T_PARTIAL_SINGLE_QUOTE_STRING_LITERAL
1126 || oldStatus.lexerState.state.tokenKind == QQmlJSGrammar::T_PARTIAL_TEMPLATE_HEAD
1127 || oldStatus.lexerState.state.tokenKind == QQmlJSGrammar::T_PARTIAL_TEMPLATE_MIDDLE) {
1128 return -1;
1129 }
1130
1131 switch (tokenKind) {
1132 case QQmlJSGrammar::T_LBRACE:
1133 if (topState.type == StateType::Substatement
1134 || topState.type == StateType::BindingAssignment
1135 || topState.type == StateType::CaseCont) {
1136 return topState.savedIndentDepth;
1137 }
1138 break;
1139 case QQmlJSGrammar::T_RBRACE: {
1140 if (topState.type == StateType::JsblockOpen && previousState.type == StateType::CaseCont) {
1141 return previousState.savedIndentDepth;
1142 }
1143 for (int i = 0; oldStatus.state(i).type != StateType::TopmostIntro; ++i) {
1144 const StateType type = oldStatus.state(i).type;
1145 if (type == StateType::ObjectdefinitionOpen || type == StateType::JsblockOpen
1146 || type == StateType::SubstatementOpen || type == StateType::ObjectliteralOpen) {
1147 return oldStatus.state(i).savedIndentDepth;
1148 }
1149 }
1150 break;
1151 }
1152 case QQmlJSGrammar::T_RBRACKET:
1153 for (int i = 0; oldStatus.state(i).type != StateType::TopmostIntro; ++i) {
1154 const StateType type = oldStatus.state(i).type;
1155 if (type == StateType::BracketOpen) {
1156 return oldStatus.state(i).savedIndentDepth;
1157 }
1158 }
1159 break;
1160 case QQmlJSGrammar::T_LBRACKET:
1161 case QQmlJSGrammar::T_LPAREN:
1162 if (topState.type == StateType::ExpressionMaybeContinuation)
1163 return topState.savedIndentDepth;
1164 break;
1165 case QQmlJSGrammar::T_ELSE:
1166 if (topState.type == StateType::MaybeElse) {
1167 return oldStatus.state(1).savedIndentDepth;
1168 } else if (topState.type == StateType::ExpressionMaybeContinuation) {
1169 bool hasElse = false;
1170 for (int i = 1; oldStatus.state(i).type != StateType::TopmostIntro; ++i) {
1171 const StateType type = oldStatus.state(i).type;
1172 if (type == StateType::ElseClause)
1173 hasElse = true;
1174 if (type == StateType::IfStatement) {
1175 if (hasElse) {
1176 hasElse = false;
1177 } else {
1178 return oldStatus.state(i).savedIndentDepth;
1179 }
1180 }
1181 }
1182 }
1183 break;
1184 case QQmlJSGrammar::T_CATCH:
1185 case QQmlJSGrammar::T_FINALLY:
1186 if (topState.type == StateType::MaybeCatchOrFinally)
1187 return oldStatus.state(1).savedIndentDepth;
1188 break;
1189 case QQmlJSGrammar::T_COLON:
1190 if (topState.type == StateType::TernaryOp)
1191 return indentDepth - 2;
1192 break;
1193 case QQmlJSGrammar::T_QUESTION:
1194 if (topState.type == StateType::ExpressionMaybeContinuation)
1195 return topState.savedIndentDepth;
1196 break;
1197
1198 case QQmlJSGrammar::T_DEFAULT:
1199 case QQmlJSGrammar::T_CASE:
1200 for (int i = 0; oldStatus.state(i).type != StateType::TopmostIntro; ++i) {
1201 const StateType type = oldStatus.state(i).type;
1202 if (type == StateType::SwitchStatement || type == StateType::CaseCont) {
1203 return oldStatus.state(i).savedIndentDepth;
1204 } else if (type == StateType::TopmostIntro) {
1205 break;
1206 }
1207 }
1208 break;
1209 default:
1210 if (Token::lexKindIsDelimiter(tokenKind)
1211 && topState.type == StateType::ExpressionMaybeContinuation)
1212 return topState.savedIndentDepth;
1213
1214 break;
1215 }
1216 return indentDepth;
1217}
1218
1219// sets currentIndent to the correct indent for the current line
1221{
1222 Q_ASSERT(currentStatus.size() >= 1);
1223 int firstToken = (lineTokens.isEmpty() ? QQmlJSGrammar::T_NONE : tokenAt(0).lexKind);
1224 int indent = indentForLineStartingWithToken(initialStatus, options, firstToken);
1226 return indent;
1227}
1228
1230{
1231 // should be just currentIndent?
1232 int indent = indentForLineStartingWithToken(currentStatus, options, QQmlJSGrammar::T_NONE);
1233 if (indent < 0)
1234 return currentIndent;
1235 return indent;
1236}
1237
1239{
1240 if (indent >= 0) {
1241 indentOffset = 0;
1242 int i = 0;
1243 while (i < line.size() && line.at(i).isSpace())
1244 ++i;
1245 indentOffset = indent - column(i);
1246 }
1247 currentIndent = initialStatus.finalIndent;
1248 auto lexerState = currentStatus.lexerState;
1249 currentStatus = initialStatus;
1250 currentStatus.lexerState = lexerState;
1251 tokenIndex = 0;
1253}
1254
1255FormatPartialStatus formatCodeLine(QStringView line, const FormatOptions &options,
1256 const FormatTextStatus &initialStatus)
1257{
1258 FormatPartialStatus status(line, options, initialStatus);
1259
1260 status.handleTokens();
1261
1262 return status;
1263}
1264
1265void FormatPartialStatus::defaultOnEnter(StateType newState, int *indentDepth,
1266 int *savedIndentDepth) const
1267{
1268 const State &parentState = currentStatus.state();
1269 const Token &tk = tokenAt(tokenIndex);
1270 const int tokenPosition = column(tk.begin());
1271 const bool firstToken = (tokenIndex == 0);
1272 const bool lastToken = (tokenIndex == lineTokens.size() - 1);
1273
1274 switch (newState) {
1275 case StateType::ObjectdefinitionOpen: {
1276 // special case for things like "gradient: Gradient {"
1277 if (parentState.type == StateType::BindingAssignment)
1278 *savedIndentDepth = currentStatus.state(1).savedIndentDepth;
1279
1280 if (firstToken)
1281 *savedIndentDepth = tokenPosition;
1282
1283 *indentDepth = *savedIndentDepth + options.indentSize;
1284 break;
1285 }
1286
1287 case StateType::BindingOrObjectdefinition:
1288 if (firstToken)
1289 *indentDepth = *savedIndentDepth = tokenPosition;
1290 break;
1291
1292 case StateType::BindingAssignment:
1293 case StateType::ObjectliteralAssignment:
1294 if (lastToken)
1295 *indentDepth = *savedIndentDepth + options.indentSize;
1296 else
1297 *indentDepth = column(tokenAt(tokenIndex + 1).begin());
1298 break;
1299
1300 case StateType::ExpressionOrObjectdefinition:
1301 *indentDepth = tokenPosition;
1302 break;
1303
1304 case StateType::ExpressionOrLabel:
1305 if (*indentDepth == tokenPosition)
1306 *indentDepth += 2 * options.indentSize;
1307 else
1308 *indentDepth = tokenPosition;
1309 break;
1310
1311 case StateType::Expression:
1312 if (*indentDepth == tokenPosition) {
1313 // expression_or_objectdefinition doesn't want the indent
1314 // expression_or_label already has it
1315 if (parentState.type != StateType::ExpressionOrObjectdefinition
1316 && parentState.type != StateType::ExpressionOrLabel
1317 && parentState.type != StateType::BindingAssignment) {
1318 *indentDepth += 2 * options.indentSize;
1319 }
1320 }
1321 // expression_or_objectdefinition and expression_or_label have already consumed the
1322 // first token
1323 else if (parentState.type != StateType::ExpressionOrObjectdefinition
1324 && parentState.type != StateType::ExpressionOrLabel) {
1325 *indentDepth = tokenPosition;
1326 }
1327 break;
1328
1329 case StateType::ExpressionMaybeContinuation:
1330 // set indent depth to indent we'd get if the expression ended here
1331 for (int i = 1; currentStatus.state(i).type != StateType::TopmostIntro; ++i) {
1332 const StateType type = currentStatus.state(i).type;
1333 if (FormatTextStatus::isExpressionEndState(type)
1334 && !FormatTextStatus::isBracelessState(type)) {
1335 *indentDepth = currentStatus.state(i - 1).savedIndentDepth;
1336 break;
1337 }
1338 }
1339 break;
1340
1341 case StateType::BracketOpen:
1342 if (parentState.type == StateType::Expression
1343 && currentStatus.state(1).type == StateType::BindingAssignment) {
1344 *savedIndentDepth = currentStatus.state(2).savedIndentDepth;
1345 *indentDepth = *savedIndentDepth + options.indentSize;
1346 } else if (parentState.type == StateType::ObjectliteralAssignment) {
1347 *savedIndentDepth = parentState.savedIndentDepth;
1348 *indentDepth = *savedIndentDepth + options.indentSize;
1349 } else if (!lastToken) {
1350 *indentDepth = tokenPosition + 1;
1351 } else {
1352 *indentDepth = *savedIndentDepth + options.indentSize;
1353 }
1354 break;
1355
1356 case StateType::LambdaStart:
1357 case StateType::FunctionStart:
1358 // align to the beginning of the line
1359 *savedIndentDepth = *indentDepth = column(tokenAt(0).begin());
1360 break;
1361
1362 case StateType::DoStatementWhileParenOpen:
1363 case StateType::StatementWithConditionParenOpen:
1364 case StateType::SignalArglistOpen:
1365 case StateType::FunctionArglistOpen:
1366 case StateType::ParenOpen:
1367 if (!lastToken)
1368 *indentDepth = tokenPosition + 1;
1369 else
1370 *indentDepth += options.indentSize;
1371 break;
1372
1373 case StateType::TernaryOp:
1374 if (!lastToken)
1375 *indentDepth = tokenPosition + tk.length + 1;
1376 else
1377 *indentDepth += options.indentSize;
1378 break;
1379
1380 case StateType::JsblockOpen:
1381 // closing brace should be aligned to case
1382 if (parentState.type == StateType::CaseCont) {
1383 *savedIndentDepth = parentState.savedIndentDepth;
1384 break;
1385 }
1386 Q_FALLTHROUGH();
1387 case StateType::SubstatementOpen:
1388 // special case for "foo: {" and "property int foo: {"
1389 if (parentState.type == StateType::BindingAssignment)
1390 *savedIndentDepth = currentStatus.state(1).savedIndentDepth;
1391 *indentDepth = *savedIndentDepth + options.indentSize;
1392 break;
1393
1394 case StateType::Substatement:
1395 *indentDepth += options.indentSize;
1396 break;
1397
1398 case StateType::ObjectliteralOpen:
1399 if (parentState.type == StateType::Expression
1400 || parentState.type == StateType::ObjectliteralAssignment) {
1401 // undo the continuation indent of the expression
1402 if (currentStatus.state(1).type == StateType::ExpressionOrLabel)
1403 *indentDepth = currentStatus.state(1).savedIndentDepth;
1404 else
1405 *indentDepth = parentState.savedIndentDepth;
1406 *savedIndentDepth = *indentDepth;
1407 }
1408 *indentDepth += options.indentSize;
1409 break;
1410
1411 case StateType::StatementWithCondition:
1412 case StateType::TryStatement:
1413 case StateType::CatchStatement:
1414 case StateType::FinallyStatement:
1415 case StateType::IfStatement:
1416 case StateType::DoStatement:
1417 case StateType::SwitchStatement:
1418 if (firstToken || parentState.type == StateType::BindingAssignment)
1419 *savedIndentDepth = tokenPosition;
1420 // ### continuation
1421 *indentDepth = *savedIndentDepth; // + 2*options.indentSize;
1422 // special case for 'else if'
1423 if (!firstToken && newState == StateType::IfStatement
1424 && parentState.type == StateType::Substatement
1425 && currentStatus.state(1).type == StateType::ElseClause) {
1426 *indentDepth = currentStatus.state(1).savedIndentDepth;
1427 *savedIndentDepth = *indentDepth;
1428 }
1429 break;
1430
1431 case StateType::MaybeElse:
1432 case StateType::MaybeCatchOrFinally: {
1433 // set indent to where leave(true) would put it
1434 int lastNonEndState = 0;
1435 while (!FormatTextStatus::isExpressionEndState(
1436 currentStatus.state(lastNonEndState + 1).type))
1437 ++lastNonEndState;
1438 *indentDepth = currentStatus.state(lastNonEndState).savedIndentDepth;
1439 break;
1440 }
1441
1442 case StateType::ConditionOpen:
1443 // fixed extra indent when continuing 'if (', but not for 'else if ('
1444 if (tokenPosition <= *indentDepth + options.indentSize)
1445 *indentDepth += 2 * options.indentSize;
1446 else
1447 *indentDepth = tokenPosition + 1;
1448 break;
1449
1450 case StateType::CaseStart:
1451 *savedIndentDepth = tokenPosition;
1452 break;
1453
1454 case StateType::CaseCont:
1455 *indentDepth += options.indentSize;
1456 break;
1457
1458 case StateType::MultilineCommentStart:
1459 *indentDepth = tokenPosition + 2;
1460 break;
1461
1462 case StateType::MultilineCommentCont:
1463 *indentDepth = tokenPosition;
1464 break;
1465 default:
1466 break;
1467 }
1468}
1469
1470} // namespace Dom
1471} // namespace QQmlJS
1472QT_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)