diff --git a/src/plugins/cpptools/cppcodeformatter.cpp b/src/plugins/cpptools/cppcodeformatter.cpp index 1bfb0a574a044cfc65eedfc226e7a0bdc738c535..7028157c1b62e4b101693a799bfaa421d3db59f2 100644 --- a/src/plugins/cpptools/cppcodeformatter.cpp +++ b/src/plugins/cpptools/cppcodeformatter.cpp @@ -38,8 +38,7 @@ using namespace TextEditor; using namespace CppTools::Internal; CodeFormatter::CodeFormatter() - : m_document(0) - , m_indentDepth(0) + : m_indentDepth(0) { } @@ -47,11 +46,6 @@ CodeFormatter::~CodeFormatter() { } -void CodeFormatter::setDocument(QTextDocument *document) -{ - m_document = document; -} - void CodeFormatter::recalculateStateAfter(const QTextBlock &block) { restoreBlockState(block.previous()); @@ -59,6 +53,7 @@ void CodeFormatter::recalculateStateAfter(const QTextBlock &block) bool endedJoined = false; const int lexerState = tokenizeBlock(block, &endedJoined); m_tokenIndex = 0; + m_newStates.clear(); if (tokenAt(0).kind() == T_POUND) { enter(cpp_macro_start); @@ -84,7 +79,8 @@ void CodeFormatter::recalculateStateAfter(const QTextBlock &block) case namespace_start: switch (kind) { - case T_LBRACE: turnInto(namespace_open); break; + case T_LBRACE: enter(namespace_open); break; + case T_RBRACE: leave(); break; } break; case namespace_open: @@ -92,7 +88,7 @@ void CodeFormatter::recalculateStateAfter(const QTextBlock &block) break; switch (kind) { case T_NAMESPACE: enter(namespace_start); break; - case T_RBRACE: leave(); break; + case T_RBRACE: leave(); continue; // always nested in namespace_start case T_STRUCT: case T_UNION: case T_CLASS: enter(class_start); break; @@ -103,14 +99,14 @@ void CodeFormatter::recalculateStateAfter(const QTextBlock &block) case class_start: switch (kind) { case T_SEMICOLON: leave(); break; - case T_LBRACE: turnInto(class_open); break; + case T_LBRACE: enter(class_open); break; } break; case class_open: if (tryDeclaration()) break; switch (kind) { - case T_RBRACE: leave(); break; + case T_RBRACE: leave(); continue; // always nested in class_start case T_STRUCT: case T_UNION: case T_CLASS: enter(class_start); break; @@ -121,13 +117,19 @@ void CodeFormatter::recalculateStateAfter(const QTextBlock &block) case enum_start: switch (kind) { case T_SEMICOLON: leave(); break; - case T_LBRACE: turnInto(brace_list_open); break; + case T_LBRACE: enter(enum_open); break; + } break; + + case enum_open: + switch (kind) { + case T_RBRACE: leave(); continue; // always nested in enum_start + case T_LBRACE: enter(brace_list_open); break; } break; case brace_list_open: switch (kind) { case T_RBRACE: leave(); break; - case T_LBRACE: enter(brace_list_open); break; // ### Other, nested brace list? + case T_LBRACE: enter(brace_list_open); break; } break; case using_start: @@ -156,10 +158,11 @@ void CodeFormatter::recalculateStateAfter(const QTextBlock &block) if (tryExpression(true)) break; switch (kind) { + case T_RBRACE: case T_SEMICOLON: leave(true); break; case T_EQUAL: enter(initializer); break; - case T_LBRACE: turnInto(defun_open); break; - case T_COLON: turnInto(member_init_open); break; + case T_LBRACE: enter(defun_open); break; + case T_COLON: enter(member_init_open); break; case T_OPERATOR: enter(operator_declaration); break; } break; @@ -211,14 +214,14 @@ void CodeFormatter::recalculateStateAfter(const QTextBlock &block) case member_init_open: switch (kind) { case T_LBRACE: turnInto(defun_open); break; - case T_SEMICOLON: leave(); break; // ### so we don't break completely if it's a bitfield or ternary + case T_SEMICOLON: leave(); continue; // so we don't break completely if it's a bitfield or ternary } break; case defun_open: if (tryStatement()) break; switch (kind) { - case T_RBRACE: leave(); break; + case T_RBRACE: leave(); continue; // always nested in declaration_start } break; case switch_statement: @@ -437,9 +440,6 @@ int CodeFormatter::indentFor(const QTextBlock &block) { // qDebug() << "indenting for" << block.blockNumber() + 1; - Q_ASSERT(m_document); - Q_ASSERT(m_document == block.document()); - requireStatesUntil(block); correctIndentation(block); return m_indentDepth; @@ -448,7 +448,7 @@ int CodeFormatter::indentFor(const QTextBlock &block) void CodeFormatter::requireStatesUntil(const QTextBlock &endBlock) { QStack<State> previousState = initialState(); - QTextBlock it = m_document->firstBlock(); + QTextBlock it = endBlock.document()->firstBlock(); for (; it.isValid() && it != endBlock; it = it.next()) { TextBlockUserData *userData = BaseTextDocumentLayout::userData(it); CppCodeFormatterData *cppData = static_cast<CppCodeFormatterData *>(userData->codeFormatterData()); @@ -478,14 +478,19 @@ CodeFormatter::State CodeFormatter::state(int belowTop) const return State(); } +const QVector<CodeFormatter::State> &CodeFormatter::newStatesThisLine() const +{ + return m_newStates; +} + int CodeFormatter::tokenIndex() const { return m_tokenIndex; } -int CodeFormatter::tokenIndexFromEnd() const +int CodeFormatter::tokenCount() const { - return m_tokens.size() - 1 - m_tokenIndex; + return m_tokens.size(); } const CPlusPlus::Token &CodeFormatter::currentToken() const @@ -493,9 +498,12 @@ const CPlusPlus::Token &CodeFormatter::currentToken() const return m_currentToken; } -void CodeFormatter::invalidateCache() +void CodeFormatter::invalidateCache(QTextDocument *document) { - QTextBlock it = m_document->firstBlock(); + if (!document) + return; + + QTextBlock it = document->firstBlock(); for (; it.isValid(); it = it.next()) { TextBlockUserData *userData = BaseTextDocumentLayout::userData(it); CppCodeFormatterData *cppData = static_cast<CppCodeFormatterData *>(userData->codeFormatterData()); @@ -509,7 +517,9 @@ void CodeFormatter::enter(int newState) { int savedIndentDepth = m_indentDepth; onEnter(newState, &m_indentDepth, &savedIndentDepth); - m_currentState.push(State(newState, savedIndentDepth)); + State s(newState, savedIndentDepth); + m_currentState.push(s); + m_newStates.push(s); } void CodeFormatter::leave(bool statementDone) @@ -518,6 +528,9 @@ void CodeFormatter::leave(bool statementDone) if (m_currentState.top().type == topmost_intro) return; + if (m_newStates.size() > 0) + m_newStates.pop(); + // restore indent depth State poppedState = m_currentState.pop(); m_indentDepth = poppedState.savedIndentDepth; @@ -794,24 +807,36 @@ void CodeFormatter::dump() QtStyleCodeFormatter::QtStyleCodeFormatter() : m_indentSize(4) - , m_style(QtStyle) + , m_indentSubstatementBraces(false) + , m_indentSubstatementStatements(true) + , m_indentDeclarationBraces(false) + , m_indentDeclarationMembers(true) { } void QtStyleCodeFormatter::setIndentSize(int size) { - if (size != m_indentSize) { - m_indentSize = size; - invalidateCache(); - } + m_indentSize = size; } -void QtStyleCodeFormatter::setCompoundStyle(CompoundStyle style) +void QtStyleCodeFormatter::setIndentSubstatementBraces(bool onOff) { - if (style != m_style) { - m_style = style; - invalidateCache(); - } + m_indentSubstatementBraces = onOff; +} + +void QtStyleCodeFormatter::setIndentSubstatementStatements(bool onOff) +{ + m_indentSubstatementStatements = onOff; +} + +void QtStyleCodeFormatter::setIndentDeclarationBraces(bool onOff) +{ + m_indentDeclarationBraces = onOff; +} + +void QtStyleCodeFormatter::setIndentDeclarationMembers(bool onOff) +{ + m_indentDeclarationMembers = onOff; } void QtStyleCodeFormatter::onEnter(int newState, int *indentDepth, int *savedIndentDepth) const @@ -820,7 +845,7 @@ void QtStyleCodeFormatter::onEnter(int newState, int *indentDepth, int *savedInd const Token &tk = currentToken(); const int tokenPosition = tk.begin(); const bool firstToken = (tokenIndex() == 0); - const bool lastToken = (tokenIndexFromEnd() == 0); + const bool lastToken = (tokenIndex() == tokenCount() - 1); switch (newState) { case namespace_start: @@ -874,28 +899,58 @@ void QtStyleCodeFormatter::onEnter(int newState, int *indentDepth, int *savedInd break; case member_init_open: + // undo the continuation indent of the parent + *savedIndentDepth = parentState.savedIndentDepth; + if (firstToken) *indentDepth = tokenPosition + tk.length() + 1; else - *indentDepth += m_indentSize; + *indentDepth = *savedIndentDepth + m_indentSize; break; - case defun_open: - case class_open: case case_cont: *indentDepth += m_indentSize; break; - case substatement_open: - if (m_style == WhitesmithsStyle) - break; - if (parentState.type != switch_statement) + case class_open: + case enum_open: + case defun_open: + // undo the continuation indent of the parent + *savedIndentDepth = parentState.savedIndentDepth; + + if (firstToken) + *savedIndentDepth = tokenPosition; + + *indentDepth = *savedIndentDepth; + + if (m_indentDeclarationMembers) *indentDepth += m_indentSize; break; + case substatement_open: + if (firstToken) { + *savedIndentDepth = tokenPosition; + *indentDepth = *savedIndentDepth; + } else if (m_indentSubstatementBraces && !m_indentSubstatementStatements) { + // ### The preceding check is quite arbitrary. + // It actually needs another flag to determine whether the closing curly + // should be indented or not + *indentDepth = *savedIndentDepth += m_indentSize; + } + + if (m_indentSubstatementStatements) { + if (parentState.type != switch_statement) + *indentDepth += m_indentSize; + } + break; + case brace_list_open: if (parentState.type != initializer) *indentDepth = parentState.savedIndentDepth + m_indentSize; + else if (lastToken) { + *savedIndentDepth = state(1).savedIndentDepth; + *indentDepth = *savedIndentDepth + m_indentSize; + } break; case block_open: @@ -919,9 +974,6 @@ void QtStyleCodeFormatter::onEnter(int newState, int *indentDepth, int *savedInd // undo the continuation indent of the parent *indentDepth = parentState.savedIndentDepth; *savedIndentDepth = *indentDepth; - // these styles want to indent braces - if (m_style == GnuStyle || m_style == WhitesmithsStyle) - *savedIndentDepth += m_indentSize; break; case maybe_else: { @@ -998,16 +1050,27 @@ void QtStyleCodeFormatter::adjustIndent(const QList<CPlusPlus::Token> &tokens, i } break; case T_LBRACE: { - // function definition - argument list is expression state - if (topState.type == case_cont) + if (topState.type == case_cont) { *indentDepth = topState.savedIndentDepth; - else if (topState.type == expression && previousState.type == declaration_start) + // function definition - argument list is expression state + } else if (topState.type == expression && previousState.type == declaration_start) { *indentDepth = previousState.savedIndentDepth; - else if (topState.type != defun_open - && topState.type != substatement_open + if (m_indentDeclarationBraces) + *indentDepth += m_indentSize; + } else if (topState.type == class_start) { + *indentDepth = topState.savedIndentDepth; + if (m_indentDeclarationBraces) + *indentDepth += m_indentSize; + } else if (topState.type == substatement) { + *indentDepth = topState.savedIndentDepth; + if (m_indentSubstatementBraces) + *indentDepth += m_indentSize; + } else if (topState.type != defun_open && topState.type != block_open - && !topWasMaybeElse) + && !topWasMaybeElse) { *indentDepth = topState.savedIndentDepth; + } + break; } case T_RBRACE: { @@ -1022,7 +1085,8 @@ void QtStyleCodeFormatter::adjustIndent(const QList<CPlusPlus::Token> &tokens, i || type == class_open || type == brace_list_open || type == namespace_open - || type == block_open) { + || type == block_open + || type == enum_open) { *indentDepth = state(i).savedIndentDepth; break; } diff --git a/src/plugins/cpptools/cppcodeformatter.h b/src/plugins/cpptools/cppcodeformatter.h index 75a9c31095420c67c230cc54eaf43f18ec47256f..72d1fe095e4ba1ff4526c25d719f470cc93a822f 100644 --- a/src/plugins/cpptools/cppcodeformatter.h +++ b/src/plugins/cpptools/cppcodeformatter.h @@ -28,9 +28,8 @@ public: CodeFormatter(); virtual ~CodeFormatter(); - void setDocument(QTextDocument *document); - int indentFor(const QTextBlock &block); + void invalidateCache(QTextDocument *document); protected: virtual void onEnter(int newState, int *indentDepth, int *savedIndentDepth) const = 0; @@ -58,7 +57,8 @@ protected: member_init_open, // After ':' that starts a member initialization list. enum_start, // After 'enum' - brace_list_open, // Open brace of an enum or static array list. + 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. @@ -125,15 +125,14 @@ protected: }; State state(int belowTop = 0) const; + const QVector<State> &newStatesThisLine() const; int tokenIndex() const; - int tokenIndexFromEnd() const; + int tokenCount() const; const CPlusPlus::Token ¤tToken() const; const CPlusPlus::Token &tokenAt(int idx) const; bool isBracelessState(int type) const; - void invalidateCache(); - private: void requireStatesUntil(const QTextBlock &block); void recalculateStateAfter(const QTextBlock &block); @@ -159,10 +158,9 @@ private: private: static QStack<State> initialState(); - QPointer<QTextDocument> m_document; - QStack<State> m_beginState; QStack<State> m_currentState; + QStack<State> m_newStates; QList<CPlusPlus::Token> m_tokens; QString m_currentLine; @@ -182,12 +180,10 @@ public: void setIndentSize(int size); - enum CompoundStyle { - QtStyle, // don't indent braces, add indent for contained statements - WhitesmithsStyle, // add indent for braces, don't for the contained statements - GnuStyle // add indent for braces and again for contained statements - }; - void setCompoundStyle(CompoundStyle style); + void setIndentSubstatementBraces(bool onOff); + void setIndentSubstatementStatements(bool onOff); + void setIndentDeclarationBraces(bool onOff); + void setIndentDeclarationMembers(bool onOff); protected: virtual void onEnter(int newState, int *indentDepth, int *savedIndentDepth) const; @@ -195,7 +191,10 @@ protected: private: int m_indentSize; - CompoundStyle m_style; + bool m_indentSubstatementBraces; + bool m_indentSubstatementStatements; + bool m_indentDeclarationBraces; + bool m_indentDeclarationMembers; }; } // namespace CppTools diff --git a/tests/auto/cplusplus/codeformatter/tst_codeformatter.cpp b/tests/auto/cplusplus/codeformatter/tst_codeformatter.cpp index 7082c21b94f14218302d10d8c9d428a2ac1bd70d..8e9b5d3fec8acf08c9267ed391145f308a04cbe5 100644 --- a/tests/auto/cplusplus/codeformatter/tst_codeformatter.cpp +++ b/tests/auto/cplusplus/codeformatter/tst_codeformatter.cpp @@ -40,6 +40,8 @@ private Q_SLOTS: void memberInitializer(); void templates(); void operatorOverloads(); + void gnuStyle(); + void whitesmithsStyle(); }; struct Line { @@ -73,12 +75,19 @@ QString concatLines(QList<Line> lines) return result; } -void checkIndent(QList<Line> data) +void checkIndent(QList<Line> data, int style = 0) { QString text = concatLines(data); QTextDocument document(text); QtStyleCodeFormatter formatter; - formatter.setDocument(&document); + if (style == 1) {// gnu + formatter.setIndentSubstatementBraces(true); + } else if (style == 2) { // whitesmiths + formatter.setIndentSubstatementStatements(false); + formatter.setIndentSubstatementBraces(true); + formatter.setIndentDeclarationMembers(false); + formatter.setIndentDeclarationBraces(true); + } int i = 0; foreach (const Line &l, data) { @@ -594,6 +603,10 @@ void tst_CodeFormatter::braceList() << Line("void foo () {") << Line(" int[] a = { foo, bar, ") << Line(" car };") + << Line(" int[] a = {") + << Line(" a, b,") + << Line(" c") + << Line(" };") << Line(" int k;") ; checkIndent(data); @@ -689,6 +702,46 @@ void tst_CodeFormatter::operatorOverloads() checkIndent(data); } +void tst_CodeFormatter::gnuStyle() +{ + QList<Line> data; + data << Line("struct S") + << Line("{") + << Line(" void foo()") + << Line(" {") + << Line(" if (a)") + << Line(" {") + << Line(" fpp;") + << Line(" }") + << Line(" if (b) {") + << Line(" fpp;") + << Line(" }") + << Line(" }") + << Line("};") + ; + checkIndent(data, 1); +} + +void tst_CodeFormatter::whitesmithsStyle() +{ + QList<Line> data; + data << Line("struct S") + << Line(" {") + << Line(" void foo()") + << Line(" {") + << Line(" if (a)") + << Line(" {") + << Line(" fpp;") + << Line(" }") + << Line(" if (b) {") + << Line(" fpp;") + << Line(" }") + << Line(" }") + << Line(" };") + ; + checkIndent(data, 2); +} + QTEST_APPLESS_MAIN(tst_CodeFormatter) #include "tst_codeformatter.moc"