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#ifndef QQMLDOMCODEFORMATTER_P_H
5#define QQMLDOMCODEFORMATTER_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include "qqmldom_global.h"
19#include "qqmldomfunctionref_p.h"
20#include "qqmldomscanner_p.h"
21#include "qqmldomlinewriter_p.h"
22
23#include <QtCore/QStack>
24#include <QtCore/QList>
25#include <QtCore/QSet>
26#include <QtCore/QVector>
27#include <QtCore/QMetaObject>
28
29QT_BEGIN_NAMESPACE
30
31namespace QQmlJS {
32namespace Dom {
33
34class QMLDOM_EXPORT FormatTextStatus
35{
36 Q_GADGET
37public:
38 enum class StateType : quint8 {
39 Invalid = 0,
40
41 TopmostIntro, // The first line in a "topmost" definition.
42
43 TopQml, // root state for qml
44 TopJs, // root for js
45 ObjectdefinitionOrJs, // file starts with identifier
46
47 MultilineCommentStart,
48 MultilineCommentCont,
49
50 ImportStart, // after 'import'
51 ImportMaybeDotOrVersionOrAs, // after string or identifier
52 ImportDot, // after .
53 ImportMaybeAs, // after version
54 ImportAs,
55
56 PropertyStart, // after 'property'
57 PropertyModifiers, // after 'default' or readonly
58 RequiredProperty, // after required
59 PropertyListOpen, // after 'list' as a type
60 PropertyName, // after the type
61 PropertyMaybeInitializer, // after the identifier
62 ComponentStart, // after component
63 ComponentName, // after component Name
64
65 TypeAnnotation, // after a : starting a type annotation
66 TypeParameter, // after a < in a type annotation (starting type parameters)
67
68 EnumStart, // after 'enum'
69
70 SignalStart, // after 'signal'
71 SignalMaybeArglist, // after identifier
72 SignalArglistOpen, // after '('
73
74 FunctionStart, // after 'function'
75 FunctionArglistOpen, // after '(' starting function argument list
76 FunctionArglistClosed, // after ')' in argument list, expecting '{'
77
78 BindingOrObjectdefinition, // after an identifier
79
80 BindingAssignment, // after : in a binding
81 ObjectdefinitionOpen, // after {
82
83 Expression,
84 ExpressionContinuation, // at the end of the line, when the next line definitely is a
85 // continuation
86 ExpressionMaybeContinuation, // at the end of the line, when the next line may be an
87 // expression
88 ExpressionOrObjectdefinition, // after a binding starting with an identifier ("x: foo")
89 ExpressionOrLabel, // when expecting a statement and getting an identifier
90
91 ParenOpen, // opening ( in expression
92 BracketOpen, // opening [ in expression
93 ObjectliteralOpen, // opening { in expression
94
95 ObjectliteralAssignment, // after : in object literal
96
97 BracketElementStart, // after starting bracket_open or after ',' in bracket_open
98 BracketElementMaybeObjectdefinition, // after an identifier in bracket_element_start
99
100 TernaryOp, // The ? : operator
101 TernaryOpAfterColon, // after the : in a ternary
102
103 JsblockOpen,
104
105 EmptyStatement, // for a ';', will be popped directly
106 BreakcontinueStatement, // for continue/break, may be followed by identifier
107
108 IfStatement, // After 'if'
109 MaybeElse, // after the first substatement in an if
110 ElseClause, // The else line of an if-else construct.
111
112 ConditionOpen, // Start of a condition in 'if', 'while', entered after opening paren
113
114 Substatement, // The first line after a conditional or loop construct.
115 SubstatementOpen, // The brace that opens a substatement block.
116
117 LabelledStatement, // after a label
118
119 ReturnStatement, // After 'return'
120 ThrowStatement, // After 'throw'
121
122 StatementWithCondition, // After the 'for', 'while', ... token
123 StatementWithConditionParenOpen, // While inside the (...)
124
125 TryStatement, // after 'try'
126 CatchStatement, // after 'catch', nested in try_statement
127 FinallyStatement, // after 'finally', nested in try_statement
128 MaybeCatchOrFinally, // after ther closing '}' of try_statement and catch_statement,
129 // nested in try_statement
130
131 DoStatement, // after 'do'
132 DoStatementWhileParenOpen, // after '(' in while clause
133
134 SwitchStatement, // After 'switch' token
135 CaseStart, // after a 'case' or 'default' token
136 CaseCont // after the colon in a case/default
137 };
138 Q_ENUM(StateType)
139
140 static QString stateToString(StateType type);
141
142 class State
143 {
144 public:
145 quint16 savedIndentDepth = 0;
146 StateType type = StateType::Invalid;
147 bool operator==(const State &other) const
148 {
149 return type == other.type && savedIndentDepth == other.savedIndentDepth;
150 }
151 QString typeStr() const { return FormatTextStatus::stateToString(type); }
152 };
153
154 static bool isBracelessState(StateType type)
155 {
156 return type == StateType::IfStatement || type == StateType::ElseClause
157 || type == StateType::Substatement || type == StateType::BindingAssignment
158 || type == StateType::BindingOrObjectdefinition;
159 }
160
161 static bool isExpressionEndState(StateType type)
162 {
163 return type == StateType::TopmostIntro || type == StateType::TopJs
164 || type == StateType::ObjectdefinitionOpen || type == StateType::DoStatement
165 || type == StateType::JsblockOpen || type == StateType::SubstatementOpen
166 || type == StateType::BracketOpen || type == StateType::ParenOpen
167 || type == StateType::CaseCont || type == StateType::ObjectliteralOpen;
168 }
169
170 static FormatTextStatus initialStatus(int baseIndent = 0)
171 {
172 return FormatTextStatus {
173 .lexerState: Scanner::State {},
174 .states: QVector<State>({ State { .savedIndentDepth: quint16(baseIndent), .type: StateType::TopmostIntro } }), .finalIndent: baseIndent
175 };
176 }
177
178 size_t size() const { return states.size(); }
179
180 State state(int belowTop = 0) const;
181
182 void pushState(StateType type, quint16 savedIndentDepth)
183 {
184 states.append(t: State { .savedIndentDepth: savedIndentDepth, .type: type });
185 }
186
187 State popState()
188 {
189 if (states.isEmpty()) {
190 Q_ASSERT(false);
191 return State();
192 }
193 State res = states.last();
194 states.removeLast();
195 return res;
196 }
197
198 Scanner::State lexerState = {};
199 QVector<State> states;
200 int finalIndent = 0;
201};
202
203class QMLDOM_EXPORT FormatPartialStatus
204{
205 Q_GADGET
206public:
207
208 using OnEnterCallback =
209 function_ref<void(FormatTextStatus::StateType newState, int *indentDepth,
210 int *savedIndentDepth, const FormatPartialStatus &fStatus)>;
211
212 // to determine whether a line was joined, Tokenizer needs a
213 // newline character at the end, lease ensure that line contains it
214 FormatPartialStatus() = default;
215 FormatPartialStatus(const FormatPartialStatus &o) = default;
216 FormatPartialStatus &operator=(const FormatPartialStatus &o) = default;
217 FormatPartialStatus(QStringView line, const FormatOptions &options,
218 const FormatTextStatus &initialStatus)
219 : line(line),
220 options(options),
221 initialStatus(initialStatus),
222 currentStatus(initialStatus),
223 currentIndent(0),
224 tokenIndex(0)
225 {
226 Scanner::State startState = initialStatus.lexerState;
227 currentIndent = initialStatus.finalIndent;
228 Scanner tokenize;
229 lineTokens = tokenize(line, startState);
230 currentStatus.lexerState = tokenize.state();
231 }
232
233 void enterState(FormatTextStatus::StateType newState);
234 void leaveState(bool statementDone);
235 void turnIntoState(FormatTextStatus::StateType newState);
236
237 const Token &tokenAt(int idx) const;
238 int tokenCount() const { return lineTokens.size(); }
239 int column(int index) const;
240 QStringView tokenText(const Token &token) const;
241 void handleTokens();
242
243 bool tryInsideExpression(bool alsoExpression);
244 bool tryStatement();
245
246 void defaultOnEnter(FormatTextStatus::StateType newState, int *indentDepth,
247 int *savedIndentDepth) const;
248
249 int indentLine();
250 int indentForNewLineAfter() const;
251 void recalculateWithIndent(int indent);
252
253 void dump() const;
254
255 QStringView line;
256 FormatOptions options;
257 FormatTextStatus initialStatus;
258 FormatTextStatus currentStatus;
259 int indentOffset = 0;
260 int currentIndent = 0;
261 QList<Token> lineTokens;
262 int tokenIndex = 0;
263};
264
265QMLDOM_EXPORT int indentForLineStartingWithToken(const FormatTextStatus &oldStatus,
266 const FormatOptions &options,
267 int token = QQmlJSGrammar::T_ERROR);
268
269QMLDOM_EXPORT FormatPartialStatus formatCodeLine(QStringView line, const FormatOptions &options,
270 const FormatTextStatus &initialStatus);
271
272} // namespace Dom
273} // namespace QQmlJs
274QT_END_NAMESPACE
275#endif // QQMLDOMCODEFORMATTER_P_H
276

source code of qtdeclarative/src/qmldom/qqmldomcodeformatter_p.h