Revision control
++ // 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
++ // Qt-Security score:significant
++
++ #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/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,
++
++ PragmaStart, // after 'pragma'
++ PragmaMaybeValue, // after pragma name
++ PragmaValue, // after ':' or ','
++ PragmaMaybeMoreValues, // after pragma value
++
++ 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 '('
++
++ LambdaStart, // 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 {},
++ QList<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 = {};
++ QList<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