C++ indenter: Make building custom styles easier, fix style issues.

Keep more information by using enter() instead of turnInto() when moving
from a *_start to *_open.
This commit is contained in:
Christian Kamm
2010-07-05 12:56:37 +02:00
parent 3100fc0b7e
commit 19db6c9826
3 changed files with 188 additions and 72 deletions

View File

@@ -38,8 +38,7 @@ using namespace TextEditor;
using namespace CppTools::Internal; using namespace CppTools::Internal;
CodeFormatter::CodeFormatter() 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) void CodeFormatter::recalculateStateAfter(const QTextBlock &block)
{ {
restoreBlockState(block.previous()); restoreBlockState(block.previous());
@@ -59,6 +53,7 @@ void CodeFormatter::recalculateStateAfter(const QTextBlock &block)
bool endedJoined = false; bool endedJoined = false;
const int lexerState = tokenizeBlock(block, &endedJoined); const int lexerState = tokenizeBlock(block, &endedJoined);
m_tokenIndex = 0; m_tokenIndex = 0;
m_newStates.clear();
if (tokenAt(0).kind() == T_POUND) { if (tokenAt(0).kind() == T_POUND) {
enter(cpp_macro_start); enter(cpp_macro_start);
@@ -84,7 +79,8 @@ void CodeFormatter::recalculateStateAfter(const QTextBlock &block)
case namespace_start: case namespace_start:
switch (kind) { switch (kind) {
case T_LBRACE: turnInto(namespace_open); break; case T_LBRACE: enter(namespace_open); break;
case T_RBRACE: leave(); break;
} break; } break;
case namespace_open: case namespace_open:
@@ -92,7 +88,7 @@ void CodeFormatter::recalculateStateAfter(const QTextBlock &block)
break; break;
switch (kind) { switch (kind) {
case T_NAMESPACE: enter(namespace_start); break; 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_STRUCT:
case T_UNION: case T_UNION:
case T_CLASS: enter(class_start); break; case T_CLASS: enter(class_start); break;
@@ -103,14 +99,14 @@ void CodeFormatter::recalculateStateAfter(const QTextBlock &block)
case class_start: case class_start:
switch (kind) { switch (kind) {
case T_SEMICOLON: leave(); break; case T_SEMICOLON: leave(); break;
case T_LBRACE: turnInto(class_open); break; case T_LBRACE: enter(class_open); break;
} break; } break;
case class_open: case class_open:
if (tryDeclaration()) if (tryDeclaration())
break; break;
switch (kind) { switch (kind) {
case T_RBRACE: leave(); break; case T_RBRACE: leave(); continue; // always nested in class_start
case T_STRUCT: case T_STRUCT:
case T_UNION: case T_UNION:
case T_CLASS: enter(class_start); break; case T_CLASS: enter(class_start); break;
@@ -121,13 +117,19 @@ void CodeFormatter::recalculateStateAfter(const QTextBlock &block)
case enum_start: case enum_start:
switch (kind) { switch (kind) {
case T_SEMICOLON: leave(); break; 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; } break;
case brace_list_open: case brace_list_open:
switch (kind) { switch (kind) {
case T_RBRACE: leave(); break; 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; } break;
case using_start: case using_start:
@@ -156,10 +158,11 @@ void CodeFormatter::recalculateStateAfter(const QTextBlock &block)
if (tryExpression(true)) if (tryExpression(true))
break; break;
switch (kind) { switch (kind) {
case T_RBRACE:
case T_SEMICOLON: leave(true); break; case T_SEMICOLON: leave(true); break;
case T_EQUAL: enter(initializer); break; case T_EQUAL: enter(initializer); break;
case T_LBRACE: turnInto(defun_open); break; case T_LBRACE: enter(defun_open); break;
case T_COLON: turnInto(member_init_open); break; case T_COLON: enter(member_init_open); break;
case T_OPERATOR: enter(operator_declaration); break; case T_OPERATOR: enter(operator_declaration); break;
} break; } break;
@@ -211,14 +214,14 @@ void CodeFormatter::recalculateStateAfter(const QTextBlock &block)
case member_init_open: case member_init_open:
switch (kind) { switch (kind) {
case T_LBRACE: turnInto(defun_open); break; 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; } break;
case defun_open: case defun_open:
if (tryStatement()) if (tryStatement())
break; break;
switch (kind) { switch (kind) {
case T_RBRACE: leave(); break; case T_RBRACE: leave(); continue; // always nested in declaration_start
} break; } break;
case switch_statement: case switch_statement:
@@ -437,9 +440,6 @@ int CodeFormatter::indentFor(const QTextBlock &block)
{ {
// qDebug() << "indenting for" << block.blockNumber() + 1; // qDebug() << "indenting for" << block.blockNumber() + 1;
Q_ASSERT(m_document);
Q_ASSERT(m_document == block.document());
requireStatesUntil(block); requireStatesUntil(block);
correctIndentation(block); correctIndentation(block);
return m_indentDepth; return m_indentDepth;
@@ -448,7 +448,7 @@ int CodeFormatter::indentFor(const QTextBlock &block)
void CodeFormatter::requireStatesUntil(const QTextBlock &endBlock) void CodeFormatter::requireStatesUntil(const QTextBlock &endBlock)
{ {
QStack<State> previousState = initialState(); QStack<State> previousState = initialState();
QTextBlock it = m_document->firstBlock(); QTextBlock it = endBlock.document()->firstBlock();
for (; it.isValid() && it != endBlock; it = it.next()) { for (; it.isValid() && it != endBlock; it = it.next()) {
TextBlockUserData *userData = BaseTextDocumentLayout::userData(it); TextBlockUserData *userData = BaseTextDocumentLayout::userData(it);
CppCodeFormatterData *cppData = static_cast<CppCodeFormatterData *>(userData->codeFormatterData()); CppCodeFormatterData *cppData = static_cast<CppCodeFormatterData *>(userData->codeFormatterData());
@@ -478,14 +478,19 @@ CodeFormatter::State CodeFormatter::state(int belowTop) const
return State(); return State();
} }
const QVector<CodeFormatter::State> &CodeFormatter::newStatesThisLine() const
{
return m_newStates;
}
int CodeFormatter::tokenIndex() const int CodeFormatter::tokenIndex() const
{ {
return m_tokenIndex; 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 const CPlusPlus::Token &CodeFormatter::currentToken() const
@@ -493,9 +498,12 @@ const CPlusPlus::Token &CodeFormatter::currentToken() const
return m_currentToken; 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()) { for (; it.isValid(); it = it.next()) {
TextBlockUserData *userData = BaseTextDocumentLayout::userData(it); TextBlockUserData *userData = BaseTextDocumentLayout::userData(it);
CppCodeFormatterData *cppData = static_cast<CppCodeFormatterData *>(userData->codeFormatterData()); CppCodeFormatterData *cppData = static_cast<CppCodeFormatterData *>(userData->codeFormatterData());
@@ -509,7 +517,9 @@ void CodeFormatter::enter(int newState)
{ {
int savedIndentDepth = m_indentDepth; int savedIndentDepth = m_indentDepth;
onEnter(newState, &m_indentDepth, &savedIndentDepth); 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) void CodeFormatter::leave(bool statementDone)
@@ -518,6 +528,9 @@ void CodeFormatter::leave(bool statementDone)
if (m_currentState.top().type == topmost_intro) if (m_currentState.top().type == topmost_intro)
return; return;
if (m_newStates.size() > 0)
m_newStates.pop();
// restore indent depth // restore indent depth
State poppedState = m_currentState.pop(); State poppedState = m_currentState.pop();
m_indentDepth = poppedState.savedIndentDepth; m_indentDepth = poppedState.savedIndentDepth;
@@ -794,24 +807,36 @@ void CodeFormatter::dump()
QtStyleCodeFormatter::QtStyleCodeFormatter() QtStyleCodeFormatter::QtStyleCodeFormatter()
: m_indentSize(4) : m_indentSize(4)
, m_style(QtStyle) , m_indentSubstatementBraces(false)
, m_indentSubstatementStatements(true)
, m_indentDeclarationBraces(false)
, m_indentDeclarationMembers(true)
{ {
} }
void QtStyleCodeFormatter::setIndentSize(int size) void QtStyleCodeFormatter::setIndentSize(int size)
{ {
if (size != m_indentSize) { m_indentSize = size;
m_indentSize = size;
invalidateCache();
}
} }
void QtStyleCodeFormatter::setCompoundStyle(CompoundStyle style) void QtStyleCodeFormatter::setIndentSubstatementBraces(bool onOff)
{ {
if (style != m_style) { m_indentSubstatementBraces = onOff;
m_style = style; }
invalidateCache();
} 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 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 Token &tk = currentToken();
const int tokenPosition = tk.begin(); const int tokenPosition = tk.begin();
const bool firstToken = (tokenIndex() == 0); const bool firstToken = (tokenIndex() == 0);
const bool lastToken = (tokenIndexFromEnd() == 0); const bool lastToken = (tokenIndex() == tokenCount() - 1);
switch (newState) { switch (newState) {
case namespace_start: case namespace_start:
@@ -874,28 +899,58 @@ void QtStyleCodeFormatter::onEnter(int newState, int *indentDepth, int *savedInd
break; break;
case member_init_open: case member_init_open:
// undo the continuation indent of the parent
*savedIndentDepth = parentState.savedIndentDepth;
if (firstToken) if (firstToken)
*indentDepth = tokenPosition + tk.length() + 1; *indentDepth = tokenPosition + tk.length() + 1;
else else
*indentDepth += m_indentSize; *indentDepth = *savedIndentDepth + m_indentSize;
break; break;
case defun_open:
case class_open:
case case_cont: case case_cont:
*indentDepth += m_indentSize; *indentDepth += m_indentSize;
break; break;
case substatement_open: case class_open:
if (m_style == WhitesmithsStyle) case enum_open:
break; case defun_open:
if (parentState.type != switch_statement) // undo the continuation indent of the parent
*savedIndentDepth = parentState.savedIndentDepth;
if (firstToken)
*savedIndentDepth = tokenPosition;
*indentDepth = *savedIndentDepth;
if (m_indentDeclarationMembers)
*indentDepth += m_indentSize; *indentDepth += m_indentSize;
break; 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: case brace_list_open:
if (parentState.type != initializer) if (parentState.type != initializer)
*indentDepth = parentState.savedIndentDepth + m_indentSize; *indentDepth = parentState.savedIndentDepth + m_indentSize;
else if (lastToken) {
*savedIndentDepth = state(1).savedIndentDepth;
*indentDepth = *savedIndentDepth + m_indentSize;
}
break; break;
case block_open: case block_open:
@@ -919,9 +974,6 @@ void QtStyleCodeFormatter::onEnter(int newState, int *indentDepth, int *savedInd
// undo the continuation indent of the parent // undo the continuation indent of the parent
*indentDepth = parentState.savedIndentDepth; *indentDepth = parentState.savedIndentDepth;
*savedIndentDepth = *indentDepth; *savedIndentDepth = *indentDepth;
// these styles want to indent braces
if (m_style == GnuStyle || m_style == WhitesmithsStyle)
*savedIndentDepth += m_indentSize;
break; break;
case maybe_else: { case maybe_else: {
@@ -998,16 +1050,27 @@ void QtStyleCodeFormatter::adjustIndent(const QList<CPlusPlus::Token> &tokens, i
} }
break; break;
case T_LBRACE: { case T_LBRACE: {
if (topState.type == case_cont) {
*indentDepth = topState.savedIndentDepth;
// function definition - argument list is expression state // function definition - argument list is expression state
if (topState.type == case_cont) } else if (topState.type == expression && previousState.type == declaration_start) {
*indentDepth = topState.savedIndentDepth;
else if (topState.type == expression && previousState.type == declaration_start)
*indentDepth = previousState.savedIndentDepth; *indentDepth = previousState.savedIndentDepth;
else if (topState.type != defun_open if (m_indentDeclarationBraces)
&& topState.type != substatement_open *indentDepth += m_indentSize;
&& topState.type != block_open } else if (topState.type == class_start) {
&& !topWasMaybeElse)
*indentDepth = topState.savedIndentDepth; *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) {
*indentDepth = topState.savedIndentDepth;
}
break; break;
} }
case T_RBRACE: { case T_RBRACE: {
@@ -1022,7 +1085,8 @@ void QtStyleCodeFormatter::adjustIndent(const QList<CPlusPlus::Token> &tokens, i
|| type == class_open || type == class_open
|| type == brace_list_open || type == brace_list_open
|| type == namespace_open || type == namespace_open
|| type == block_open) { || type == block_open
|| type == enum_open) {
*indentDepth = state(i).savedIndentDepth; *indentDepth = state(i).savedIndentDepth;
break; break;
} }

View File

@@ -28,9 +28,8 @@ public:
CodeFormatter(); CodeFormatter();
virtual ~CodeFormatter(); virtual ~CodeFormatter();
void setDocument(QTextDocument *document);
int indentFor(const QTextBlock &block); int indentFor(const QTextBlock &block);
void invalidateCache(QTextDocument *document);
protected: protected:
virtual void onEnter(int newState, int *indentDepth, int *savedIndentDepth) const = 0; 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. member_init_open, // After ':' that starts a member initialization list.
enum_start, // After 'enum' 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_start, // after the namespace token, before the opening brace.
namespace_open, // Brace that opens a C++ namespace block. namespace_open, // Brace that opens a C++ namespace block.
@@ -125,15 +125,14 @@ protected:
}; };
State state(int belowTop = 0) const; State state(int belowTop = 0) const;
const QVector<State> &newStatesThisLine() const;
int tokenIndex() const; int tokenIndex() const;
int tokenIndexFromEnd() const; int tokenCount() const;
const CPlusPlus::Token &currentToken() const; const CPlusPlus::Token &currentToken() const;
const CPlusPlus::Token &tokenAt(int idx) const; const CPlusPlus::Token &tokenAt(int idx) const;
bool isBracelessState(int type) const; bool isBracelessState(int type) const;
void invalidateCache();
private: private:
void requireStatesUntil(const QTextBlock &block); void requireStatesUntil(const QTextBlock &block);
void recalculateStateAfter(const QTextBlock &block); void recalculateStateAfter(const QTextBlock &block);
@@ -159,10 +158,9 @@ private:
private: private:
static QStack<State> initialState(); static QStack<State> initialState();
QPointer<QTextDocument> m_document;
QStack<State> m_beginState; QStack<State> m_beginState;
QStack<State> m_currentState; QStack<State> m_currentState;
QStack<State> m_newStates;
QList<CPlusPlus::Token> m_tokens; QList<CPlusPlus::Token> m_tokens;
QString m_currentLine; QString m_currentLine;
@@ -182,12 +180,10 @@ public:
void setIndentSize(int size); void setIndentSize(int size);
enum CompoundStyle { void setIndentSubstatementBraces(bool onOff);
QtStyle, // don't indent braces, add indent for contained statements void setIndentSubstatementStatements(bool onOff);
WhitesmithsStyle, // add indent for braces, don't for the contained statements void setIndentDeclarationBraces(bool onOff);
GnuStyle // add indent for braces and again for contained statements void setIndentDeclarationMembers(bool onOff);
};
void setCompoundStyle(CompoundStyle style);
protected: protected:
virtual void onEnter(int newState, int *indentDepth, int *savedIndentDepth) const; virtual void onEnter(int newState, int *indentDepth, int *savedIndentDepth) const;
@@ -195,7 +191,10 @@ protected:
private: private:
int m_indentSize; int m_indentSize;
CompoundStyle m_style; bool m_indentSubstatementBraces;
bool m_indentSubstatementStatements;
bool m_indentDeclarationBraces;
bool m_indentDeclarationMembers;
}; };
} // namespace CppTools } // namespace CppTools

View File

@@ -40,6 +40,8 @@ private Q_SLOTS:
void memberInitializer(); void memberInitializer();
void templates(); void templates();
void operatorOverloads(); void operatorOverloads();
void gnuStyle();
void whitesmithsStyle();
}; };
struct Line { struct Line {
@@ -73,12 +75,19 @@ QString concatLines(QList<Line> lines)
return result; return result;
} }
void checkIndent(QList<Line> data) void checkIndent(QList<Line> data, int style = 0)
{ {
QString text = concatLines(data); QString text = concatLines(data);
QTextDocument document(text); QTextDocument document(text);
QtStyleCodeFormatter formatter; 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; int i = 0;
foreach (const Line &l, data) { foreach (const Line &l, data) {
@@ -594,6 +603,10 @@ void tst_CodeFormatter::braceList()
<< Line("void foo () {") << Line("void foo () {")
<< Line(" int[] a = { foo, bar, ") << Line(" int[] a = { foo, bar, ")
<< Line(" car };") << Line(" car };")
<< Line(" int[] a = {")
<< Line(" a, b,")
<< Line(" c")
<< Line(" };")
<< Line(" int k;") << Line(" int k;")
; ;
checkIndent(data); checkIndent(data);
@@ -689,6 +702,46 @@ void tst_CodeFormatter::operatorOverloads()
checkIndent(data); 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) QTEST_APPLESS_MAIN(tst_CodeFormatter)
#include "tst_codeformatter.moc" #include "tst_codeformatter.moc"