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