2010-07-09 15:47:07 +02:00
|
|
|
/**************************************************************************
|
|
|
|
|
**
|
|
|
|
|
** This file is part of Qt Creator
|
|
|
|
|
**
|
|
|
|
|
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
|
|
|
|
**
|
|
|
|
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
|
|
|
|
**
|
|
|
|
|
** Commercial Usage
|
|
|
|
|
**
|
|
|
|
|
** Licensees holding valid Qt Commercial licenses may use this file in
|
|
|
|
|
** accordance with the Qt Commercial License Agreement provided with the
|
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
|
|
|
** a written agreement between you and Nokia.
|
|
|
|
|
**
|
|
|
|
|
** GNU Lesser General Public License Usage
|
|
|
|
|
**
|
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
|
|
|
|
** General Public License version 2.1 as published by the Free Software
|
|
|
|
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
|
|
|
|
** packaging of this file. Please review the following information to
|
|
|
|
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
|
|
|
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
|
|
|
**
|
|
|
|
|
** If you are unsure which license is appropriate for your use, please
|
|
|
|
|
** contact the sales department at http://qt.nokia.com/contact.
|
|
|
|
|
**
|
|
|
|
|
**************************************************************************/
|
|
|
|
|
|
2010-07-02 15:43:34 +02:00
|
|
|
#include "cppcodeformatter.h"
|
|
|
|
|
|
|
|
|
|
#include <Token.h>
|
|
|
|
|
#include <Lexer.h>
|
|
|
|
|
|
|
|
|
|
#include <texteditor/basetextdocumentlayout.h>
|
2010-08-12 14:00:15 +02:00
|
|
|
#include <texteditor/tabsettings.h>
|
2010-07-02 15:43:34 +02:00
|
|
|
|
|
|
|
|
#include <QtCore/QDebug>
|
|
|
|
|
#include <QtGui/QTextDocument>
|
|
|
|
|
#include <QtGui/QTextCursor>
|
|
|
|
|
#include <QtGui/QTextBlock>
|
|
|
|
|
|
|
|
|
|
using namespace CPlusPlus;
|
|
|
|
|
using namespace CppTools;
|
|
|
|
|
using namespace TextEditor;
|
|
|
|
|
using namespace CppTools::Internal;
|
|
|
|
|
|
2010-07-07 11:01:38 +02:00
|
|
|
CodeFormatter::BlockData::BlockData()
|
|
|
|
|
: m_blockRevision(-1)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-02 15:43:34 +02:00
|
|
|
CodeFormatter::CodeFormatter()
|
2010-07-05 12:56:37 +02:00
|
|
|
: m_indentDepth(0)
|
2010-07-05 13:39:23 +02:00
|
|
|
, m_tabSize(4)
|
2010-07-02 15:43:34 +02:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CodeFormatter::~CodeFormatter()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-05 13:39:23 +02:00
|
|
|
void CodeFormatter::setTabSize(int tabSize)
|
|
|
|
|
{
|
|
|
|
|
m_tabSize = tabSize;
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-02 15:43:34 +02:00
|
|
|
void CodeFormatter::recalculateStateAfter(const QTextBlock &block)
|
|
|
|
|
{
|
2010-07-07 11:01:38 +02:00
|
|
|
restoreCurrentState(block.previous());
|
2010-07-02 15:43:34 +02:00
|
|
|
|
|
|
|
|
bool endedJoined = false;
|
|
|
|
|
const int lexerState = tokenizeBlock(block, &endedJoined);
|
|
|
|
|
m_tokenIndex = 0;
|
2010-07-05 12:56:37 +02:00
|
|
|
m_newStates.clear();
|
2010-07-02 15:43:34 +02:00
|
|
|
|
|
|
|
|
if (tokenAt(0).kind() == T_POUND) {
|
|
|
|
|
enter(cpp_macro_start);
|
|
|
|
|
m_tokenIndex = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (; m_tokenIndex < m_tokens.size(); ) {
|
|
|
|
|
m_currentToken = tokenAt(m_tokenIndex);
|
|
|
|
|
const int kind = m_currentToken.kind();
|
|
|
|
|
|
|
|
|
|
switch (m_currentState.top().type) {
|
|
|
|
|
case topmost_intro:
|
2010-07-08 08:35:12 +02:00
|
|
|
tryDeclaration();
|
|
|
|
|
break;
|
2010-07-02 15:43:34 +02:00
|
|
|
|
|
|
|
|
case namespace_start:
|
|
|
|
|
switch (kind) {
|
2010-07-05 12:56:37 +02:00
|
|
|
case T_LBRACE: enter(namespace_open); break;
|
|
|
|
|
case T_RBRACE: leave(); break;
|
2010-07-02 15:43:34 +02:00
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
case namespace_open:
|
|
|
|
|
if (tryDeclaration())
|
|
|
|
|
break;
|
|
|
|
|
switch (kind) {
|
2010-07-05 12:56:37 +02:00
|
|
|
case T_RBRACE: leave(); continue; // always nested in namespace_start
|
2010-07-02 15:43:34 +02:00
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
case class_start:
|
|
|
|
|
switch (kind) {
|
|
|
|
|
case T_SEMICOLON: leave(); break;
|
2010-07-05 12:56:37 +02:00
|
|
|
case T_LBRACE: enter(class_open); break;
|
2010-07-02 15:43:34 +02:00
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
case class_open:
|
|
|
|
|
if (tryDeclaration())
|
|
|
|
|
break;
|
|
|
|
|
switch (kind) {
|
2010-07-05 12:56:37 +02:00
|
|
|
case T_RBRACE: leave(); continue; // always nested in class_start
|
2010-07-02 15:43:34 +02:00
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
case enum_start:
|
|
|
|
|
switch (kind) {
|
|
|
|
|
case T_SEMICOLON: leave(); break;
|
2010-07-05 12:56:37 +02:00
|
|
|
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;
|
2010-07-02 15:43:34 +02:00
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
case brace_list_open:
|
|
|
|
|
switch (kind) {
|
|
|
|
|
case T_RBRACE: leave(); break;
|
2010-07-05 12:56:37 +02:00
|
|
|
case T_LBRACE: enter(brace_list_open); break;
|
2010-07-02 15:43:34 +02:00
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
case using_start:
|
|
|
|
|
switch (kind) {
|
|
|
|
|
case T_SEMICOLON: leave(); break;
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
case template_start:
|
|
|
|
|
switch (kind) {
|
|
|
|
|
case T_LESS: turnInto(template_param); break;
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
case template_param:
|
|
|
|
|
switch (kind) {
|
|
|
|
|
case T_LESS: enter(template_param); break;
|
|
|
|
|
case T_GREATER: leave(); break;
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
case operator_declaration:
|
|
|
|
|
switch (kind) {
|
|
|
|
|
case T_LPAREN: break;
|
|
|
|
|
default: leave(); break;
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
case declaration_start:
|
|
|
|
|
if (tryExpression(true))
|
|
|
|
|
break;
|
|
|
|
|
switch (kind) {
|
2010-07-05 12:56:37 +02:00
|
|
|
case T_RBRACE:
|
2010-07-02 15:43:34 +02:00
|
|
|
case T_SEMICOLON: leave(true); break;
|
|
|
|
|
case T_EQUAL: enter(initializer); break;
|
2010-07-05 12:56:37 +02:00
|
|
|
case T_LBRACE: enter(defun_open); break;
|
2010-07-16 10:27:15 +02:00
|
|
|
case T_COLON: enter(member_init_open); enter(member_init); break;
|
2010-07-02 15:43:34 +02:00
|
|
|
case T_OPERATOR: enter(operator_declaration); break;
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
case initializer:
|
|
|
|
|
switch (kind) {
|
|
|
|
|
case T_LBRACE: enter(brace_list_open); break;
|
|
|
|
|
default: turnInto(expression); continue;
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
case expression:
|
|
|
|
|
if (tryExpression())
|
|
|
|
|
break;
|
|
|
|
|
switch (kind) {
|
|
|
|
|
case T_SEMICOLON: leave(); continue;
|
|
|
|
|
case T_LBRACE:
|
|
|
|
|
case T_COLON:
|
|
|
|
|
if (m_currentState.at(m_currentState.size() - 2).type == declaration_start) {
|
|
|
|
|
// oops, the expression was a function declaration argument list, hand lbrace/colon to declaration_start
|
|
|
|
|
leave();
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
case arglist_open:
|
|
|
|
|
if (tryExpression())
|
|
|
|
|
break;
|
|
|
|
|
switch (kind) {
|
|
|
|
|
case T_RPAREN: leave(); break;
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
case ternary_op:
|
|
|
|
|
if (tryExpression())
|
|
|
|
|
break;
|
|
|
|
|
switch (kind) {
|
|
|
|
|
case T_RPAREN:
|
|
|
|
|
case T_COMMA:
|
|
|
|
|
case T_SEMICOLON: leave(); continue; // always nested, propagate
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
case stream_op:
|
2010-07-15 16:14:22 +02:00
|
|
|
case stream_op_cont:
|
|
|
|
|
if (kind != T_LESS_LESS && kind != T_GREATER_GREATER && tryExpression())
|
2010-07-02 15:43:34 +02:00
|
|
|
break;
|
|
|
|
|
switch (kind) {
|
2010-07-15 16:14:22 +02:00
|
|
|
case T_LESS_LESS:
|
|
|
|
|
case T_GREATER_GREATER:
|
|
|
|
|
if (m_currentState.top().type == stream_op)
|
|
|
|
|
enter(stream_op_cont);
|
|
|
|
|
else // stream_op_cont already
|
|
|
|
|
turnInto(stream_op_cont);
|
|
|
|
|
break;
|
2010-07-02 15:43:34 +02:00
|
|
|
case T_COMMA:
|
|
|
|
|
case T_SEMICOLON: leave(); continue; // always nested, propagate semicolon
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
case member_init_open:
|
|
|
|
|
switch (kind) {
|
|
|
|
|
case T_LBRACE: turnInto(defun_open); break;
|
2010-07-16 10:27:15 +02:00
|
|
|
case T_COMMA: enter(member_init); break;
|
|
|
|
|
case T_SEMICOLON: leave(); continue; // try to recover
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
case member_init:
|
|
|
|
|
switch (kind) {
|
|
|
|
|
case T_LPAREN: enter(member_init_paren_open); break;
|
|
|
|
|
case T_RPAREN: leave(); break;
|
|
|
|
|
case T_LBRACE:
|
|
|
|
|
case T_SEMICOLON: leave(); continue; // try to recover
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
case member_init_paren_open:
|
|
|
|
|
if (tryExpression())
|
|
|
|
|
break;
|
|
|
|
|
switch (kind) {
|
|
|
|
|
case T_RPAREN: leave(); continue;
|
|
|
|
|
case T_SEMICOLON: leave(); continue; // try to recover
|
2010-07-02 15:43:34 +02:00
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
case defun_open:
|
|
|
|
|
if (tryStatement())
|
|
|
|
|
break;
|
|
|
|
|
switch (kind) {
|
2010-07-05 12:56:37 +02:00
|
|
|
case T_RBRACE: leave(); continue; // always nested in declaration_start
|
2010-07-02 15:43:34 +02:00
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
case switch_statement:
|
|
|
|
|
case statement_with_condition:
|
|
|
|
|
case if_statement:
|
|
|
|
|
switch (kind) {
|
|
|
|
|
case T_LPAREN: enter(condition_open); break;
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
case maybe_else:
|
|
|
|
|
if (m_currentToken.isComment()) {
|
|
|
|
|
break;
|
|
|
|
|
} else if (kind == T_ELSE) {
|
|
|
|
|
turnInto(else_clause);
|
|
|
|
|
enter(substatement);
|
|
|
|
|
break;
|
|
|
|
|
} else {
|
|
|
|
|
leave(true);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case else_clause:
|
|
|
|
|
// ### shouldn't happen
|
|
|
|
|
dump();
|
|
|
|
|
Q_ASSERT(false);
|
|
|
|
|
leave(true);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case do_statement:
|
|
|
|
|
// ### shouldn't happen
|
|
|
|
|
dump();
|
|
|
|
|
Q_ASSERT(false);
|
|
|
|
|
leave(true);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case return_statement:
|
|
|
|
|
switch (kind) {
|
|
|
|
|
case T_SEMICOLON: leave(true); break;
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
case substatement:
|
|
|
|
|
// prefer substatement_open over block_open
|
|
|
|
|
if (kind != T_LBRACE && tryStatement())
|
|
|
|
|
break;
|
|
|
|
|
switch (kind) {
|
|
|
|
|
case T_LBRACE: turnInto(substatement_open); break;
|
|
|
|
|
case T_SEMICOLON: leave(true); break;
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
case for_statement:
|
|
|
|
|
switch (kind) {
|
|
|
|
|
case T_LPAREN: enter(for_statement_paren_open); break;
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
case for_statement_paren_open:
|
|
|
|
|
enter(for_statement_init); continue;
|
|
|
|
|
|
|
|
|
|
case for_statement_init:
|
|
|
|
|
switch (kind) {
|
|
|
|
|
case T_SEMICOLON: turnInto(for_statement_condition); break;
|
|
|
|
|
case T_LPAREN: enter(condition_paren_open); break;
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
case for_statement_condition:
|
|
|
|
|
switch (kind) {
|
|
|
|
|
case T_SEMICOLON: turnInto(for_statement_expression); break;
|
|
|
|
|
case T_LPAREN: enter(condition_paren_open); break;
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
case for_statement_expression:
|
|
|
|
|
switch (kind) {
|
|
|
|
|
case T_RPAREN: leave(); turnInto(substatement); break;
|
|
|
|
|
case T_LPAREN: enter(condition_paren_open); break;
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
case case_start:
|
|
|
|
|
switch (kind) {
|
|
|
|
|
case T_COLON: turnInto(case_cont); break;
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
case case_cont:
|
|
|
|
|
if (kind != T_CASE && kind != T_DEFAULT && tryStatement())
|
|
|
|
|
break;
|
|
|
|
|
switch (kind) {
|
|
|
|
|
case T_RBRACE: leave(); continue;
|
|
|
|
|
case T_DEFAULT:
|
|
|
|
|
case T_CASE: leave(); continue;
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
case substatement_open:
|
|
|
|
|
if (tryStatement())
|
|
|
|
|
break;
|
|
|
|
|
switch (kind) {
|
|
|
|
|
case T_RBRACE: leave(true); break;
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
case condition_open:
|
|
|
|
|
switch (kind) {
|
|
|
|
|
case T_RPAREN: turnInto(substatement); break;
|
|
|
|
|
case T_LPAREN: enter(condition_paren_open); break;
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
case block_open:
|
|
|
|
|
if (tryStatement())
|
|
|
|
|
break;
|
|
|
|
|
switch(kind) {
|
|
|
|
|
case T_RBRACE: leave(true); break;
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
// paren nesting
|
|
|
|
|
case condition_paren_open:
|
|
|
|
|
switch (kind) {
|
|
|
|
|
case T_RPAREN: leave(); break;
|
|
|
|
|
case T_LPAREN: enter(condition_paren_open); break;
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
case qt_like_macro:
|
|
|
|
|
switch (kind) {
|
|
|
|
|
case T_LPAREN: enter(arglist_open); break;
|
|
|
|
|
case T_SEMICOLON: leave(true); break;
|
|
|
|
|
default: leave(); continue;
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
case multiline_comment_start:
|
|
|
|
|
case multiline_comment_cont:
|
|
|
|
|
if (kind != T_COMMENT && kind != T_DOXY_COMMENT) {
|
|
|
|
|
leave();
|
|
|
|
|
continue;
|
|
|
|
|
} else if (m_tokenIndex == m_tokens.size() - 1
|
|
|
|
|
&& lexerState == Lexer::State_Default) {
|
|
|
|
|
leave();
|
|
|
|
|
} else if (m_tokenIndex == 0 && m_currentToken.isComment()) {
|
|
|
|
|
// to allow enter/leave to update the indentDepth
|
|
|
|
|
turnInto(multiline_comment_cont);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case cpp_macro_start: {
|
|
|
|
|
const int size = m_currentState.size();
|
|
|
|
|
|
|
|
|
|
int previousMarker = -1;
|
|
|
|
|
int previousPreviousMarker = -1;
|
|
|
|
|
for (int i = size - 1; i >= 0; --i) {
|
|
|
|
|
if (m_currentState.at(i).type == cpp_macro_conditional) {
|
|
|
|
|
if (previousMarker == -1)
|
|
|
|
|
previousMarker = i;
|
|
|
|
|
else {
|
|
|
|
|
previousPreviousMarker = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QStringRef tokenText = currentTokenText();
|
|
|
|
|
if (tokenText == QLatin1String("ifdef")
|
|
|
|
|
|| tokenText == QLatin1String("if")
|
|
|
|
|
|| tokenText == QLatin1String("ifndef")) {
|
|
|
|
|
enter(cpp_macro_conditional);
|
|
|
|
|
// copy everything right of previousMarker, excluding cpp_macro_conditional
|
|
|
|
|
for (int i = previousMarker + 1; i < size; ++i)
|
|
|
|
|
m_currentState += m_currentState.at(i);
|
|
|
|
|
}
|
|
|
|
|
if (previousMarker != -1) {
|
|
|
|
|
if (tokenText == QLatin1String("endif")) {
|
|
|
|
|
QStack<State>::iterator begin = m_currentState.begin() + previousPreviousMarker + 1;
|
|
|
|
|
QStack<State>::iterator end = m_currentState.begin() + previousMarker + 1;
|
|
|
|
|
m_currentState.erase(begin, end);
|
|
|
|
|
} else if (tokenText == QLatin1String("else")
|
|
|
|
|
|| tokenText == QLatin1String("elif")) {
|
|
|
|
|
m_currentState.resize(previousMarker + 1);
|
|
|
|
|
for (int i = previousPreviousMarker + 1; i < previousMarker; ++i)
|
|
|
|
|
m_currentState += m_currentState.at(i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
turnInto(cpp_macro);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case cpp_macro:
|
|
|
|
|
case cpp_macro_cont:
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
qWarning() << "Unhandled state" << m_currentState.top().type;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
} // end of state switch
|
|
|
|
|
|
|
|
|
|
++m_tokenIndex;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int topState = m_currentState.top().type;
|
|
|
|
|
|
|
|
|
|
if (topState != multiline_comment_start
|
|
|
|
|
&& topState != multiline_comment_cont
|
|
|
|
|
&& (lexerState == Lexer::State_MultiLineComment
|
|
|
|
|
|| lexerState == Lexer::State_MultiLineDoxyComment)) {
|
|
|
|
|
enter(multiline_comment_start);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (topState == qt_like_macro)
|
|
|
|
|
leave(true);
|
|
|
|
|
|
|
|
|
|
if ((topState == cpp_macro_cont
|
|
|
|
|
|| topState == cpp_macro) && !endedJoined)
|
|
|
|
|
leave();
|
|
|
|
|
|
|
|
|
|
if (topState == cpp_macro && endedJoined)
|
|
|
|
|
turnInto(cpp_macro_cont);
|
|
|
|
|
|
2010-07-07 11:01:38 +02:00
|
|
|
saveCurrentState(block);
|
2010-07-02 15:43:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int CodeFormatter::indentFor(const QTextBlock &block)
|
|
|
|
|
{
|
|
|
|
|
// qDebug() << "indenting for" << block.blockNumber() + 1;
|
|
|
|
|
|
2010-07-07 11:01:38 +02:00
|
|
|
restoreCurrentState(block.previous());
|
2010-07-02 15:43:34 +02:00
|
|
|
correctIndentation(block);
|
|
|
|
|
return m_indentDepth;
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-08 16:18:58 +02:00
|
|
|
int CodeFormatter::indentForNewLineAfter(const QTextBlock &block)
|
|
|
|
|
{
|
|
|
|
|
restoreCurrentState(block);
|
|
|
|
|
return m_indentDepth;
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-06 13:52:49 +02:00
|
|
|
void CodeFormatter::updateStateUntil(const QTextBlock &endBlock)
|
2010-07-02 15:43:34 +02:00
|
|
|
{
|
|
|
|
|
QStack<State> previousState = initialState();
|
2010-07-05 12:56:37 +02:00
|
|
|
QTextBlock it = endBlock.document()->firstBlock();
|
2010-07-06 14:41:34 +02:00
|
|
|
|
|
|
|
|
// find the first block that needs recalculation
|
2010-07-02 15:43:34 +02:00
|
|
|
for (; it.isValid() && it != endBlock; it = it.next()) {
|
2010-07-07 11:01:38 +02:00
|
|
|
BlockData blockData;
|
|
|
|
|
if (!loadBlockData(it, &blockData))
|
2010-07-02 15:43:34 +02:00
|
|
|
break;
|
2010-07-07 11:01:38 +02:00
|
|
|
if (blockData.m_blockRevision != it.revision())
|
2010-07-02 15:43:34 +02:00
|
|
|
break;
|
2010-07-08 10:18:24 +02:00
|
|
|
if (previousState.isEmpty() || blockData.m_beginState.isEmpty()
|
|
|
|
|
|| previousState != blockData.m_beginState)
|
2010-07-02 15:43:34 +02:00
|
|
|
break;
|
2010-07-07 11:01:38 +02:00
|
|
|
if (loadLexerState(it) == -1)
|
2010-07-02 15:43:34 +02:00
|
|
|
break;
|
|
|
|
|
|
2010-07-07 11:01:38 +02:00
|
|
|
previousState = blockData.m_endState;
|
2010-07-02 15:43:34 +02:00
|
|
|
}
|
2010-07-06 14:41:34 +02:00
|
|
|
|
|
|
|
|
if (it == endBlock)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// update everthing until endBlock
|
2010-07-02 15:43:34 +02:00
|
|
|
for (; it.isValid() && it != endBlock; it = it.next()) {
|
|
|
|
|
recalculateStateAfter(it);
|
|
|
|
|
}
|
2010-07-06 14:41:34 +02:00
|
|
|
|
|
|
|
|
// invalidate everything below by marking the state in endBlock as invalid
|
2010-07-07 11:01:38 +02:00
|
|
|
if (it.isValid()) {
|
|
|
|
|
BlockData invalidBlockData;
|
|
|
|
|
saveBlockData(&it, invalidBlockData);
|
|
|
|
|
}
|
2010-07-06 14:41:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CodeFormatter::updateLineStateChange(const QTextBlock &block)
|
|
|
|
|
{
|
|
|
|
|
if (!block.isValid())
|
|
|
|
|
return;
|
|
|
|
|
|
2010-07-07 11:01:38 +02:00
|
|
|
BlockData blockData;
|
|
|
|
|
if (loadBlockData(block, &blockData) && blockData.m_blockRevision == block.revision())
|
|
|
|
|
return;
|
2010-07-06 14:41:34 +02:00
|
|
|
|
|
|
|
|
recalculateStateAfter(block);
|
|
|
|
|
|
2010-07-07 11:01:38 +02:00
|
|
|
// invalidate everything below by marking the next block's state as invalid
|
|
|
|
|
QTextBlock next = block.next();
|
|
|
|
|
if (!next.isValid())
|
|
|
|
|
return;
|
2010-07-06 14:41:34 +02:00
|
|
|
|
2010-07-07 11:01:38 +02:00
|
|
|
saveBlockData(&next, BlockData());
|
2010-07-02 15:43:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CodeFormatter::State CodeFormatter::state(int belowTop) const
|
|
|
|
|
{
|
|
|
|
|
if (belowTop < m_currentState.size())
|
|
|
|
|
return m_currentState.at(m_currentState.size() - 1 - belowTop);
|
|
|
|
|
else
|
|
|
|
|
return State();
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-05 12:56:37 +02:00
|
|
|
const QVector<CodeFormatter::State> &CodeFormatter::newStatesThisLine() const
|
|
|
|
|
{
|
|
|
|
|
return m_newStates;
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-02 15:43:34 +02:00
|
|
|
int CodeFormatter::tokenIndex() const
|
|
|
|
|
{
|
|
|
|
|
return m_tokenIndex;
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-05 12:56:37 +02:00
|
|
|
int CodeFormatter::tokenCount() const
|
2010-07-02 15:43:34 +02:00
|
|
|
{
|
2010-07-05 12:56:37 +02:00
|
|
|
return m_tokens.size();
|
2010-07-02 15:43:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const CPlusPlus::Token &CodeFormatter::currentToken() const
|
|
|
|
|
{
|
|
|
|
|
return m_currentToken;
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-05 12:56:37 +02:00
|
|
|
void CodeFormatter::invalidateCache(QTextDocument *document)
|
2010-07-02 15:43:34 +02:00
|
|
|
{
|
2010-07-05 12:56:37 +02:00
|
|
|
if (!document)
|
|
|
|
|
return;
|
|
|
|
|
|
2010-07-07 11:01:38 +02:00
|
|
|
BlockData invalidBlockData;
|
2010-07-05 12:56:37 +02:00
|
|
|
QTextBlock it = document->firstBlock();
|
2010-07-02 15:43:34 +02:00
|
|
|
for (; it.isValid(); it = it.next()) {
|
2010-07-07 11:01:38 +02:00
|
|
|
saveBlockData(&it, invalidBlockData);
|
2010-07-02 15:43:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CodeFormatter::enter(int newState)
|
|
|
|
|
{
|
|
|
|
|
int savedIndentDepth = m_indentDepth;
|
|
|
|
|
onEnter(newState, &m_indentDepth, &savedIndentDepth);
|
2010-07-05 12:56:37 +02:00
|
|
|
State s(newState, savedIndentDepth);
|
|
|
|
|
m_currentState.push(s);
|
|
|
|
|
m_newStates.push(s);
|
2010-07-02 15:43:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CodeFormatter::leave(bool statementDone)
|
|
|
|
|
{
|
|
|
|
|
Q_ASSERT(m_currentState.size() > 1);
|
|
|
|
|
if (m_currentState.top().type == topmost_intro)
|
|
|
|
|
return;
|
|
|
|
|
|
2010-07-05 12:56:37 +02:00
|
|
|
if (m_newStates.size() > 0)
|
|
|
|
|
m_newStates.pop();
|
|
|
|
|
|
2010-07-02 15:43:34 +02:00
|
|
|
// restore indent depth
|
|
|
|
|
State poppedState = m_currentState.pop();
|
|
|
|
|
m_indentDepth = poppedState.savedIndentDepth;
|
|
|
|
|
|
|
|
|
|
int topState = m_currentState.top().type;
|
|
|
|
|
|
|
|
|
|
// does it suffice to check if token is T_SEMICOLON or T_RBRACE?
|
|
|
|
|
// maybe distinction between leave and turnInto?
|
|
|
|
|
if (statementDone) {
|
|
|
|
|
if (topState == substatement
|
|
|
|
|
|| topState == statement_with_condition
|
|
|
|
|
|| topState == for_statement
|
|
|
|
|
|| topState == switch_statement
|
|
|
|
|
|| topState == do_statement) {
|
|
|
|
|
leave(true);
|
|
|
|
|
} else if (topState == if_statement) {
|
|
|
|
|
if (poppedState.type != maybe_else)
|
|
|
|
|
enter(maybe_else);
|
|
|
|
|
else
|
|
|
|
|
leave(true);
|
|
|
|
|
} else if (topState == else_clause) {
|
|
|
|
|
// leave the else *and* the surrounding if, to prevent another else
|
|
|
|
|
leave();
|
|
|
|
|
leave(true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CodeFormatter::correctIndentation(const QTextBlock &block)
|
|
|
|
|
{
|
|
|
|
|
const int lexerState = tokenizeBlock(block);
|
|
|
|
|
Q_ASSERT(m_currentState.size() >= 1);
|
|
|
|
|
|
|
|
|
|
adjustIndent(m_tokens, lexerState, &m_indentDepth);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CodeFormatter::tryExpression(bool alsoExpression)
|
|
|
|
|
{
|
|
|
|
|
int newState = -1;
|
|
|
|
|
|
|
|
|
|
const int kind = m_currentToken.kind();
|
|
|
|
|
switch (kind) {
|
|
|
|
|
case T_LPAREN: newState = arglist_open; break;
|
|
|
|
|
case T_QUESTION: newState = ternary_op; break;
|
|
|
|
|
|
|
|
|
|
case T_LESS_LESS:
|
|
|
|
|
case T_GREATER_GREATER:
|
|
|
|
|
newState = stream_op;
|
|
|
|
|
for (int i = m_currentState.size() - 1; i >= 0; --i) {
|
|
|
|
|
const int type = m_currentState.at(i).type;
|
2010-07-15 16:14:22 +02:00
|
|
|
if (type == arglist_open) { // likely a left-shift instead
|
2010-07-02 15:43:34 +02:00
|
|
|
newState = -1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (type == topmost_intro
|
|
|
|
|
|| type == substatement_open
|
|
|
|
|
|| type == defun_open
|
|
|
|
|
|| type == namespace_open
|
|
|
|
|
|| type == class_open
|
|
|
|
|
|| type == brace_list_open) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (newState != -1) {
|
|
|
|
|
if (alsoExpression)
|
|
|
|
|
enter(expression);
|
|
|
|
|
enter(newState);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CodeFormatter::tryDeclaration()
|
|
|
|
|
{
|
|
|
|
|
const int kind = m_currentToken.kind();
|
|
|
|
|
switch (kind) {
|
|
|
|
|
case T_Q_ENUMS:
|
|
|
|
|
case T_Q_PROPERTY:
|
|
|
|
|
case T_Q_FLAGS:
|
|
|
|
|
case T_Q_GADGET:
|
|
|
|
|
case T_Q_OBJECT:
|
|
|
|
|
case T_Q_INTERFACES:
|
|
|
|
|
case T_Q_DECLARE_INTERFACE:
|
|
|
|
|
case T_Q_PRIVATE_SLOT:
|
|
|
|
|
enter(qt_like_macro);
|
|
|
|
|
return true;
|
|
|
|
|
case T_IDENTIFIER:
|
|
|
|
|
if (m_tokenIndex == 0) {
|
|
|
|
|
QString tokenText = currentTokenText().toString();
|
|
|
|
|
if (tokenText.startsWith(QLatin1String("Q_"))
|
|
|
|
|
|| tokenText.startsWith(QLatin1String("QT_"))
|
2010-08-25 14:54:45 +02:00
|
|
|
|| tokenText.startsWith(QLatin1String("QML_"))
|
2010-07-02 15:43:34 +02:00
|
|
|
|| tokenText.startsWith(QLatin1String("QDOC_"))) {
|
|
|
|
|
enter(qt_like_macro);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// fallthrough
|
|
|
|
|
case T_CHAR:
|
|
|
|
|
case T_WCHAR_T:
|
|
|
|
|
case T_BOOL:
|
|
|
|
|
case T_SHORT:
|
|
|
|
|
case T_INT:
|
|
|
|
|
case T_LONG:
|
|
|
|
|
case T_SIGNED:
|
|
|
|
|
case T_UNSIGNED:
|
|
|
|
|
case T_FLOAT:
|
|
|
|
|
case T_DOUBLE:
|
|
|
|
|
case T_VOID:
|
|
|
|
|
case T_AUTO:
|
|
|
|
|
case T___TYPEOF__:
|
|
|
|
|
case T___ATTRIBUTE__:
|
|
|
|
|
case T_STATIC:
|
|
|
|
|
case T_FRIEND:
|
|
|
|
|
case T_CONST:
|
|
|
|
|
case T_VOLATILE:
|
|
|
|
|
case T_INLINE:
|
|
|
|
|
enter(declaration_start);
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
case T_TEMPLATE:
|
|
|
|
|
enter(template_start);
|
|
|
|
|
return true;
|
|
|
|
|
|
2010-07-08 08:35:12 +02:00
|
|
|
case T_NAMESPACE:
|
|
|
|
|
enter(namespace_start);
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
case T_STRUCT:
|
|
|
|
|
case T_UNION:
|
|
|
|
|
case T_CLASS:
|
|
|
|
|
enter(class_start);
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
case T_ENUM:
|
|
|
|
|
enter(enum_start);
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
case T_USING:
|
|
|
|
|
enter(using_start);
|
|
|
|
|
return true;
|
|
|
|
|
|
2010-07-02 15:43:34 +02:00
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CodeFormatter::tryStatement()
|
|
|
|
|
{
|
|
|
|
|
const int kind = m_currentToken.kind();
|
|
|
|
|
if (tryDeclaration())
|
|
|
|
|
return true;
|
|
|
|
|
switch (kind) {
|
|
|
|
|
case T_RETURN:
|
|
|
|
|
enter(return_statement);
|
|
|
|
|
enter(expression);
|
|
|
|
|
return true;
|
|
|
|
|
case T_FOR:
|
|
|
|
|
enter(for_statement);
|
|
|
|
|
return true;
|
|
|
|
|
case T_SWITCH:
|
|
|
|
|
enter(switch_statement);
|
|
|
|
|
return true;
|
|
|
|
|
case T_IF:
|
|
|
|
|
enter(if_statement);
|
|
|
|
|
return true;
|
|
|
|
|
case T_WHILE:
|
|
|
|
|
case T_Q_FOREACH:
|
|
|
|
|
enter(statement_with_condition);
|
|
|
|
|
return true;
|
|
|
|
|
case T_DO:
|
|
|
|
|
enter(do_statement);
|
|
|
|
|
enter(substatement);
|
|
|
|
|
return true;
|
|
|
|
|
case T_CASE:
|
|
|
|
|
case T_DEFAULT:
|
|
|
|
|
enter(case_start);
|
|
|
|
|
return true;
|
|
|
|
|
case T_LBRACE:
|
|
|
|
|
enter(block_open);
|
|
|
|
|
return true;
|
|
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CodeFormatter::isBracelessState(int type) const
|
|
|
|
|
{
|
|
|
|
|
return type == substatement
|
|
|
|
|
|| type == if_statement
|
|
|
|
|
|| type == else_clause
|
|
|
|
|
|| type == statement_with_condition
|
|
|
|
|
|| type == for_statement
|
|
|
|
|
|| type == do_statement;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const Token &CodeFormatter::tokenAt(int idx) const
|
|
|
|
|
{
|
|
|
|
|
static const Token empty;
|
|
|
|
|
if (idx < 0 || idx >= m_tokens.size())
|
|
|
|
|
return empty;
|
|
|
|
|
else
|
|
|
|
|
return m_tokens.at(idx);
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-05 13:39:23 +02:00
|
|
|
int CodeFormatter::column(int index) const
|
|
|
|
|
{
|
|
|
|
|
int col = 0;
|
|
|
|
|
if (index > m_currentLine.length())
|
|
|
|
|
index = m_currentLine.length();
|
|
|
|
|
|
|
|
|
|
const QChar tab = QLatin1Char('\t');
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < index; i++) {
|
|
|
|
|
if (m_currentLine[i] == tab) {
|
|
|
|
|
col = ((col / m_tabSize) + 1) * m_tabSize;
|
|
|
|
|
} else {
|
|
|
|
|
col++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return col;
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-02 15:43:34 +02:00
|
|
|
QStringRef CodeFormatter::currentTokenText() const
|
|
|
|
|
{
|
|
|
|
|
return m_currentLine.midRef(m_currentToken.begin(), m_currentToken.length());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CodeFormatter::turnInto(int newState)
|
|
|
|
|
{
|
|
|
|
|
leave(false);
|
|
|
|
|
enter(newState);
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-07 11:01:38 +02:00
|
|
|
void CodeFormatter::saveCurrentState(const QTextBlock &block)
|
2010-07-02 15:43:34 +02:00
|
|
|
{
|
|
|
|
|
if (!block.isValid())
|
|
|
|
|
return;
|
|
|
|
|
|
2010-07-07 11:01:38 +02:00
|
|
|
BlockData blockData;
|
|
|
|
|
blockData.m_blockRevision = block.revision();
|
|
|
|
|
blockData.m_beginState = m_beginState;
|
|
|
|
|
blockData.m_endState = m_currentState;
|
|
|
|
|
blockData.m_indentDepth = m_indentDepth;
|
|
|
|
|
|
|
|
|
|
QTextBlock saveableBlock(block);
|
|
|
|
|
saveBlockData(&saveableBlock, blockData);
|
2010-07-02 15:43:34 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-07 11:01:38 +02:00
|
|
|
void CodeFormatter::restoreCurrentState(const QTextBlock &block)
|
2010-07-02 15:43:34 +02:00
|
|
|
{
|
|
|
|
|
if (block.isValid()) {
|
2010-07-07 11:01:38 +02:00
|
|
|
BlockData blockData;
|
|
|
|
|
if (loadBlockData(block, &blockData)) {
|
|
|
|
|
m_indentDepth = blockData.m_indentDepth;
|
|
|
|
|
m_currentState = blockData.m_endState;
|
2010-07-02 15:43:34 +02:00
|
|
|
m_beginState = m_currentState;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_currentState = initialState();
|
|
|
|
|
m_beginState = m_currentState;
|
|
|
|
|
m_indentDepth = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QStack<CodeFormatter::State> CodeFormatter::initialState()
|
|
|
|
|
{
|
|
|
|
|
static QStack<CodeFormatter::State> initialState;
|
|
|
|
|
if (initialState.isEmpty())
|
|
|
|
|
initialState.push(State(topmost_intro, 0));
|
|
|
|
|
return initialState;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int CodeFormatter::tokenizeBlock(const QTextBlock &block, bool *endedJoined)
|
|
|
|
|
{
|
2010-07-07 11:01:38 +02:00
|
|
|
int startState = loadLexerState(block.previous());
|
2010-07-02 15:43:34 +02:00
|
|
|
if (block.blockNumber() == 0)
|
|
|
|
|
startState = 0;
|
|
|
|
|
Q_ASSERT(startState != -1);
|
|
|
|
|
|
|
|
|
|
SimpleLexer tokenize;
|
|
|
|
|
tokenize.setQtMocRunEnabled(true);
|
|
|
|
|
tokenize.setObjCEnabled(true);
|
|
|
|
|
|
|
|
|
|
m_currentLine = block.text();
|
|
|
|
|
// to determine whether a line was joined, Tokenizer needs a
|
|
|
|
|
// newline character at the end
|
|
|
|
|
m_currentLine.append(QLatin1Char('\n'));
|
|
|
|
|
m_tokens = tokenize(m_currentLine, startState);
|
|
|
|
|
|
|
|
|
|
if (endedJoined)
|
|
|
|
|
*endedJoined = tokenize.endedJoined();
|
|
|
|
|
|
|
|
|
|
const int lexerState = tokenize.state();
|
2010-07-12 11:16:10 +02:00
|
|
|
BaseTextDocumentLayout::setLexerState(block, lexerState);
|
2010-07-02 15:43:34 +02:00
|
|
|
return lexerState;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CodeFormatter::dump()
|
|
|
|
|
{
|
|
|
|
|
qDebug() << "Current token index" << m_tokenIndex;
|
|
|
|
|
qDebug() << "Current state:";
|
|
|
|
|
foreach (State s, m_currentState) {
|
|
|
|
|
qDebug() << s.type << s.savedIndentDepth;
|
|
|
|
|
}
|
|
|
|
|
qDebug() << "Current indent depth:" << m_indentDepth;
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-07 11:01:38 +02:00
|
|
|
|
|
|
|
|
namespace CppTools {
|
|
|
|
|
namespace Internal {
|
|
|
|
|
class CppCodeFormatterData: public TextEditor::CodeFormatterData
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
CodeFormatter::BlockData m_data;
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-02 15:43:34 +02:00
|
|
|
QtStyleCodeFormatter::QtStyleCodeFormatter()
|
|
|
|
|
: m_indentSize(4)
|
2010-07-05 12:56:37 +02:00
|
|
|
, m_indentSubstatementBraces(false)
|
|
|
|
|
, m_indentSubstatementStatements(true)
|
|
|
|
|
, m_indentDeclarationBraces(false)
|
|
|
|
|
, m_indentDeclarationMembers(true)
|
2010-07-02 15:43:34 +02:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2010-08-12 14:00:15 +02:00
|
|
|
QtStyleCodeFormatter::QtStyleCodeFormatter(const TextEditor::TabSettings &tabSettings)
|
|
|
|
|
: m_indentSize(tabSettings.m_indentSize)
|
|
|
|
|
, m_indentSubstatementBraces(false)
|
|
|
|
|
, m_indentSubstatementStatements(true)
|
|
|
|
|
, m_indentDeclarationBraces(false)
|
|
|
|
|
, m_indentDeclarationMembers(true)
|
|
|
|
|
{
|
|
|
|
|
setTabSize(tabSettings.m_tabSize);
|
|
|
|
|
if (tabSettings.m_indentBraces && tabSettings.m_doubleIndentBlocks) { // gnu style
|
|
|
|
|
setIndentSubstatementBraces(true);
|
|
|
|
|
setIndentSubstatementStatements(true);
|
|
|
|
|
setIndentDeclarationBraces(false);
|
|
|
|
|
setIndentDeclarationMembers(true);
|
|
|
|
|
} else if (tabSettings.m_indentBraces) { // whitesmiths style
|
|
|
|
|
setIndentSubstatementBraces(true);
|
|
|
|
|
setIndentSubstatementStatements(false);
|
|
|
|
|
setIndentDeclarationBraces(true);
|
|
|
|
|
setIndentDeclarationMembers(false);
|
|
|
|
|
} else { // default Qt style
|
|
|
|
|
setIndentSubstatementBraces(false);
|
|
|
|
|
setIndentSubstatementStatements(true);
|
|
|
|
|
setIndentDeclarationBraces(false);
|
|
|
|
|
setIndentDeclarationMembers(true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-02 15:43:34 +02:00
|
|
|
void QtStyleCodeFormatter::setIndentSize(int size)
|
|
|
|
|
{
|
2010-07-05 12:56:37 +02:00
|
|
|
m_indentSize = size;
|
2010-07-02 15:43:34 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-05 12:56:37 +02:00
|
|
|
void QtStyleCodeFormatter::setIndentSubstatementBraces(bool onOff)
|
2010-07-05 09:59:42 +02:00
|
|
|
{
|
2010-07-05 12:56:37 +02:00
|
|
|
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;
|
2010-07-05 09:59:42 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-07 11:01:38 +02:00
|
|
|
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
|
|
|
|
|
{
|
2010-07-12 11:16:10 +02:00
|
|
|
BaseTextDocumentLayout::setLexerState(*block, state);
|
2010-07-07 11:01:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int QtStyleCodeFormatter::loadLexerState(const QTextBlock &block) const
|
|
|
|
|
{
|
2010-07-12 11:16:10 +02:00
|
|
|
return BaseTextDocumentLayout::lexerState(block);
|
2010-07-07 11:01:38 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-02 15:43:34 +02:00
|
|
|
void QtStyleCodeFormatter::onEnter(int newState, int *indentDepth, int *savedIndentDepth) const
|
|
|
|
|
{
|
|
|
|
|
const State &parentState = state();
|
|
|
|
|
const Token &tk = currentToken();
|
2010-07-05 13:39:23 +02:00
|
|
|
const int tokenPosition = column(tk.begin());
|
2010-07-02 15:43:34 +02:00
|
|
|
const bool firstToken = (tokenIndex() == 0);
|
2010-07-05 12:56:37 +02:00
|
|
|
const bool lastToken = (tokenIndex() == tokenCount() - 1);
|
2010-08-31 09:45:01 +02:00
|
|
|
int nextTokenStart = 0;
|
|
|
|
|
if (!lastToken)
|
|
|
|
|
nextTokenStart = column(tokenAt(tokenIndex() + 1).begin());
|
2010-07-02 15:43:34 +02:00
|
|
|
|
|
|
|
|
switch (newState) {
|
|
|
|
|
case namespace_start:
|
|
|
|
|
if (firstToken)
|
|
|
|
|
*savedIndentDepth = tokenPosition;
|
|
|
|
|
*indentDepth = tokenPosition;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case enum_start:
|
|
|
|
|
case class_start:
|
|
|
|
|
if (firstToken)
|
|
|
|
|
*savedIndentDepth = tokenPosition;
|
|
|
|
|
*indentDepth = tokenPosition + 2*m_indentSize;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case template_param:
|
|
|
|
|
if (!lastToken)
|
|
|
|
|
*indentDepth = tokenPosition + tk.length();
|
|
|
|
|
else
|
|
|
|
|
*indentDepth += 2*m_indentSize;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case statement_with_condition:
|
|
|
|
|
case for_statement:
|
|
|
|
|
case switch_statement:
|
|
|
|
|
case if_statement:
|
|
|
|
|
case return_statement:
|
|
|
|
|
if (firstToken)
|
|
|
|
|
*savedIndentDepth = tokenPosition;
|
|
|
|
|
*indentDepth = *savedIndentDepth + 2*m_indentSize;
|
|
|
|
|
break;
|
|
|
|
|
|
2010-07-08 08:48:36 +02:00
|
|
|
case declaration_start:
|
|
|
|
|
if (firstToken)
|
|
|
|
|
*savedIndentDepth = tokenPosition;
|
|
|
|
|
// 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) {
|
|
|
|
|
*indentDepth = *savedIndentDepth + 2*m_indentSize;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2010-07-02 15:43:34 +02:00
|
|
|
case arglist_open:
|
|
|
|
|
case condition_paren_open:
|
|
|
|
|
if (!lastToken)
|
|
|
|
|
*indentDepth = tokenPosition + 1;
|
|
|
|
|
else
|
|
|
|
|
*indentDepth += m_indentSize;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ternary_op:
|
|
|
|
|
if (!lastToken)
|
|
|
|
|
*indentDepth = tokenPosition + tk.length() + 1;
|
|
|
|
|
else
|
|
|
|
|
*indentDepth += m_indentSize;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case stream_op:
|
|
|
|
|
*indentDepth = tokenPosition + tk.length() + 1;
|
|
|
|
|
break;
|
2010-07-15 16:14:22 +02:00
|
|
|
case stream_op_cont:
|
|
|
|
|
if (firstToken)
|
|
|
|
|
*savedIndentDepth = *indentDepth = tokenPosition + tk.length() + 1;
|
|
|
|
|
break;
|
2010-07-02 15:43:34 +02:00
|
|
|
|
|
|
|
|
case member_init_open:
|
2010-07-05 12:56:37 +02:00
|
|
|
// undo the continuation indent of the parent
|
|
|
|
|
*savedIndentDepth = parentState.savedIndentDepth;
|
|
|
|
|
|
2010-07-02 15:43:34 +02:00
|
|
|
if (firstToken)
|
2010-07-16 10:27:15 +02:00
|
|
|
*indentDepth = tokenPosition;
|
2010-07-02 15:43:34 +02:00
|
|
|
else
|
2010-07-16 10:27:15 +02:00
|
|
|
*indentDepth = *savedIndentDepth + m_indentSize - 2; // they'll get another 2 from member_init
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case member_init:
|
|
|
|
|
*indentDepth = *savedIndentDepth + 2; // savedIndentDepth is the position of ':'
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case member_init_paren_open:
|
|
|
|
|
*indentDepth = *savedIndentDepth + m_indentSize;
|
2010-07-02 15:43:34 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case case_cont:
|
|
|
|
|
*indentDepth += m_indentSize;
|
|
|
|
|
break;
|
|
|
|
|
|
2010-07-05 12:56:37 +02:00
|
|
|
case class_open:
|
|
|
|
|
case enum_open:
|
2010-07-06 13:54:21 +02:00
|
|
|
case defun_open: {
|
2010-07-05 12:56:37 +02:00
|
|
|
// undo the continuation indent of the parent
|
|
|
|
|
*savedIndentDepth = parentState.savedIndentDepth;
|
|
|
|
|
|
2010-07-06 13:54:21 +02:00
|
|
|
bool followedByData = (!lastToken && !tokenAt(tokenIndex() + 1).isComment());
|
|
|
|
|
if (firstToken || followedByData)
|
2010-07-05 12:56:37 +02:00
|
|
|
*savedIndentDepth = tokenPosition;
|
|
|
|
|
|
|
|
|
|
*indentDepth = *savedIndentDepth;
|
|
|
|
|
|
2010-07-06 13:54:21 +02:00
|
|
|
if (followedByData) {
|
|
|
|
|
*indentDepth = column(tokenAt(tokenIndex() + 1).begin());
|
|
|
|
|
} else if (m_indentDeclarationMembers) {
|
2010-07-02 15:43:34 +02:00
|
|
|
*indentDepth += m_indentSize;
|
2010-07-06 13:54:21 +02:00
|
|
|
}
|
2010-07-02 15:43:34 +02:00
|
|
|
break;
|
2010-07-06 13:54:21 +02:00
|
|
|
}
|
2010-07-02 15:43:34 +02:00
|
|
|
|
2010-07-05 12:56:37 +02:00
|
|
|
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;
|
|
|
|
|
|
2010-07-02 15:43:34 +02:00
|
|
|
case brace_list_open:
|
2010-08-31 09:45:01 +02:00
|
|
|
if (!lastToken) {
|
|
|
|
|
if (parentState.type == initializer)
|
|
|
|
|
*savedIndentDepth = tokenPosition;
|
|
|
|
|
*indentDepth = nextTokenStart;
|
|
|
|
|
} else {
|
|
|
|
|
// avoid existing continuation indents
|
|
|
|
|
if (parentState.type == initializer)
|
|
|
|
|
*savedIndentDepth = state(1).savedIndentDepth;
|
2010-07-05 12:56:37 +02:00
|
|
|
*indentDepth = *savedIndentDepth + m_indentSize;
|
|
|
|
|
}
|
2010-07-02 15:43:34 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case block_open:
|
|
|
|
|
if (parentState.type != case_cont)
|
|
|
|
|
*indentDepth += m_indentSize;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case condition_open:
|
|
|
|
|
// undo the continuation indent of the parent
|
|
|
|
|
*indentDepth = parentState.savedIndentDepth;
|
|
|
|
|
*savedIndentDepth = *indentDepth;
|
|
|
|
|
|
|
|
|
|
// fixed extra indent when continuing 'if (', but not for 'else if ('
|
|
|
|
|
if (tokenPosition <= *indentDepth + m_indentSize)
|
|
|
|
|
*indentDepth += 2*m_indentSize;
|
|
|
|
|
else
|
|
|
|
|
*indentDepth = tokenPosition + 1;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case substatement:
|
|
|
|
|
// undo the continuation indent of the parent
|
|
|
|
|
*indentDepth = parentState.savedIndentDepth;
|
|
|
|
|
*savedIndentDepth = *indentDepth;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case maybe_else: {
|
|
|
|
|
// set indent to outermost braceless savedIndent
|
|
|
|
|
int outermostBraceless = 0;
|
|
|
|
|
while (isBracelessState(state(outermostBraceless).type))
|
|
|
|
|
++outermostBraceless;
|
|
|
|
|
*indentDepth = state(outermostBraceless - 1).savedIndentDepth;
|
2010-07-06 09:44:04 +02:00
|
|
|
// this is where the else should go, if one appears - aligned to if_statement
|
|
|
|
|
*savedIndentDepth = state().savedIndentDepth;
|
2010-07-02 15:43:34 +02:00
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
case for_statement_paren_open:
|
|
|
|
|
*indentDepth = tokenPosition + 1;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case multiline_comment_start:
|
|
|
|
|
*indentDepth = tokenPosition + 2;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case multiline_comment_cont:
|
|
|
|
|
*indentDepth = tokenPosition;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case cpp_macro:
|
|
|
|
|
case cpp_macro_cont:
|
|
|
|
|
*indentDepth = m_indentSize;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QtStyleCodeFormatter::adjustIndent(const QList<CPlusPlus::Token> &tokens, int lexerState, int *indentDepth) 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) {
|
|
|
|
|
*indentDepth = previousState.savedIndentDepth + m_indentSize;
|
|
|
|
|
} else if (topState.type == ternary_op) {
|
|
|
|
|
*indentDepth -= 2;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case T_LBRACE: {
|
2010-07-05 12:56:37 +02:00
|
|
|
if (topState.type == case_cont) {
|
2010-07-02 15:43:34 +02:00
|
|
|
*indentDepth = topState.savedIndentDepth;
|
2010-07-05 12:56:37 +02:00
|
|
|
// function definition - argument list is expression state
|
|
|
|
|
} else if (topState.type == expression && previousState.type == declaration_start) {
|
2010-07-02 15:43:34 +02:00
|
|
|
*indentDepth = previousState.savedIndentDepth;
|
2010-07-05 12:56:37 +02:00
|
|
|
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
|
2010-07-02 15:43:34 +02:00
|
|
|
&& topState.type != block_open
|
2010-07-28 12:37:54 +02:00
|
|
|
&& topState.type != substatement_open
|
2010-08-31 09:45:01 +02:00
|
|
|
&& topState.type != brace_list_open
|
2010-07-05 12:56:37 +02:00
|
|
|
&& !topWasMaybeElse) {
|
2010-07-02 15:43:34 +02:00
|
|
|
*indentDepth = topState.savedIndentDepth;
|
2010-07-05 12:56:37 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-02 15:43:34 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case T_RBRACE: {
|
|
|
|
|
if (topState.type == block_open && previousState.type == case_cont) {
|
|
|
|
|
*indentDepth = previousState.savedIndentDepth;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
for (int i = 0; state(i).type != topmost_intro; ++i) {
|
|
|
|
|
const int type = state(i).type;
|
|
|
|
|
if (type == defun_open
|
|
|
|
|
|| type == substatement_open
|
|
|
|
|
|| type == class_open
|
|
|
|
|
|| type == brace_list_open
|
|
|
|
|
|| type == namespace_open
|
2010-07-05 12:56:37 +02:00
|
|
|
|| type == block_open
|
|
|
|
|
|| type == enum_open) {
|
2010-07-02 15:43:34 +02:00
|
|
|
*indentDepth = state(i).savedIndentDepth;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
2010-07-08 13:07:32 +02:00
|
|
|
// 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;
|
2010-07-02 15:43:34 +02:00
|
|
|
case T_DEFAULT:
|
|
|
|
|
case T_CASE:
|
|
|
|
|
for (int i = 0; state(i).type != topmost_intro; ++i) {
|
|
|
|
|
const int type = state(i).type;
|
|
|
|
|
if (type == switch_statement || 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:
|
2010-07-15 16:14:22 +02:00
|
|
|
if (topState.type == stream_op || topState.type == stream_op_cont)
|
2010-07-02 15:43:34 +02:00
|
|
|
*indentDepth -= 3; // to align << with <<
|
|
|
|
|
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)) {
|
|
|
|
|
*indentDepth -= m_indentSize;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|