diff options
Diffstat (limited to 'src/qmldom/qqmldomcodeformatter_p.h')
-rw-r--r-- | src/qmldom/qqmldomcodeformatter_p.h | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/src/qmldom/qqmldomcodeformatter_p.h b/src/qmldom/qqmldomcodeformatter_p.h new file mode 100644 index 0000000000..084272401e --- /dev/null +++ b/src/qmldom/qqmldomcodeformatter_p.h @@ -0,0 +1,275 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QQMLDOMCODEFORMATTER_P_H +#define QQMLDOMCODEFORMATTER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qqmldom_global.h" +#include "qqmldomfunctionref_p.h" +#include "qqmldomscanner_p.h" +#include "qqmldomlinewriter_p.h" + +#include <QtCore/QStack> +#include <QtCore/QList> +#include <QtCore/QSet> +#include <QtCore/QVector> +#include <QtCore/QMetaObject> + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace Dom { + +class QMLDOM_EXPORT FormatTextStatus +{ + Q_GADGET +public: + enum class StateType : quint8 { + Invalid = 0, + + TopmostIntro, // The first line in a "topmost" definition. + + TopQml, // root state for qml + TopJs, // root for js + ObjectdefinitionOrJs, // file starts with identifier + + MultilineCommentStart, + MultilineCommentCont, + + ImportStart, // after 'import' + ImportMaybeDotOrVersionOrAs, // after string or identifier + ImportDot, // after . + ImportMaybeAs, // after version + ImportAs, + + PropertyStart, // after 'property' + PropertyModifiers, // after 'default' or readonly + RequiredProperty, // after required + PropertyListOpen, // after 'list' as a type + PropertyName, // after the type + PropertyMaybeInitializer, // after the identifier + ComponentStart, // after component + ComponentName, // after component Name + + TypeAnnotation, // after a : starting a type annotation + TypeParameter, // after a < in a type annotation (starting type parameters) + + EnumStart, // after 'enum' + + SignalStart, // after 'signal' + SignalMaybeArglist, // after identifier + SignalArglistOpen, // after '(' + + FunctionStart, // after 'function' + FunctionArglistOpen, // after '(' starting function argument list + FunctionArglistClosed, // after ')' in argument list, expecting '{' + + BindingOrObjectdefinition, // after an identifier + + BindingAssignment, // after : in a binding + ObjectdefinitionOpen, // after { + + Expression, + ExpressionContinuation, // at the end of the line, when the next line definitely is a + // continuation + ExpressionMaybeContinuation, // at the end of the line, when the next line may be an + // expression + ExpressionOrObjectdefinition, // after a binding starting with an identifier ("x: foo") + ExpressionOrLabel, // when expecting a statement and getting an identifier + + ParenOpen, // opening ( in expression + BracketOpen, // opening [ in expression + ObjectliteralOpen, // opening { in expression + + ObjectliteralAssignment, // after : in object literal + + BracketElementStart, // after starting bracket_open or after ',' in bracket_open + BracketElementMaybeObjectdefinition, // after an identifier in bracket_element_start + + TernaryOp, // The ? : operator + TernaryOpAfterColon, // after the : in a ternary + + JsblockOpen, + + EmptyStatement, // for a ';', will be popped directly + BreakcontinueStatement, // for continue/break, may be followed by identifier + + IfStatement, // After 'if' + MaybeElse, // after the first substatement in an if + ElseClause, // The else line of an if-else construct. + + ConditionOpen, // Start of a condition in 'if', 'while', entered after opening paren + + Substatement, // The first line after a conditional or loop construct. + SubstatementOpen, // The brace that opens a substatement block. + + LabelledStatement, // after a label + + ReturnStatement, // After 'return' + ThrowStatement, // After 'throw' + + StatementWithCondition, // After the 'for', 'while', ... token + StatementWithConditionParenOpen, // While inside the (...) + + TryStatement, // after 'try' + CatchStatement, // after 'catch', nested in try_statement + FinallyStatement, // after 'finally', nested in try_statement + MaybeCatchOrFinally, // after ther closing '}' of try_statement and catch_statement, + // nested in try_statement + + DoStatement, // after 'do' + DoStatementWhileParenOpen, // after '(' in while clause + + SwitchStatement, // After 'switch' token + CaseStart, // after a 'case' or 'default' token + CaseCont // after the colon in a case/default + }; + Q_ENUM(StateType) + + static QString stateToString(StateType type); + + class State + { + public: + quint16 savedIndentDepth = 0; + StateType type = StateType::Invalid; + bool operator==(const State &other) const + { + return type == other.type && savedIndentDepth == other.savedIndentDepth; + } + QString typeStr() const { return FormatTextStatus::stateToString(type); } + }; + + static bool isBracelessState(StateType type) + { + return type == StateType::IfStatement || type == StateType::ElseClause + || type == StateType::Substatement || type == StateType::BindingAssignment + || type == StateType::BindingOrObjectdefinition; + } + + static bool isExpressionEndState(StateType type) + { + return type == StateType::TopmostIntro || type == StateType::TopJs + || type == StateType::ObjectdefinitionOpen || type == StateType::DoStatement + || type == StateType::JsblockOpen || type == StateType::SubstatementOpen + || type == StateType::BracketOpen || type == StateType::ParenOpen + || type == StateType::CaseCont || type == StateType::ObjectliteralOpen; + } + + static FormatTextStatus initialStatus(int baseIndent = 0) + { + return FormatTextStatus { + Scanner::State {}, + QVector<State>({ State { quint16(baseIndent), StateType::TopmostIntro } }), baseIndent + }; + } + + size_t size() const { return states.size(); } + + State state(int belowTop = 0) const; + + void pushState(StateType type, quint16 savedIndentDepth) + { + states.append(State { savedIndentDepth, type }); + } + + State popState() + { + if (states.isEmpty()) { + Q_ASSERT(false); + return State(); + } + State res = states.last(); + states.removeLast(); + return res; + } + + Scanner::State lexerState = {}; + QVector<State> states; + int finalIndent = 0; +}; + +class QMLDOM_EXPORT FormatPartialStatus +{ + Q_GADGET +public: + + using OnEnterCallback = + function_ref<void(FormatTextStatus::StateType newState, int *indentDepth, + int *savedIndentDepth, const FormatPartialStatus &fStatus)>; + + // to determine whether a line was joined, Tokenizer needs a + // newline character at the end, lease ensure that line contains it + FormatPartialStatus() = default; + FormatPartialStatus(const FormatPartialStatus &o) = default; + FormatPartialStatus &operator=(const FormatPartialStatus &o) = default; + FormatPartialStatus(QStringView line, const FormatOptions &options, + const FormatTextStatus &initialStatus) + : line(line), + options(options), + initialStatus(initialStatus), + currentStatus(initialStatus), + currentIndent(0), + tokenIndex(0) + { + Scanner::State startState = initialStatus.lexerState; + currentIndent = initialStatus.finalIndent; + Scanner tokenize; + lineTokens = tokenize(line, startState); + currentStatus.lexerState = tokenize.state(); + } + + void enterState(FormatTextStatus::StateType newState); + void leaveState(bool statementDone); + void turnIntoState(FormatTextStatus::StateType newState); + + const Token &tokenAt(int idx) const; + int tokenCount() const { return lineTokens.size(); } + int column(int index) const; + QStringView tokenText(const Token &token) const; + void handleTokens(); + + bool tryInsideExpression(bool alsoExpression); + bool tryStatement(); + + void defaultOnEnter(FormatTextStatus::StateType newState, int *indentDepth, + int *savedIndentDepth) const; + + int indentLine(); + int indentForNewLineAfter() const; + void recalculateWithIndent(int indent); + + void dump() const; + + QStringView line; + FormatOptions options; + FormatTextStatus initialStatus; + FormatTextStatus currentStatus; + int indentOffset = 0; + int currentIndent = 0; + QList<Token> lineTokens; + int tokenIndex = 0; +}; + +QMLDOM_EXPORT int indentForLineStartingWithToken(const FormatTextStatus &oldStatus, + const FormatOptions &options, + int token = QQmlJSGrammar::T_ERROR); + +QMLDOM_EXPORT FormatPartialStatus formatCodeLine(QStringView line, const FormatOptions &options, + const FormatTextStatus &initialStatus); + +} // namespace Dom +} // namespace QQmlJs +QT_END_NAMESPACE +#endif // QQMLDOMCODEFORMATTER_P_H |