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