Skip to content
Snippets Groups Projects
cppcodeformatter.cpp 44.4 KiB
Newer Older
}

void QtStyleCodeFormatter::setIndentDeclarationBraces(bool onOff)
{
    m_indentDeclarationBraces = onOff;
}

void QtStyleCodeFormatter::setIndentDeclarationMembers(bool onOff)
{
    m_indentDeclarationMembers = onOff;
void QtStyleCodeFormatter::saveBlockData(QTextBlock *block, const BlockData &data) const
{
    TextBlockUserData *userData = BaseTextDocumentLayout::userData(*block);
    CppCodeFormatterData *cppData = static_cast<CppCodeFormatterData *>(userData->codeFormatterData());
    if (!cppData) {
        cppData = new CppCodeFormatterData;
        userData->setCodeFormatterData(cppData);
    }
    cppData->m_data = data;
}

bool QtStyleCodeFormatter::loadBlockData(const QTextBlock &block, BlockData *data) const
{
    TextBlockUserData *userData = BaseTextDocumentLayout::testUserData(block);
    if (!userData)
        return false;
    CppCodeFormatterData *cppData = static_cast<CppCodeFormatterData *>(userData->codeFormatterData());
    if (!cppData)
        return false;

    *data = cppData->m_data;
    return true;
}

void QtStyleCodeFormatter::saveLexerState(QTextBlock *block, int state) const
{
mae's avatar
mae committed
    BaseTextDocumentLayout::setLexerState(*block, state);
}

int QtStyleCodeFormatter::loadLexerState(const QTextBlock &block) const
{
mae's avatar
mae committed
    return BaseTextDocumentLayout::lexerState(block);
void QtStyleCodeFormatter::onEnter(int newState, int *indentDepth, int *savedIndentDepth, int *paddingDepth, int *savedPaddingDepth) const
{
    const State &parentState = state();
    const Token &tk = currentToken();
    const bool firstToken = (tokenIndex() == 0);
    const bool lastToken = (tokenIndex() == tokenCount() - 1);
    const int tokenPosition = column(tk.begin());
    const int nextTokenPosition = lastToken ? tokenPosition + tk.length()
                                            : column(tokenAt(tokenIndex() + 1).begin());
    const int spaceOrNextTokenPosition = lastToken ? tokenPosition + tk.length() + 1
                                                   : nextTokenPosition;
    if (shouldClearPaddingOnEnter(newState))
        *paddingDepth = 0;

    switch (newState) {
    case namespace_start:
            *savedIndentDepth = tokenPosition;
        break;

    case enum_start:
    case class_start:
            *savedIndentDepth = tokenPosition;
        *paddingDepth = 2*m_indentSize;
        break;

    case template_param:
        if (!lastToken)
            *paddingDepth = nextTokenPosition-*indentDepth;
        else {
            if (*paddingDepth == 0)
                *paddingDepth = 2*m_indentSize;
            else
                *paddingDepth += m_indentSize;
        }
        break;

    case statement_with_condition:
    case for_statement:
    case switch_statement:
    case if_statement:
    case return_statement:
        if (firstToken)
            *indentDepth = *savedIndentDepth = tokenPosition;
        *paddingDepth = 2*m_indentSize;
        // continuation indent in function bodies only, to not indent
        // after the return type in "void\nfoo() {}"
        for (int i = 0; state(i).type != topmost_intro; ++i) {
            if (state(i).type == defun_open) {
                *paddingDepth = 2*m_indentSize;
    case arglist_open:
    case condition_paren_open:
        if (!lastToken)
            *paddingDepth = nextTokenPosition-*indentDepth;
            *paddingDepth += m_indentSize;
        break;

    case ternary_op:
        if (!lastToken)
            *paddingDepth = spaceOrNextTokenPosition-*indentDepth;
            *paddingDepth += m_indentSize;
        break;

    case stream_op:
        *paddingDepth = spaceOrNextTokenPosition-*indentDepth;
    case stream_op_cont:
        if (firstToken)
            *savedPaddingDepth = *paddingDepth = spaceOrNextTokenPosition-*indentDepth;

    case member_init_open:
        // undo the continuation indent of the parent
        if (firstToken)
            *paddingDepth = tokenPosition-*indentDepth;
            *paddingDepth = m_indentSize - 2; // they'll get another 2 from member_init
        *paddingDepth += 2; // savedIndentDepth is the position of ':'
        break;

    case member_init_paren_open:
        *paddingDepth += m_indentSize;
        break;

    case case_cont:
        *indentDepth += m_indentSize;
        break;

        // undo the continuation indent of the parent
        bool followedByData = (!lastToken && !tokenAt(tokenIndex() + 1).isComment());
        if (followedByData)
            *savedPaddingDepth = tokenPosition-*indentDepth;
        *indentDepth += m_indentSize;
            *paddingDepth = nextTokenPosition-*indentDepth;
        // undo parent continuation indent
        *savedPaddingDepth = 0;

        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 (!lastToken) {
            if (parentState.type == initializer)
                *savedPaddingDepth = tokenPosition-*indentDepth;
            *paddingDepth = nextTokenPosition-*indentDepth;
        } else {
            // avoid existing continuation indents
            if (parentState.type == initializer)
                *savedPaddingDepth = state(1).savedPaddingDepth;
            *paddingDepth = *savedPaddingDepth + m_indentSize;
        break;

    case block_open:
        // case_cont already adds some indent, revert it for a block
        if (parentState.type == case_cont && !m_indentSubstatementBraces)
            *indentDepth = *savedIndentDepth = parentState.savedIndentDepth;

        if (m_indentSubstatementStatements)
            *indentDepth += m_indentSize;
        break;

    case condition_open:
        // undo the continuation indent of the parent
        *paddingDepth = parentState.savedPaddingDepth;
        *savedPaddingDepth = *paddingDepth;

        // fixed extra indent when continuing 'if (', but not for 'else if ('
        if (nextTokenPosition-*indentDepth <= m_indentSize)
            *paddingDepth = 2*m_indentSize;
            *paddingDepth = nextTokenPosition-*indentDepth;
        break;

    case substatement:
        // undo the continuation indent of the parent
        break;

    case maybe_else: {
        // set indent to outermost braceless savedIndent
        int outermostBraceless = 0;
        while (isBracelessState(state(outermostBraceless).type))
            ++outermostBraceless;
        *indentDepth = state(outermostBraceless - 1).savedIndentDepth;
        // this is where the else should go, if one appears - aligned to if_statement
        *savedIndentDepth = state().savedIndentDepth;
    }   break;

    case for_statement_paren_open:
        *paddingDepth = nextTokenPosition - *indentDepth;
        break;

    case multiline_comment_start:
        *indentDepth = tokenPosition + 2; // nextTokenPosition won't work
        break;

    case multiline_comment_cont:
        *indentDepth = tokenPosition;
        break;

    case cpp_macro:
    case cpp_macro_cont:
        *indentDepth = m_indentSize;
        break;
    }

    // ensure padding and indent are >= 0
    *indentDepth = qMax(0, *indentDepth);
    *savedIndentDepth = qMax(0, *savedIndentDepth);
    *paddingDepth = qMax(0, *paddingDepth);
    *savedPaddingDepth = qMax(0, *savedPaddingDepth);
void QtStyleCodeFormatter::adjustIndent(const QList<CPlusPlus::Token> &tokens, int lexerState, int *indentDepth, int *paddingDepth) const
{
    State topState = state();
    State previousState = state(1);

    const bool topWasMaybeElse = (topState.type == maybe_else);
    if (topWasMaybeElse) {
        int outermostBraceless = 1;
        while (state(outermostBraceless).type != invalid && isBracelessState(state(outermostBraceless).type))
            ++outermostBraceless;

        topState = state(outermostBraceless);
        previousState = state(outermostBraceless + 1);
    }


    // adjusting the indentDepth here instead of in enter() gives 'else if' the correct indentation
    // ### could be moved?
    if (topState.type == substatement)
        *indentDepth += m_indentSize;

    // keep user-adjusted indent in multiline comments
    if (topState.type == multiline_comment_start
            || topState.type == multiline_comment_cont) {
        if (!tokens.isEmpty()) {
            *indentDepth = tokens.at(0).begin();
            return;
        }
    }

    const int kind = tokenAt(0).kind();
    switch (kind) {
    case T_POUND: *indentDepth = 0; break;
    case T_COLON:
        // ### ok for constructor initializer lists - what about ? and bitfields?
        if (topState.type == expression && previousState.type == declaration_start) {
        } else if (topState.type == ternary_op) {
            if (*paddingDepth >= 2)
                *paddingDepth -= 2;
            else
                *paddingDepth = 0;
        }
        break;
    case T_LBRACE: {
        if (topState.type == case_cont) {
            *indentDepth = topState.savedIndentDepth;
            if (m_indentSubstatementBraces)
                *indentDepth += m_indentSize;
        // function definition - argument list is expression state
        } else if (topState.type == expression && previousState.type == declaration_start) {
            *indentDepth = previousState.savedIndentDepth;
            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
                && topState.type != substatement_open
                && topState.type != brace_list_open
            *indentDepth = topState.savedIndentDepth;
        break;
    }
    case T_RBRACE: {
        if (topState.type == block_open && previousState.type == case_cont) {
            *indentDepth = topState.savedIndentDepth;
            *paddingDepth = topState.savedPaddingDepth;
            break;
        }
        for (int i = 0; state(i).type != topmost_intro; ++i) {
            const int type = state(i).type;
                    || type == namespace_open
                    || type == enum_open
                    || type == defun_open) {
                *indentDepth = state(i).savedIndentDepth;
                if (m_indentDeclarationBraces)
                    *indentDepth += m_indentSize;
                *paddingDepth = state(i).savedPaddingDepth;
                break;
            } else if (type == substatement_open
                       || type == brace_list_open
                       || type == block_open) {
                *indentDepth = state(i).savedIndentDepth;
                *paddingDepth = state(i).savedPaddingDepth;
    // Disabled for now, see QTCREATORBUG-1825. It makes extending if conditions
    // awkward: inserting a newline just before the ) shouldn't align to 'if'.
    //case T_RPAREN:
    //    if (topState.type == condition_open) {
    //        *indentDepth = previousState.savedIndentDepth;
    //    }
    //    break;
    case T_DEFAULT:
    case T_CASE: {
        int lastSubstatementIndent = 0;
        for (int i = 0; state(i).type != topmost_intro; ++i) {
            const int type = state(i).type;
            if (type == substatement_open) {
                lastSubstatementIndent = state(i).savedIndentDepth;
            } else if (type == switch_statement) {
                *indentDepth = lastSubstatementIndent;
                break;
            } else if (type == case_cont) {
                *indentDepth = state(i).savedIndentDepth;
                break;
            } else if (type == topmost_intro) {
                break;
            }
        }
        break;
    case T_PUBLIC:
    case T_PRIVATE:
    case T_PROTECTED:
    case T_Q_SIGNALS:
        if (topState.type == class_open) {
            if (tokenAt(1).is(T_COLON) || tokenAt(2).is(T_COLON))
                *indentDepth = topState.savedIndentDepth;
        }
        break;
    case T_ELSE:
        if (topWasMaybeElse)
            *indentDepth = state().savedIndentDepth; // topSavedIndent is actually the previous
        break;
    case T_LESS_LESS:
    case T_GREATER_GREATER:
        if (topState.type == stream_op || topState.type == stream_op_cont) {
            if (*paddingDepth >= 3)
                *paddingDepth -= 3; // to align << with <<
            else
                *paddingDepth = 0;
        }
        break;
    case T_COMMENT:
    case T_DOXY_COMMENT:
    case T_CPP_COMMENT:
    case T_CPP_DOXY_COMMENT:
        // unindent the last line of a comment
        if ((topState.type == multiline_comment_cont
             || topState.type == multiline_comment_start)
                && (kind == T_COMMENT || kind == T_DOXY_COMMENT)
                && (lexerState == Lexer::State_Default
                    || tokens.size() != 1)) {
            if (*indentDepth >= m_indentSize)
                *indentDepth -= m_indentSize;
            else
                *indentDepth = 0;
    case T_IDENTIFIER:
        if (topState.type == substatement
                || topState.type == substatement_open
                || topState.type == case_cont
                || topState.type == block_open
                || topState.type == defun_open) {
            if (tokens.size() > 1 && tokens.at(1).kind() == T_COLON) // label?
                *indentDepth = 0;
        }
        break;

bool QtStyleCodeFormatter::shouldClearPaddingOnEnter(int state)
{
    switch (state) {
    case defun_open:
    case class_start:
    case class_open:
    case enum_start:
    case enum_open:
    case namespace_start:
    case namespace_open:
    case template_start:
    case if_statement:
    case else_clause:
    case for_statement:
    case switch_statement:
    case statement_with_condition:
    case do_statement:
    case return_statement:
    case block_open:
    case substatement_open: