// Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #pragma once #include "cppeditor_global.h" #include "cppcodestylesettings.h" #include #include #include #include #include QT_BEGIN_NAMESPACE class QTextDocument; class QTextBlock; QT_END_NAMESPACE namespace CppEditor { namespace Internal { class CppCodeFormatterData; } class CPPEDITOR_EXPORT CodeFormatter { Q_GADGET public: virtual ~CodeFormatter(); // updates all states up until block if necessary // it is safe to call indentFor on block afterwards void updateStateUntil(const QTextBlock &block); // calculates the state change introduced by changing a single line void updateLineStateChange(const QTextBlock &block); bool isInRawStringLiteral(const QTextBlock &block) const; void indentFor(const QTextBlock &block, int *indent, int *padding); void indentForNewLineAfter(const QTextBlock &block, int *indent, int *padding); void setTabSize(int tabSize); void setStatementMacros(const QStringList ¯os) { m_statementMacros = macros; } void invalidateCache(QTextDocument *document); protected: virtual void onEnter(int newState, int *indentDepth, int *savedIndentDepth, int *paddingDepth, int *savedPaddingDepth) const = 0; virtual void adjustIndent(const CPlusPlus::Tokens &tokens, int lexerState, int *indentDepth, int *paddingDepth) const = 0; class State; class BlockData { public: QStack m_beginState; QStack m_endState; int m_indentDepth = 0; int m_paddingDepth = 0; int m_blockRevision = -1; }; virtual void saveBlockData(QTextBlock *block, const BlockData &data) const = 0; virtual bool loadBlockData(const QTextBlock &block, BlockData *data) const = 0; virtual void saveLexerState(QTextBlock *block, int state) const = 0; virtual int loadLexerState(const QTextBlock &block) const = 0; public: // must be public to make Q_GADGET introspection work enum StateType { invalid = 0, topmost_intro, // The first line in a "topmost" definition. multiline_comment_start, // Inside the first line of a multi-line C style block comment. multiline_comment_cont, // Inside the following lines of a multi-line C style block comment. cpp_macro_start, // After the '#' token cpp_macro, // The start of a C preprocessor macro definition. cpp_macro_cont, // Subsequent lines of a multi-line C preprocessor macro definition. cpp_macro_conditional, // Special marker used for separating saved from current state when dealing with #ifdef qt_like_macro, // after an identifier starting with Q_ or QT_ at the beginning of the line label, // after an identifier followed by a colon defun_open, // Brace that opens a top-level function definition. using_start, // right after the "using" token class_start, // after the 'class' token class_open, // Brace that opens a class definition. access_specifier_start, // after 'private', 'protected' etc. member_init_open, // After ':' that starts a member initialization list. member_init_expected, // At the start and after every ',' in member_init_open member_init, // After an identifier in member_init_expected member_init_nest_open, // After '(' or '{' in member_init. enum_start, // After 'enum' enum_open, // Brace that opens a enum declaration. brace_list_open, // Open brace nested inside an enum or for a static array list. namespace_start, // after the namespace token, before the opening brace. namespace_open, // Brace that opens a C++ namespace block. extern_start, // after the extern token, before the opening brace. extern_open, // Brace that opens a C++ extern block. declaration_start, // shifted a token which could start a declaration. operator_declaration, // after 'operator' in declaration_start template_start, // after the 'template' token template_param, // after the '<' in a template_start if_statement, // After 'if' maybe_else, // after the first substatement in an if else_clause, // The else line of an if-else construct. for_statement, // After the 'for' token for_statement_paren_open, // While inside the (...) for_statement_init, // The initializer part of the for statement for_statement_condition, // The condition part of the for statement for_statement_expression, // The expression part of the for statement catch_statement, switch_statement, // After 'switch' token case_start, // after a 'case' or 'default' token case_cont, // after the colon in a case/default statement_with_condition, // A statement that takes a condition after the start token. do_statement, // After 'do' token return_statement, // After 'return' block_open, // Statement block open brace. substatement, // The first line after a conditional or loop construct. substatement_open, // The brace that opens a substatement block. arglist_open, // after the lparen. TODO: check if this is enough. stream_op, // After a '<<' or '>>' in a context where it's likely a stream operator. stream_op_cont, // When finding another stream operator in stream_op ternary_op, // The ? : operator braceinit_open, // after '{' in an expression context condition_open, // Start of a condition in 'if', 'while', entered after opening paren condition_paren_open, // After an lparen in a condition assign_open, // after an assignment token expression, // after a '=' in a declaration_start once we're sure it's not '= {' assign_open_or_initializer, // after a '=' in a declaration start lambda_instroducer_or_subscribtion, // just after '[' or in cases '[]' and '[id]' when we're not sure in the exact kind of expression lambda_declarator_expected, // just after ']' in lambda_introducer_or_subscribtion lambda_declarator_or_expression, // just after '](' when previous state is 'lambda_instroducer_or_subscribtion' lambda_statement_expected, lambda_instroducer, // when '=', '&' or ',' occurred within '[]' lambda_declarator, // just after ']' when previous state is lambda_introducer lambda_statement, // just after '{' when previous state is lambda_declarator or lambda_declarator_or_expression string_open, // after opening quote of simple string types, like ", L", u8" etc. raw_string_open // after raw string open delimiter, like R"EOF( }; Q_ENUM(StateType) protected: class State { public: State() = default; State(quint8 ty, quint16 savedIndentDepth, quint16 savedPaddingDepth) : savedIndentDepth(savedIndentDepth) , savedPaddingDepth(savedPaddingDepth) , type(ty) {} quint16 savedIndentDepth = 0; quint16 savedPaddingDepth = 0; quint8 type = 0; bool operator==(const State &other) const { return type == other.type && savedIndentDepth == other.savedIndentDepth && savedPaddingDepth == other.savedPaddingDepth; } }; State state(int belowTop = 0) const; int tokenIndex() const; int tokenCount() const; const CPlusPlus::Token ¤tToken() const; const CPlusPlus::Token &tokenAt(int idx) const; int column(int position) const; bool isBracelessState(int type) const; void dump() const; private: void recalculateStateAfter(const QTextBlock &block); void saveCurrentState(const QTextBlock &block); void restoreCurrentState(const QTextBlock &block); QStringView currentTokenText() const; int tokenizeBlock(const QTextBlock &block, bool *endedJoined = nullptr); void turnInto(int newState); bool tryExpression(bool alsoExpression = false); bool tryDeclaration(); bool tryStatement(); void enter(int newState); void leave(bool statementDone = false); void correctIndentation(const QTextBlock &block); bool isStatementMacroOrEquivalent() const; private: static QStack initialState(); QStack m_beginState; QStack m_currentState; QStack m_newStates; CPlusPlus::Tokens m_tokens; QString m_currentLine; CPlusPlus::Token m_currentToken; int m_tokenIndex = 0; int m_indentDepth = 0; int m_paddingDepth = 0; int m_tabSize = 4; QStringList m_statementMacros; friend class Internal::CppCodeFormatterData; }; class CPPEDITOR_EXPORT QtStyleCodeFormatter : public CodeFormatter { public: QtStyleCodeFormatter(); QtStyleCodeFormatter(const TextEditor::TabSettings &tabSettings, const CppCodeStyleSettings &settings); void setTabSettings(const TextEditor::TabSettings &tabSettings); void setCodeStyleSettings(const CppCodeStyleSettings &settings); protected: void onEnter(int newState, int *indentDepth, int *savedIndentDepth, int *paddingDepth, int *savedPaddingDepth) const override; void adjustIndent(const CPlusPlus::Tokens &tokens, int lexerState, int *indentDepth, int *paddingDepth) const override; void saveBlockData(QTextBlock *block, const BlockData &data) const override; bool loadBlockData(const QTextBlock &block, BlockData *data) const override; void saveLexerState(QTextBlock *block, int state) const override; int loadLexerState(const QTextBlock &block) const override; static bool shouldClearPaddingOnEnter(int state); private: void addContinuationIndent(int *paddingDepth) const; TextEditor::TabSettings m_tabSettings; CppCodeStyleSettings m_styleSettings; }; } // namespace CppEditor