forked from qt-creator/qt-creator
QmlJS: Introduce a new indenter that works similarly to the new C++ one.
Done-with: Thomas Hartmann
This commit is contained in:
@@ -56,6 +56,11 @@ OTHER_FILES += \
|
||||
$$PWD/parser/qmljs.g
|
||||
|
||||
contains(QT, gui) {
|
||||
SOURCES += $$PWD/qmljsindenter.cpp
|
||||
HEADERS += $$PWD/qmljsindenter.h
|
||||
SOURCES += \
|
||||
$$PWD/qmljsindenter.cpp \
|
||||
$$PWD/qmljscodeformatter.cpp
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/qmljsindenter.h \
|
||||
$$PWD/qmljscodeformatter.h
|
||||
}
|
||||
|
910
src/libs/qmljs/qmljscodeformatter.cpp
Normal file
910
src/libs/qmljs/qmljscodeformatter.cpp
Normal file
@@ -0,0 +1,910 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#include "qmljscodeformatter.h"
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QMetaEnum>
|
||||
#include <QtGui/QTextDocument>
|
||||
#include <QtGui/QTextCursor>
|
||||
#include <QtGui/QTextBlock>
|
||||
|
||||
using namespace QmlJS;
|
||||
|
||||
CodeFormatter::BlockData::BlockData()
|
||||
: m_blockRevision(-1)
|
||||
{
|
||||
}
|
||||
|
||||
CodeFormatter::CodeFormatter()
|
||||
: m_indentDepth(0)
|
||||
, m_tabSize(4)
|
||||
{
|
||||
}
|
||||
|
||||
CodeFormatter::~CodeFormatter()
|
||||
{
|
||||
}
|
||||
|
||||
void CodeFormatter::setTabSize(int tabSize)
|
||||
{
|
||||
m_tabSize = tabSize;
|
||||
}
|
||||
|
||||
void CodeFormatter::recalculateStateAfter(const QTextBlock &block)
|
||||
{
|
||||
restoreCurrentState(block.previous());
|
||||
|
||||
const int lexerState = tokenizeBlock(block);
|
||||
m_tokenIndex = 0;
|
||||
m_newStates.clear();
|
||||
|
||||
//qDebug() << "Starting to look at " << block.text() << block.blockNumber() + 1;
|
||||
|
||||
for (; m_tokenIndex < m_tokens.size(); ) {
|
||||
m_currentToken = tokenAt(m_tokenIndex);
|
||||
const int kind = extendedTokenKind(m_currentToken);
|
||||
//dump();
|
||||
//qDebug() << "Token" << m_currentLine.mid(m_currentToken.begin(), m_currentToken.length) << m_tokenIndex << "in line" << block.blockNumber() + 1;
|
||||
|
||||
if (kind == Comment
|
||||
&& state().type != multiline_comment_cont
|
||||
&& state().type != multiline_comment_start) {
|
||||
m_tokenIndex += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (m_currentState.top().type) {
|
||||
case topmost_intro:
|
||||
switch (kind) {
|
||||
case Identifier: enter(objectdefinition_or_js); continue;
|
||||
case Import: enter(top_qml); continue;
|
||||
default: enter(top_js); continue;
|
||||
} break;
|
||||
|
||||
case top_qml:
|
||||
switch (kind) {
|
||||
case Import: enter(import_start); break;
|
||||
case Identifier: enter(binding_or_objectdefinition); break;
|
||||
} break;
|
||||
|
||||
case top_js:
|
||||
tryStatement();
|
||||
break;
|
||||
|
||||
case objectdefinition_or_js:
|
||||
switch (kind) {
|
||||
case Dot: break;
|
||||
case Identifier:
|
||||
if (!m_currentLine.at(m_currentToken.begin()).isUpper()) {
|
||||
turnInto(top_js);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case LeftBrace: turnInto(binding_or_objectdefinition); continue;
|
||||
default: turnInto(top_js); continue;
|
||||
} break;
|
||||
|
||||
case import_start:
|
||||
enter(import_maybe_dot_or_version_or_as);
|
||||
break;
|
||||
|
||||
case import_maybe_dot_or_version_or_as:
|
||||
switch (kind) {
|
||||
case Dot: turnInto(import_dot); break;
|
||||
case As: turnInto(import_as); break;
|
||||
case Number: turnInto(import_maybe_as); break;
|
||||
default: leave(); leave(); continue;
|
||||
} break;
|
||||
|
||||
case import_maybe_as:
|
||||
switch (kind) {
|
||||
case As: turnInto(import_as); break;
|
||||
default: leave(); leave(); continue;
|
||||
} break;
|
||||
|
||||
case import_dot:
|
||||
switch (kind) {
|
||||
case Identifier: turnInto(import_maybe_dot_or_version_or_as); break;
|
||||
default: leave(); leave(); continue;
|
||||
} break;
|
||||
|
||||
case import_as:
|
||||
switch (kind) {
|
||||
case Identifier: leave(); leave(); break;
|
||||
} break;
|
||||
|
||||
case binding_or_objectdefinition:
|
||||
switch (kind) {
|
||||
case Colon: enter(binding_assignment); break;
|
||||
case LeftBrace: enter(objectdefinition_open); break;
|
||||
} break;
|
||||
|
||||
case binding_assignment:
|
||||
switch (kind) {
|
||||
case Semicolon: leave(true); break;
|
||||
case If: enter(if_statement); break;
|
||||
case LeftBrace: enter(jsblock_open); break;
|
||||
case On:
|
||||
case As:
|
||||
case List:
|
||||
case Import:
|
||||
case Signal:
|
||||
case Property:
|
||||
case Identifier: enter(expression_or_objectdefinition); break;
|
||||
default: enter(expression); continue;
|
||||
} break;
|
||||
|
||||
case objectdefinition_open:
|
||||
switch (kind) {
|
||||
case RightBrace: leave(true); break;
|
||||
case Default: enter(default_property_start); break;
|
||||
case Property: enter(property_start); break;
|
||||
case Function: enter(function_start); break;
|
||||
case Signal: enter(signal_start); break;
|
||||
case On:
|
||||
case As:
|
||||
case List:
|
||||
case Import:
|
||||
case Identifier: enter(binding_or_objectdefinition); break;
|
||||
} break;
|
||||
|
||||
case default_property_start:
|
||||
if (kind != Property)
|
||||
leave(true);
|
||||
else
|
||||
turnInto(property_start);
|
||||
break;
|
||||
|
||||
case property_start:
|
||||
switch (kind) {
|
||||
case Colon: enter(binding_assignment); break; // oops, was a binding
|
||||
case Var:
|
||||
case Identifier: enter(property_type); break;
|
||||
case List: enter(property_list_open); break;
|
||||
default: leave(true); continue;
|
||||
} break;
|
||||
|
||||
case property_type:
|
||||
turnInto(property_maybe_initializer);
|
||||
break;
|
||||
|
||||
case property_list_open:
|
||||
if (m_currentLine.midRef(m_currentToken.begin(), m_currentToken.length) == QLatin1String(">"))
|
||||
turnInto(property_maybe_initializer);
|
||||
break;
|
||||
|
||||
case property_maybe_initializer:
|
||||
switch (kind) {
|
||||
case Colon: enter(binding_assignment); break;
|
||||
default: leave(true); continue;
|
||||
} break;
|
||||
|
||||
case signal_start:
|
||||
switch (kind) {
|
||||
case Colon: enter(binding_assignment); break; // oops, was a binding
|
||||
default: enter(signal_maybe_arglist); break;
|
||||
} break;
|
||||
|
||||
case signal_maybe_arglist:
|
||||
switch (kind) {
|
||||
case LeftParenthesis: turnInto(signal_arglist_open); break;
|
||||
default: leave(true); continue;
|
||||
} break;
|
||||
|
||||
case signal_arglist_open:
|
||||
switch (kind) {
|
||||
case RightParenthesis: leave(true); break;
|
||||
} break;
|
||||
|
||||
case function_start:
|
||||
switch (kind) {
|
||||
case LeftParenthesis: enter(function_arglist_open); break;
|
||||
} break;
|
||||
|
||||
case function_arglist_open:
|
||||
switch (kind) {
|
||||
case RightParenthesis: turnInto(function_arglist_closed); break;
|
||||
} break;
|
||||
|
||||
case function_arglist_closed:
|
||||
switch (kind) {
|
||||
case LeftBrace: turnInto(jsblock_open); break;
|
||||
default: leave(true); continue; // error recovery
|
||||
} break;
|
||||
|
||||
case expression_or_objectdefinition:
|
||||
switch (kind) {
|
||||
case LeftBrace: turnInto(objectdefinition_open); break;
|
||||
default: enter(expression); continue; // really? first token already gone!
|
||||
} break;
|
||||
|
||||
case expression:
|
||||
if (tryInsideExpression())
|
||||
break;
|
||||
switch (kind) {
|
||||
case Comma:
|
||||
case Delimiter: enter(expression_continuation); break;
|
||||
case RightBracket:
|
||||
case RightParenthesis: leave(); continue;
|
||||
case RightBrace: leave(true); continue;
|
||||
case Semicolon: leave(true); break;
|
||||
} break;
|
||||
|
||||
case expression_continuation:
|
||||
leave();
|
||||
continue;
|
||||
|
||||
case expression_maybe_continuation:
|
||||
switch (kind) {
|
||||
case Question:
|
||||
case Delimiter:
|
||||
case LeftBracket:
|
||||
case LeftParenthesis: leave(); continue;
|
||||
default: leave(true); continue;
|
||||
} break;
|
||||
|
||||
case paren_open:
|
||||
if (tryInsideExpression())
|
||||
break;
|
||||
switch (kind) {
|
||||
case RightParenthesis: leave(); break;
|
||||
} break;
|
||||
|
||||
case bracket_open:
|
||||
if (tryInsideExpression())
|
||||
break;
|
||||
switch (kind) {
|
||||
case Comma: enter(bracket_element_start); break;
|
||||
case RightBracket: leave(); break;
|
||||
} break;
|
||||
|
||||
case bracket_element_start:
|
||||
switch (kind) {
|
||||
case Identifier: turnInto(bracket_element_maybe_objectdefinition); break;
|
||||
default: leave(); continue;
|
||||
} break;
|
||||
|
||||
case bracket_element_maybe_objectdefinition:
|
||||
switch (kind) {
|
||||
case LeftBrace: turnInto(objectdefinition_open); break;
|
||||
default: leave(); continue;
|
||||
} break;
|
||||
|
||||
case ternary_op:
|
||||
if (tryInsideExpression())
|
||||
break;
|
||||
switch (kind) {
|
||||
case RightParenthesis:
|
||||
case RightBracket:
|
||||
case RightBrace:
|
||||
case Comma:
|
||||
case Semicolon: leave(); continue;
|
||||
case Colon: enter(expression); break; // entering expression makes maybe_continuation work
|
||||
} break;
|
||||
|
||||
case jsblock_open:
|
||||
case substatement_open:
|
||||
if (tryStatement())
|
||||
break;
|
||||
switch (kind) {
|
||||
case RightBrace: leave(true); break;
|
||||
} break;
|
||||
|
||||
case substatement:
|
||||
// prefer substatement_open over block_open
|
||||
if (kind != LeftBrace) {
|
||||
if (tryStatement())
|
||||
break;
|
||||
}
|
||||
switch (kind) {
|
||||
case LeftBrace: turnInto(substatement_open); break;
|
||||
} break;
|
||||
|
||||
case if_statement:
|
||||
switch (kind) {
|
||||
case LeftParenthesis: enter(condition_open); break;
|
||||
default: leave(true); break; // error recovery
|
||||
} break;
|
||||
|
||||
case maybe_else:
|
||||
if (kind == 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 condition_open:
|
||||
switch (kind) {
|
||||
case RightParenthesis: turnInto(substatement); break;
|
||||
case LeftParenthesis: enter(condition_paren_open); break;
|
||||
} break;
|
||||
|
||||
// paren nesting
|
||||
case condition_paren_open:
|
||||
switch (kind) {
|
||||
case RightParenthesis: leave(); break;
|
||||
case LeftParenthesis: enter(condition_paren_open); break;
|
||||
} break;
|
||||
|
||||
case switch_statement:
|
||||
case statement_with_condition:
|
||||
switch (kind) {
|
||||
case LeftParenthesis: enter(statement_with_condition_paren_open); break;
|
||||
default: leave(true);
|
||||
} break;
|
||||
|
||||
case statement_with_condition_paren_open:
|
||||
if (tryInsideExpression())
|
||||
break;
|
||||
switch (kind) {
|
||||
case RightParenthesis: turnInto(substatement); break;
|
||||
} break;
|
||||
|
||||
case statement_with_block:
|
||||
switch (kind) {
|
||||
case LeftBrace: enter(jsblock_open); break;
|
||||
default: leave(true); break;
|
||||
} break;
|
||||
|
||||
case do_statement:
|
||||
switch (kind) {
|
||||
case While: break;
|
||||
case LeftParenthesis: enter(do_statement_while_paren_open); break;
|
||||
default: leave(true); break;
|
||||
} break;
|
||||
|
||||
case do_statement_while_paren_open:
|
||||
if (tryInsideExpression())
|
||||
break;
|
||||
switch (kind) {
|
||||
case RightParenthesis: leave(); leave(true); break;
|
||||
} break;
|
||||
|
||||
break;
|
||||
|
||||
case case_start:
|
||||
switch (kind) {
|
||||
case Colon: turnInto(case_cont); break;
|
||||
} break;
|
||||
|
||||
case case_cont:
|
||||
if (kind != Case && kind != Default && tryStatement())
|
||||
break;
|
||||
switch (kind) {
|
||||
case RightBrace: leave(); continue;
|
||||
case Default:
|
||||
case Case: leave(); continue;
|
||||
} break;
|
||||
|
||||
case multiline_comment_start:
|
||||
case multiline_comment_cont:
|
||||
if (kind != Comment) {
|
||||
leave();
|
||||
continue;
|
||||
} else if (m_tokenIndex == m_tokens.size() - 1
|
||||
&& lexerState == Scanner::Normal) {
|
||||
leave();
|
||||
} else if (m_tokenIndex == 0) {
|
||||
// to allow enter/leave to update the indentDepth
|
||||
turnInto(multiline_comment_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 == expression
|
||||
|| topState == expression_or_objectdefinition) {
|
||||
enter(expression_maybe_continuation);
|
||||
}
|
||||
if (topState != multiline_comment_start
|
||||
&& topState != multiline_comment_cont
|
||||
&& lexerState == Scanner::MultiLineComment) {
|
||||
enter(multiline_comment_start);
|
||||
}
|
||||
|
||||
saveCurrentState(block);
|
||||
}
|
||||
|
||||
int CodeFormatter::indentFor(const QTextBlock &block)
|
||||
{
|
||||
// qDebug() << "indenting for" << block.blockNumber() + 1;
|
||||
|
||||
restoreCurrentState(block.previous());
|
||||
correctIndentation(block);
|
||||
return m_indentDepth;
|
||||
}
|
||||
|
||||
void CodeFormatter::updateStateUntil(const QTextBlock &endBlock)
|
||||
{
|
||||
QStack<State> previousState = initialState();
|
||||
QTextBlock it = endBlock.document()->firstBlock();
|
||||
|
||||
// find the first block that needs recalculation
|
||||
for (; it.isValid() && it != endBlock; it = it.next()) {
|
||||
BlockData blockData;
|
||||
if (!loadBlockData(it, &blockData))
|
||||
break;
|
||||
if (blockData.m_blockRevision != it.revision())
|
||||
break;
|
||||
if (previousState != blockData.m_beginState)
|
||||
break;
|
||||
if (loadLexerState(it) == -1)
|
||||
break;
|
||||
|
||||
previousState = blockData.m_endState;
|
||||
}
|
||||
|
||||
if (it == endBlock)
|
||||
return;
|
||||
|
||||
// update everthing until endBlock
|
||||
for (; it.isValid() && it != endBlock; it = it.next()) {
|
||||
recalculateStateAfter(it);
|
||||
}
|
||||
|
||||
// invalidate everything below by marking the state in endBlock as invalid
|
||||
if (it.isValid()) {
|
||||
BlockData invalidBlockData;
|
||||
saveBlockData(&it, invalidBlockData);
|
||||
}
|
||||
}
|
||||
|
||||
void CodeFormatter::updateLineStateChange(const QTextBlock &block)
|
||||
{
|
||||
if (!block.isValid())
|
||||
return;
|
||||
|
||||
BlockData blockData;
|
||||
if (loadBlockData(block, &blockData) && blockData.m_blockRevision == block.revision())
|
||||
return;
|
||||
|
||||
recalculateStateAfter(block);
|
||||
|
||||
// invalidate everything below by marking the next block's state as invalid
|
||||
QTextBlock next = block.next();
|
||||
if (!next.isValid())
|
||||
return;
|
||||
|
||||
saveBlockData(&next, BlockData());
|
||||
}
|
||||
|
||||
CodeFormatter::State CodeFormatter::state(int belowTop) const
|
||||
{
|
||||
if (belowTop < m_currentState.size())
|
||||
return m_currentState.at(m_currentState.size() - 1 - belowTop);
|
||||
else
|
||||
return State();
|
||||
}
|
||||
|
||||
const QVector<CodeFormatter::State> &CodeFormatter::newStatesThisLine() const
|
||||
{
|
||||
return m_newStates;
|
||||
}
|
||||
|
||||
int CodeFormatter::tokenIndex() const
|
||||
{
|
||||
return m_tokenIndex;
|
||||
}
|
||||
|
||||
int CodeFormatter::tokenCount() const
|
||||
{
|
||||
return m_tokens.size();
|
||||
}
|
||||
|
||||
const Token &CodeFormatter::currentToken() const
|
||||
{
|
||||
return m_currentToken;
|
||||
}
|
||||
|
||||
void CodeFormatter::invalidateCache(QTextDocument *document)
|
||||
{
|
||||
if (!document)
|
||||
return;
|
||||
|
||||
BlockData invalidBlockData;
|
||||
QTextBlock it = document->firstBlock();
|
||||
for (; it.isValid(); it = it.next()) {
|
||||
saveBlockData(&it, invalidBlockData);
|
||||
}
|
||||
}
|
||||
|
||||
void CodeFormatter::enter(int newState)
|
||||
{
|
||||
int savedIndentDepth = m_indentDepth;
|
||||
onEnter(newState, &m_indentDepth, &savedIndentDepth);
|
||||
State s(newState, savedIndentDepth);
|
||||
m_currentState.push(s);
|
||||
m_newStates.push(s);
|
||||
|
||||
if (newState == bracket_open)
|
||||
enter(bracket_element_start);
|
||||
}
|
||||
|
||||
void CodeFormatter::leave(bool statementDone)
|
||||
{
|
||||
Q_ASSERT(m_currentState.size() > 1);
|
||||
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;
|
||||
|
||||
int topState = m_currentState.top().type;
|
||||
|
||||
// if statement is done, may need to leave recursively
|
||||
if (statementDone) {
|
||||
if (!isExpressionEndState(topState))
|
||||
leave(true);
|
||||
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::tryInsideExpression(bool alsoExpression)
|
||||
{
|
||||
int newState = -1;
|
||||
const int kind = extendedTokenKind(m_currentToken);
|
||||
switch (kind) {
|
||||
case LeftParenthesis: newState = paren_open; break;
|
||||
case LeftBracket: newState = bracket_open; break;
|
||||
case Function: newState = function_start; break;
|
||||
case Question: newState = ternary_op; break;
|
||||
}
|
||||
|
||||
if (newState != -1) {
|
||||
if (alsoExpression)
|
||||
enter(expression);
|
||||
enter(newState);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CodeFormatter::tryStatement()
|
||||
{
|
||||
const int kind = extendedTokenKind(m_currentToken);
|
||||
switch (kind) {
|
||||
case Semicolon:
|
||||
enter(empty_statement);
|
||||
leave(true);
|
||||
return true;
|
||||
case Return:
|
||||
enter(return_statement);
|
||||
enter(expression);
|
||||
return true;
|
||||
case While:
|
||||
case For:
|
||||
case Catch:
|
||||
enter(statement_with_condition);
|
||||
return true;
|
||||
case Switch:
|
||||
enter(switch_statement);
|
||||
return true;
|
||||
case If:
|
||||
enter(if_statement);
|
||||
return true;
|
||||
case Do:
|
||||
enter(do_statement);
|
||||
enter(substatement);
|
||||
return true;
|
||||
case Case:
|
||||
case Default:
|
||||
enter(case_start);
|
||||
return true;
|
||||
case Try:
|
||||
case Finally:
|
||||
enter(statement_with_block);
|
||||
return true;
|
||||
case LeftBrace:
|
||||
enter(jsblock_open);
|
||||
return true;
|
||||
case Identifier:
|
||||
case Delimiter:
|
||||
case PlusPlus:
|
||||
case MinusMinus:
|
||||
case Import:
|
||||
case Signal:
|
||||
case On:
|
||||
case As:
|
||||
case List:
|
||||
case Property:
|
||||
case Function:
|
||||
enter(expression);
|
||||
// look at the token again
|
||||
m_tokenIndex -= 1;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CodeFormatter::isBracelessState(int type) const
|
||||
{
|
||||
return
|
||||
type == if_statement ||
|
||||
type == else_clause ||
|
||||
type == substatement ||
|
||||
type == binding_assignment ||
|
||||
type == binding_or_objectdefinition;
|
||||
}
|
||||
|
||||
bool CodeFormatter::isExpressionEndState(int type) const
|
||||
{
|
||||
return
|
||||
type == topmost_intro ||
|
||||
type == top_js ||
|
||||
type == objectdefinition_open ||
|
||||
type == if_statement ||
|
||||
type == else_clause ||
|
||||
type == do_statement ||
|
||||
type == jsblock_open ||
|
||||
type == substatement_open ||
|
||||
type == bracket_open ||
|
||||
type == paren_open ||
|
||||
type == case_cont;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
QStringRef CodeFormatter::currentTokenText() const
|
||||
{
|
||||
return m_currentLine.midRef(m_currentToken.begin(), m_currentToken.length);
|
||||
}
|
||||
|
||||
void CodeFormatter::turnInto(int newState)
|
||||
{
|
||||
leave(false);
|
||||
enter(newState);
|
||||
}
|
||||
|
||||
void CodeFormatter::saveCurrentState(const QTextBlock &block)
|
||||
{
|
||||
if (!block.isValid())
|
||||
return;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void CodeFormatter::restoreCurrentState(const QTextBlock &block)
|
||||
{
|
||||
if (block.isValid()) {
|
||||
BlockData blockData;
|
||||
if (loadBlockData(block, &blockData)) {
|
||||
m_indentDepth = blockData.m_indentDepth;
|
||||
m_currentState = blockData.m_endState;
|
||||
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)
|
||||
{
|
||||
int startState = loadLexerState(block.previous());
|
||||
if (block.blockNumber() == 0)
|
||||
startState = 0;
|
||||
Q_ASSERT(startState != -1);
|
||||
|
||||
Scanner tokenize;
|
||||
tokenize.setScanComments(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);
|
||||
|
||||
const int lexerState = tokenize.state();
|
||||
QTextBlock saveableBlock(block);
|
||||
saveLexerState(&saveableBlock, lexerState);
|
||||
return lexerState;
|
||||
}
|
||||
|
||||
CodeFormatter::TokenKind CodeFormatter::extendedTokenKind(const QmlJS::Token &token) const
|
||||
{
|
||||
const int kind = token.kind;
|
||||
QStringRef text = m_currentLine.midRef(token.begin(), token.length);
|
||||
|
||||
if (kind == Identifier) {
|
||||
if (text == "as")
|
||||
return As;
|
||||
if (text == "import")
|
||||
return Import;
|
||||
if (text == "signal")
|
||||
return Signal;
|
||||
if (text == "property")
|
||||
return Property;
|
||||
if (text == "on")
|
||||
return On;
|
||||
if (text == "list")
|
||||
return On;
|
||||
} else if (kind == Keyword) {
|
||||
const QChar char1 = text.at(0);
|
||||
const QChar char2 = text.at(1);
|
||||
const QChar char3 = (text.size() > 2 ? text.at(2) : QChar());
|
||||
switch (char1.toLatin1()) {
|
||||
case 'v':
|
||||
return Var;
|
||||
case 'i':
|
||||
if (char2 == 'f')
|
||||
return If;
|
||||
else if (char3 == 's')
|
||||
return Instanceof;
|
||||
else
|
||||
return In;
|
||||
case 'f':
|
||||
if (char2 == 'o')
|
||||
return For;
|
||||
else if (char2 == 'u')
|
||||
return Function;
|
||||
else
|
||||
return Finally;
|
||||
case 'e':
|
||||
return Else;
|
||||
case 'n':
|
||||
return New;
|
||||
case 'r':
|
||||
return Return;
|
||||
case 's':
|
||||
return Switch;
|
||||
case 'w':
|
||||
if (char2 == 'h')
|
||||
return While;
|
||||
return With;
|
||||
case 'c':
|
||||
if (char3 == 's')
|
||||
return Case;
|
||||
if (char3 == 't')
|
||||
return Catch;
|
||||
return Continue;
|
||||
case 'd':
|
||||
if (char3 == 'l')
|
||||
return Delete;
|
||||
if (char3 == 'f')
|
||||
return Default;
|
||||
if (char3 == 'b')
|
||||
return Debugger;
|
||||
return Do;
|
||||
case 't':
|
||||
if (char3 == 'i')
|
||||
return This;
|
||||
if (char3 == 'y')
|
||||
return Try;
|
||||
if (char3 == 'r')
|
||||
return Throw;
|
||||
return Typeof;
|
||||
case 'b':
|
||||
return Break;
|
||||
}
|
||||
} else if (kind == Delimiter) {
|
||||
if (text == "?")
|
||||
return Question;
|
||||
else if (text == "++")
|
||||
return PlusPlus;
|
||||
else if (text == "--")
|
||||
return MinusMinus;
|
||||
}
|
||||
|
||||
return static_cast<TokenKind>(kind);
|
||||
}
|
||||
|
||||
void CodeFormatter::dump() const
|
||||
{
|
||||
QMetaEnum metaEnum = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("StateType"));
|
||||
|
||||
qDebug() << "Current token index" << m_tokenIndex;
|
||||
qDebug() << "Current state:";
|
||||
foreach (State s, m_currentState) {
|
||||
qDebug() << metaEnum.valueToKey(s.type) << s.savedIndentDepth;
|
||||
}
|
||||
qDebug() << "Current indent depth:" << m_indentDepth;
|
||||
}
|
309
src/libs/qmljs/qmljscodeformatter.h
Normal file
309
src/libs/qmljs/qmljscodeformatter.h
Normal file
@@ -0,0 +1,309 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef QMLJSCODEFORMATTER_H
|
||||
#define QMLJSCODEFORMATTER_H
|
||||
|
||||
#include "qmljs_global.h"
|
||||
|
||||
#include "qmljsscanner.h"
|
||||
|
||||
#include <QtCore/QChar>
|
||||
#include <QtCore/QStack>
|
||||
#include <QtCore/QList>
|
||||
#include <QtCore/QVector>
|
||||
#include <QtCore/QPointer>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QTextDocument;
|
||||
class QTextBlock;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace QmlJS {
|
||||
|
||||
class QMLJS_EXPORT CodeFormatter
|
||||
{
|
||||
Q_GADGET
|
||||
public:
|
||||
CodeFormatter();
|
||||
virtual ~CodeFormatter();
|
||||
|
||||
// updates all states up until block if necessary
|
||||
// it is safe to call indentFor on block afterwards
|
||||
void updateStateUntil(const QTextBlock &block);
|
||||
|
||||
// calculates the state change introduced by changing a single line
|
||||
void updateLineStateChange(const QTextBlock &block);
|
||||
|
||||
int indentFor(const QTextBlock &block);
|
||||
|
||||
void setTabSize(int tabSize);
|
||||
|
||||
void invalidateCache(QTextDocument *document);
|
||||
|
||||
protected:
|
||||
virtual void onEnter(int newState, int *indentDepth, int *savedIndentDepth) const = 0;
|
||||
virtual void adjustIndent(const QList<Token> &tokens, int lexerState, int *indentDepth) const = 0;
|
||||
|
||||
class State;
|
||||
class BlockData
|
||||
{
|
||||
public:
|
||||
BlockData();
|
||||
|
||||
QStack<State> m_beginState;
|
||||
QStack<State> m_endState;
|
||||
int m_indentDepth;
|
||||
int m_blockRevision;
|
||||
};
|
||||
|
||||
virtual void saveBlockData(QTextBlock *block, const BlockData &data) const = 0;
|
||||
virtual bool loadBlockData(const QTextBlock &block, BlockData *data) const = 0;
|
||||
|
||||
virtual void saveLexerState(QTextBlock *block, int state) const = 0;
|
||||
virtual int loadLexerState(const QTextBlock &block) const = 0;
|
||||
|
||||
public: // must be public to make Q_GADGET introspection work
|
||||
enum StateType {
|
||||
invalid = 0,
|
||||
|
||||
topmost_intro, // The first line in a "topmost" definition.
|
||||
|
||||
top_qml, // root state for qml
|
||||
top_js, // root for js
|
||||
objectdefinition_or_js, // file starts with identifier
|
||||
|
||||
multiline_comment_start,
|
||||
multiline_comment_cont,
|
||||
|
||||
import_start, // after 'import'
|
||||
import_maybe_dot_or_version_or_as, // after string or identifier
|
||||
import_dot, // after .
|
||||
import_maybe_as, // after version
|
||||
import_as,
|
||||
|
||||
property_start, // after 'property'
|
||||
default_property_start, // after 'default'
|
||||
property_type, // after first identifier
|
||||
property_list_open, // after 'list' as a type
|
||||
property_maybe_initializer, // after
|
||||
|
||||
signal_start, // after 'signal'
|
||||
signal_maybe_arglist, // after identifier
|
||||
signal_arglist_open, // after '('
|
||||
|
||||
function_start, // after 'function'
|
||||
function_arglist_open, // after '(' starting function argument list
|
||||
function_arglist_closed, // after ')' in argument list, expecting '{'
|
||||
|
||||
binding_or_objectdefinition, // after an identifier
|
||||
|
||||
binding_assignment, // after :
|
||||
objectdefinition_open, // after {
|
||||
|
||||
expression,
|
||||
expression_continuation, // at the end of the line, when the next line definitely is a continuation
|
||||
expression_maybe_continuation, // at the end of the line, when the next line may be an expression
|
||||
expression_or_objectdefinition, // after a binding starting with an identifier ("x: foo")
|
||||
|
||||
paren_open, // opening ( in expression
|
||||
bracket_open, // opening [ in expression
|
||||
|
||||
bracket_element_start, // after starting bracket_open or after ',' in bracket_open
|
||||
bracket_element_maybe_objectdefinition, // after an identifier in bracket_element_start
|
||||
|
||||
ternary_op, // The ? : operator
|
||||
|
||||
jsblock_open,
|
||||
|
||||
empty_statement, // for a ';', will never linger
|
||||
|
||||
if_statement, // After 'if'
|
||||
maybe_else, // after the first substatement in an if
|
||||
else_clause, // The else line of an if-else construct.
|
||||
|
||||
condition_open, // Start of a condition in 'if', 'while', entered after opening paren
|
||||
condition_paren_open, // After an lparen in a condition
|
||||
|
||||
substatement, // The first line after a conditional or loop construct.
|
||||
substatement_open, // The brace that opens a substatement block.
|
||||
|
||||
return_statement, // After 'return'
|
||||
|
||||
statement_with_condition, // After the 'for', 'while', 'catch', ... token
|
||||
statement_with_condition_paren_open, // While inside the (...)
|
||||
|
||||
statement_with_block, // try, finally
|
||||
|
||||
do_statement, // after 'do'
|
||||
do_statement_while_paren_open, // after '(' in while clause
|
||||
|
||||
switch_statement, // After 'switch' token
|
||||
case_start, // after a 'case' or 'default' token
|
||||
case_cont, // after the colon in a case/default
|
||||
};
|
||||
Q_ENUMS(StateType)
|
||||
|
||||
protected:
|
||||
// extends Token::Kind from qmljsscanner.h
|
||||
// the entries until EndOfExistingTokenKinds must match
|
||||
enum TokenKind {
|
||||
EndOfFile,
|
||||
Keyword,
|
||||
Identifier,
|
||||
String,
|
||||
Comment,
|
||||
Number,
|
||||
LeftParenthesis,
|
||||
RightParenthesis,
|
||||
LeftBrace,
|
||||
RightBrace,
|
||||
LeftBracket,
|
||||
RightBracket,
|
||||
Semicolon,
|
||||
Colon,
|
||||
Comma,
|
||||
Dot,
|
||||
Delimiter,
|
||||
|
||||
EndOfExistingTokenKinds,
|
||||
|
||||
Break,
|
||||
Case,
|
||||
Catch,
|
||||
Continue,
|
||||
Debugger,
|
||||
Default,
|
||||
Delete,
|
||||
Do,
|
||||
Else,
|
||||
Finally,
|
||||
For,
|
||||
Function,
|
||||
If,
|
||||
In,
|
||||
Instanceof,
|
||||
New,
|
||||
Return,
|
||||
Switch,
|
||||
This,
|
||||
Throw,
|
||||
Try,
|
||||
Typeof,
|
||||
Var,
|
||||
Void,
|
||||
While,
|
||||
With,
|
||||
|
||||
Import,
|
||||
Signal,
|
||||
On,
|
||||
As,
|
||||
List,
|
||||
Property,
|
||||
|
||||
Question,
|
||||
PlusPlus,
|
||||
MinusMinus,
|
||||
};
|
||||
|
||||
TokenKind extendedTokenKind(const QmlJS::Token &token) const;
|
||||
|
||||
struct State {
|
||||
State()
|
||||
: savedIndentDepth(0)
|
||||
, type(0)
|
||||
{}
|
||||
|
||||
State(quint8 ty, quint16 savedDepth)
|
||||
: savedIndentDepth(savedDepth)
|
||||
, type(ty)
|
||||
{}
|
||||
|
||||
quint16 savedIndentDepth;
|
||||
quint8 type;
|
||||
|
||||
bool operator==(const State &other) const {
|
||||
return type == other.type
|
||||
&& savedIndentDepth == other.savedIndentDepth;
|
||||
}
|
||||
};
|
||||
|
||||
State state(int belowTop = 0) const;
|
||||
const QVector<State> &newStatesThisLine() const;
|
||||
int tokenIndex() const;
|
||||
int tokenCount() const;
|
||||
const Token ¤tToken() const;
|
||||
const Token &tokenAt(int idx) const;
|
||||
int column(int position) const;
|
||||
|
||||
bool isBracelessState(int type) const;
|
||||
bool isExpressionEndState(int type) const;
|
||||
|
||||
void dump() const;
|
||||
|
||||
private:
|
||||
void recalculateStateAfter(const QTextBlock &block);
|
||||
void saveCurrentState(const QTextBlock &block);
|
||||
void restoreCurrentState(const QTextBlock &block);
|
||||
|
||||
QStringRef currentTokenText() const;
|
||||
|
||||
int tokenizeBlock(const QTextBlock &block);
|
||||
|
||||
void turnInto(int newState);
|
||||
|
||||
bool tryInsideExpression(bool alsoExpression = false);
|
||||
bool tryStatement();
|
||||
|
||||
void enter(int newState);
|
||||
void leave(bool statementDone = false);
|
||||
void correctIndentation(const QTextBlock &block);
|
||||
|
||||
private:
|
||||
static QStack<State> initialState();
|
||||
|
||||
QStack<State> m_beginState;
|
||||
QStack<State> m_currentState;
|
||||
QStack<State> m_newStates;
|
||||
|
||||
QList<Token> m_tokens;
|
||||
QString m_currentLine;
|
||||
Token m_currentToken;
|
||||
int m_tokenIndex;
|
||||
|
||||
// should store indent level and padding instead
|
||||
int m_indentDepth;
|
||||
|
||||
int m_tabSize;
|
||||
};
|
||||
|
||||
} // namespace QmlJS
|
||||
|
||||
#endif // QMLJSCODEFORMATTER_H
|
@@ -33,8 +33,8 @@
|
||||
#include "qmljseditorplugin.h"
|
||||
#include "qmljsmodelmanager.h"
|
||||
#include "qmloutlinemodel.h"
|
||||
#include "qmljseditorcodeformatter.h"
|
||||
|
||||
#include <qmljs/qmljsindenter.h>
|
||||
#include <qmljs/qmljsbind.h>
|
||||
#include <qmljs/qmljscheck.h>
|
||||
#include <qmljs/qmljsdocument.h>
|
||||
@@ -1276,15 +1276,25 @@ bool QmlJSTextEditor::isClosingBrace(const QList<Token> &tokens) const
|
||||
return false;
|
||||
}
|
||||
|
||||
static QmlJSEditor::QtStyleCodeFormatter setupCodeFormatter(const TextEditor::TabSettings &ts)
|
||||
{
|
||||
QmlJSEditor::QtStyleCodeFormatter codeFormatter;
|
||||
codeFormatter.setIndentSize(ts.m_indentSize);
|
||||
codeFormatter.setTabSize(ts.m_tabSize);
|
||||
return codeFormatter;
|
||||
}
|
||||
|
||||
void QmlJSTextEditor::indentBlock(QTextDocument *doc, QTextBlock block, QChar typedChar)
|
||||
{
|
||||
TextEditor::TabSettings ts = tabSettings();
|
||||
QmlJSIndenter indenter;
|
||||
indenter.setTabSize(ts.m_tabSize);
|
||||
indenter.setIndentSize(ts.m_indentSize);
|
||||
Q_UNUSED(doc)
|
||||
Q_UNUSED(typedChar)
|
||||
|
||||
const int indent = indenter.indentForBottomLine(doc->begin(), block.next(), typedChar);
|
||||
ts.indentLine(block, indent);
|
||||
const TextEditor::TabSettings &ts = tabSettings();
|
||||
QmlJSEditor::QtStyleCodeFormatter codeFormatter = setupCodeFormatter(ts);
|
||||
|
||||
codeFormatter.updateStateUntil(block);
|
||||
const int depth = codeFormatter.indentFor(block);
|
||||
ts.indentLine(block, depth);
|
||||
}
|
||||
|
||||
TextEditor::BaseTextEditorEditable *QmlJSTextEditor::createEditableInterface()
|
||||
|
@@ -26,7 +26,8 @@ HEADERS += \
|
||||
qmljscomponentfromobjectdef.h \
|
||||
qmljsoutline.h \
|
||||
qmloutlinemodel.h \
|
||||
qmltaskmanager.h
|
||||
qmltaskmanager.h \
|
||||
qmljseditorcodeformatter.h
|
||||
|
||||
SOURCES += \
|
||||
qmljscodecompletion.cpp \
|
||||
@@ -46,7 +47,8 @@ SOURCES += \
|
||||
qmljsoutline.cpp \
|
||||
qmloutlinemodel.cpp \
|
||||
qmltaskmanager.cpp \
|
||||
qmljsquickfixes.cpp
|
||||
qmljsquickfixes.cpp \
|
||||
qmljseditorcodeformatter.cpp
|
||||
|
||||
RESOURCES += qmljseditor.qrc
|
||||
OTHER_FILES += QmlJSEditor.pluginspec QmlJSEditor.mimetypes.xml
|
||||
|
358
src/plugins/qmljseditor/qmljseditorcodeformatter.cpp
Normal file
358
src/plugins/qmljseditor/qmljseditorcodeformatter.cpp
Normal file
@@ -0,0 +1,358 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#include "qmljseditorcodeformatter.h"
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
using namespace QmlJS;
|
||||
using namespace QmlJSEditor;
|
||||
using namespace TextEditor;
|
||||
|
||||
QtStyleCodeFormatter::QtStyleCodeFormatter()
|
||||
: m_indentSize(4)
|
||||
{
|
||||
}
|
||||
|
||||
void QtStyleCodeFormatter::setIndentSize(int size)
|
||||
{
|
||||
m_indentSize = size;
|
||||
}
|
||||
|
||||
void QtStyleCodeFormatter::saveBlockData(QTextBlock *block, const BlockData &data) const
|
||||
{
|
||||
TextBlockUserData *userData = BaseTextDocumentLayout::userData(*block);
|
||||
QmlJSCodeFormatterData *cppData = static_cast<QmlJSCodeFormatterData *>(userData->codeFormatterData());
|
||||
if (!cppData) {
|
||||
cppData = new QmlJSCodeFormatterData;
|
||||
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;
|
||||
QmlJSCodeFormatterData *cppData = static_cast<QmlJSCodeFormatterData *>(userData->codeFormatterData());
|
||||
if (!cppData)
|
||||
return false;
|
||||
|
||||
*data = cppData->m_data;
|
||||
return true;
|
||||
}
|
||||
|
||||
void QtStyleCodeFormatter::saveLexerState(QTextBlock *block, int state) const
|
||||
{
|
||||
BaseTextDocumentLayout::setLexerState(*block, state);
|
||||
}
|
||||
|
||||
int QtStyleCodeFormatter::loadLexerState(const QTextBlock &block) const
|
||||
{
|
||||
return BaseTextDocumentLayout::lexerState(block);
|
||||
}
|
||||
|
||||
void QtStyleCodeFormatter::onEnter(int newState, int *indentDepth, int *savedIndentDepth) const
|
||||
{
|
||||
const State &parentState = state();
|
||||
const Token &tk = currentToken();
|
||||
const int tokenPosition = column(tk.begin());
|
||||
const bool firstToken = (tokenIndex() == 0);
|
||||
const bool lastToken = (tokenIndex() == tokenCount() - 1);
|
||||
|
||||
switch (newState) {
|
||||
case objectdefinition_open: {
|
||||
// special case for things like "gradient: Gradient {"
|
||||
if (parentState.type == binding_assignment)
|
||||
*savedIndentDepth = state(1).savedIndentDepth;
|
||||
|
||||
bool followedByData = (!lastToken && !tokenAt(tokenIndex() + 1).kind == Token::Comment);
|
||||
if (firstToken || followedByData)
|
||||
*savedIndentDepth = tokenPosition;
|
||||
|
||||
*indentDepth = *savedIndentDepth;
|
||||
|
||||
if (followedByData) {
|
||||
*indentDepth = column(tokenAt(tokenIndex() + 1).begin());
|
||||
} else {
|
||||
*indentDepth += m_indentSize;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case binding_or_objectdefinition:
|
||||
if (firstToken)
|
||||
*indentDepth = *savedIndentDepth = tokenPosition;
|
||||
break;
|
||||
|
||||
case binding_assignment:
|
||||
if (lastToken)
|
||||
*indentDepth = *savedIndentDepth + 4;
|
||||
else
|
||||
*indentDepth = column(tokenAt(tokenIndex() + 1).begin());
|
||||
break;
|
||||
|
||||
case expression_or_objectdefinition:
|
||||
*indentDepth = tokenPosition;
|
||||
break;
|
||||
|
||||
case expression:
|
||||
// expression_or_objectdefinition has already consumed the first token
|
||||
// ternary already adjusts indents nicely
|
||||
if (parentState.type != expression_or_objectdefinition
|
||||
&& parentState.type != binding_assignment
|
||||
&& parentState.type != ternary_op) {
|
||||
*indentDepth += 2 * m_indentSize;
|
||||
}
|
||||
if (!firstToken && parentState.type != expression_or_objectdefinition) {
|
||||
*indentDepth = tokenPosition;
|
||||
}
|
||||
break;
|
||||
|
||||
case expression_maybe_continuation:
|
||||
// set indent depth to indent we'd get if the expression ended here
|
||||
for (int i = 1; state(i).type != topmost_intro; ++i) {
|
||||
const int type = state(i).type;
|
||||
if (isExpressionEndState(type) && !isBracelessState(type)) {
|
||||
*indentDepth = state(i - 1).savedIndentDepth;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case bracket_open:
|
||||
if (parentState.type == expression && state(1).type == binding_assignment) {
|
||||
*savedIndentDepth = state(2).savedIndentDepth;
|
||||
*indentDepth = *savedIndentDepth + m_indentSize;
|
||||
} else if (!lastToken) {
|
||||
*indentDepth = tokenPosition + 1;
|
||||
} else {
|
||||
*indentDepth = *savedIndentDepth + m_indentSize;
|
||||
}
|
||||
break;
|
||||
|
||||
case function_start:
|
||||
if (parentState.type == expression) {
|
||||
// undo the continuation indent of the expression
|
||||
*indentDepth = parentState.savedIndentDepth;
|
||||
*savedIndentDepth = *indentDepth;
|
||||
}
|
||||
break;
|
||||
|
||||
case do_statement_while_paren_open:
|
||||
case statement_with_condition_paren_open:
|
||||
case signal_arglist_open:
|
||||
case function_arglist_open:
|
||||
case paren_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 jsblock_open:
|
||||
// closing brace should be aligned to case
|
||||
if (parentState.type == case_cont) {
|
||||
*savedIndentDepth = parentState.savedIndentDepth;
|
||||
break;
|
||||
}
|
||||
// fallthrough
|
||||
case substatement_open:
|
||||
// special case for foo: {
|
||||
if (parentState.type == binding_assignment && state(1).type == binding_or_objectdefinition)
|
||||
*savedIndentDepth = state(1).savedIndentDepth;
|
||||
*indentDepth = *savedIndentDepth + m_indentSize;
|
||||
break;
|
||||
|
||||
case statement_with_condition:
|
||||
case statement_with_block:
|
||||
case if_statement:
|
||||
case do_statement:
|
||||
case switch_statement:
|
||||
if (firstToken || parentState.type == binding_assignment)
|
||||
*savedIndentDepth = tokenPosition;
|
||||
// ### continuation
|
||||
*indentDepth = *savedIndentDepth; // + 2*m_indentSize;
|
||||
break;
|
||||
|
||||
case maybe_else: {
|
||||
// set indent to outermost braceless savedIndent
|
||||
int outermostBraceless = 0;
|
||||
while (isBracelessState(state(outermostBraceless + 1).type))
|
||||
++outermostBraceless;
|
||||
*indentDepth = state(outermostBraceless).savedIndentDepth;
|
||||
// this is where the else should go, if one appears - aligned to if_statement
|
||||
*savedIndentDepth = state().savedIndentDepth;
|
||||
break;
|
||||
}
|
||||
|
||||
case condition_open:
|
||||
// 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 case_start:
|
||||
*savedIndentDepth = tokenPosition;
|
||||
break;
|
||||
|
||||
case case_cont:
|
||||
*indentDepth += m_indentSize;
|
||||
break;
|
||||
|
||||
case multiline_comment_start:
|
||||
*indentDepth = tokenPosition + 2;
|
||||
break;
|
||||
|
||||
case multiline_comment_cont:
|
||||
*indentDepth = tokenPosition;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void QtStyleCodeFormatter::adjustIndent(const QList<Token> &tokens, int lexerState, int *indentDepth) const
|
||||
{
|
||||
Q_UNUSED(lexerState)
|
||||
|
||||
State topState = state();
|
||||
State previousState = state(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 = column(tokens.at(0).begin());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const int kind = extendedTokenKind(tokenAt(0));
|
||||
switch (kind) {
|
||||
case LeftBrace:
|
||||
if (topState.type == substatement
|
||||
|| topState.type == binding_assignment
|
||||
|| topState.type == case_cont) {
|
||||
*indentDepth = topState.savedIndentDepth;
|
||||
}
|
||||
break;
|
||||
case RightBrace: {
|
||||
if (topState.type == jsblock_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 == objectdefinition_open
|
||||
|| type == jsblock_open
|
||||
|| type == substatement_open) {
|
||||
*indentDepth = state(i).savedIndentDepth;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RightBracket:
|
||||
for (int i = 0; state(i).type != topmost_intro; ++i) {
|
||||
const int type = state(i).type;
|
||||
if (type == bracket_open) {
|
||||
*indentDepth = state(i).savedIndentDepth;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case LeftBracket:
|
||||
case LeftParenthesis:
|
||||
case Delimiter:
|
||||
if (topState.type == expression_maybe_continuation)
|
||||
*indentDepth = topState.savedIndentDepth;
|
||||
break;
|
||||
|
||||
case Else:
|
||||
if (topState.type == maybe_else) {
|
||||
*indentDepth = topState.savedIndentDepth;
|
||||
} else if (topState.type == expression_maybe_continuation) {
|
||||
bool hasElse = false;
|
||||
for (int i = 1; state(i).type != topmost_intro; ++i) {
|
||||
const int type = state(i).type;
|
||||
if (type == else_clause)
|
||||
hasElse = true;
|
||||
if (type == if_statement) {
|
||||
if (hasElse) {
|
||||
hasElse = false;
|
||||
} else {
|
||||
*indentDepth = state(i).savedIndentDepth;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Colon:
|
||||
if (topState.type == ternary_op) {
|
||||
*indentDepth -= 2;
|
||||
}
|
||||
break;
|
||||
|
||||
case Question:
|
||||
if (topState.type == expression_maybe_continuation)
|
||||
*indentDepth = topState.savedIndentDepth;
|
||||
break;
|
||||
|
||||
case Default:
|
||||
case 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;
|
||||
}
|
||||
}
|
69
src/plugins/qmljseditor/qmljseditorcodeformatter.h
Normal file
69
src/plugins/qmljseditor/qmljseditorcodeformatter.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef QMLJSEDITORCODEFORMATTER_H
|
||||
#define QMLJSEDITORCODEFORMATTER_H
|
||||
|
||||
#include "qmljseditor_global.h"
|
||||
|
||||
#include <texteditor/basetextdocumentlayout.h>
|
||||
#include <qmljs/qmljscodeformatter.h>
|
||||
|
||||
namespace QmlJSEditor {
|
||||
|
||||
class QMLJSEDITOR_EXPORT QtStyleCodeFormatter : public QmlJS::CodeFormatter
|
||||
{
|
||||
public:
|
||||
QtStyleCodeFormatter();
|
||||
|
||||
void setIndentSize(int size);
|
||||
|
||||
protected:
|
||||
virtual void onEnter(int newState, int *indentDepth, int *savedIndentDepth) const;
|
||||
virtual void adjustIndent(const QList<QmlJS::Token> &tokens, int lexerState, int *indentDepth) const;
|
||||
|
||||
virtual void saveBlockData(QTextBlock *block, const BlockData &data) const;
|
||||
virtual bool loadBlockData(const QTextBlock &block, BlockData *data) const;
|
||||
|
||||
virtual void saveLexerState(QTextBlock *block, int state) const;
|
||||
virtual int loadLexerState(const QTextBlock &block) const;
|
||||
|
||||
private:
|
||||
int m_indentSize;
|
||||
|
||||
class QmlJSCodeFormatterData: public TextEditor::CodeFormatterData
|
||||
{
|
||||
public:
|
||||
QmlJS::CodeFormatter::BlockData m_data;
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace QmlJSEditor
|
||||
|
||||
#endif // QMLJSEDITORCODEFORMATTER_H
|
24
tests/auto/qml/qmleditor/codeformatter/codeformatter.pro
Normal file
24
tests/auto/qml/qmleditor/codeformatter/codeformatter.pro
Normal file
@@ -0,0 +1,24 @@
|
||||
TEMPLATE = app
|
||||
CONFIG += qt warn_on console depend_includepath
|
||||
QT += testlib network
|
||||
|
||||
SRCDIR = ../../../../../src
|
||||
|
||||
#DEFINES += QML_BUILD_STATIC_LIB
|
||||
#include($$SRCDIR/../qtcreator.pri)
|
||||
include($$SRCDIR/libs/qmljs/qmljs-lib.pri)
|
||||
include($$SRCDIR/libs/utils/utils-lib.pri)
|
||||
#LIBS += -L$$IDE_LIBRARY_PATH
|
||||
|
||||
SOURCES += \
|
||||
tst_codeformatter.cpp \
|
||||
$$SRCDIR/plugins/qmljseditor/qmljseditorcodeformatter.cpp \
|
||||
$$SRCDIR/plugins/texteditor/basetextdocumentlayout.cpp
|
||||
|
||||
HEADERS += \
|
||||
$$SRCDIR/plugins/qmljseditor/qmljseditorcodeformatter.h \
|
||||
$$SRCDIR/plugins/texteditor/basetextdocumentlayout.h \
|
||||
|
||||
INCLUDEPATH += $$SRCDIR/plugins $$SRCDIR/libs
|
||||
|
||||
TARGET=tst_$$TARGET
|
888
tests/auto/qml/qmleditor/codeformatter/tst_codeformatter.cpp
Normal file
888
tests/auto/qml/qmleditor/codeformatter/tst_codeformatter.cpp
Normal file
@@ -0,0 +1,888 @@
|
||||
#include <QtTest>
|
||||
#include <QObject>
|
||||
#include <QList>
|
||||
#include <QTextDocument>
|
||||
#include <QTextBlock>
|
||||
|
||||
#include <qmljseditor/qmljseditorcodeformatter.h>
|
||||
|
||||
using namespace QmlJSEditor;
|
||||
|
||||
class tst_CodeFormatter: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void objectDefinitions1();
|
||||
void objectDefinitions2();
|
||||
void expressionEndSimple();
|
||||
void expressionEnd();
|
||||
void expressionEndBracket();
|
||||
void expressionEndParen();
|
||||
void objectBinding();
|
||||
void arrayBinding();
|
||||
void functionDeclaration();
|
||||
void functionExpression();
|
||||
void propertyDeclarations();
|
||||
void signalDeclarations();
|
||||
void ifBinding1();
|
||||
void ifBinding2();
|
||||
void ifStatementWithoutBraces1();
|
||||
void ifStatementWithoutBraces2();
|
||||
void ifStatementWithBraces1();
|
||||
void ifStatementWithBraces2();
|
||||
void ifStatementMixed();
|
||||
void ifStatementAndComments();
|
||||
void ifStatementLongCondition();
|
||||
void moreIfThenElse();
|
||||
void strayElse();
|
||||
void oneLineIf();
|
||||
void forStatement();
|
||||
void whileStatement();
|
||||
void tryStatement();
|
||||
void doWhile();
|
||||
void cStyleComments();
|
||||
void cppStyleComments();
|
||||
void qmlKeywords();
|
||||
void ternary();
|
||||
void switch1();
|
||||
// void gnuStyle();
|
||||
// void whitesmithsStyle();
|
||||
void expressionContinuation();
|
||||
};
|
||||
|
||||
struct Line {
|
||||
Line(QString l)
|
||||
: line(l)
|
||||
{
|
||||
for (int i = 0; i < l.size(); ++i) {
|
||||
if (!l.at(i).isSpace()) {
|
||||
expectedIndent = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
expectedIndent = l.size();
|
||||
}
|
||||
|
||||
Line(QString l, int expect)
|
||||
: line(l), expectedIndent(expect)
|
||||
{}
|
||||
|
||||
QString line;
|
||||
int expectedIndent;
|
||||
};
|
||||
|
||||
QString concatLines(QList<Line> lines)
|
||||
{
|
||||
QString result;
|
||||
foreach (const Line &l, lines) {
|
||||
result += l.line;
|
||||
result += "\n";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void checkIndent(QList<Line> data, int style = 0)
|
||||
{
|
||||
Q_UNUSED(style)
|
||||
|
||||
QString text = concatLines(data);
|
||||
QTextDocument document(text);
|
||||
QtStyleCodeFormatter formatter;
|
||||
|
||||
int i = 0;
|
||||
foreach (const Line &l, data) {
|
||||
QTextBlock b = document.findBlockByLineNumber(i);
|
||||
if (l.expectedIndent != -1) {
|
||||
int actualIndent = formatter.indentFor(b);
|
||||
if (actualIndent != l.expectedIndent) {
|
||||
QFAIL(QString("Wrong indent in line %1 with text '%2', expected indent %3, got %4").arg(
|
||||
QString::number(i+1), l.line, QString::number(l.expectedIndent), QString::number(actualIndent)).toLatin1().constData());
|
||||
}
|
||||
}
|
||||
formatter.updateLineStateChange(b);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
void tst_CodeFormatter::objectDefinitions1()
|
||||
{
|
||||
QList<Line> data;
|
||||
data << Line("import Qt 4.7")
|
||||
<< Line("")
|
||||
<< Line("Rectangle {")
|
||||
<< Line(" foo: bar;")
|
||||
<< Line(" Item {")
|
||||
<< Line(" x: 42;")
|
||||
<< Line(" y: x;")
|
||||
<< Line(" }")
|
||||
<< Line(" Component.onCompleted: foo;")
|
||||
<< Line(" ")
|
||||
<< Line(" Foo.Bar {")
|
||||
<< Line(" width: 12 + 54;")
|
||||
<< Line(" anchors.fill: parent;")
|
||||
<< Line(" }")
|
||||
<< Line("}")
|
||||
;
|
||||
checkIndent(data);
|
||||
}
|
||||
|
||||
void tst_CodeFormatter::objectDefinitions2()
|
||||
{
|
||||
QList<Line> data;
|
||||
data << Line("import Qt 4.7")
|
||||
<< Line("")
|
||||
<< Line("Rectangle {")
|
||||
<< Line(" foo: bar;")
|
||||
<< Line(" Image { source: \"a+b+c\"; x: 42; y: 12 }")
|
||||
<< Line(" Component.onCompleted: foo;")
|
||||
<< Line("}")
|
||||
;
|
||||
checkIndent(data);
|
||||
}
|
||||
|
||||
void tst_CodeFormatter::expressionEndSimple()
|
||||
{
|
||||
QList<Line> data;
|
||||
data << Line("Rectangle {")
|
||||
<< Line(" foo: bar +")
|
||||
<< Line(" foo(4, 5) +")
|
||||
<< Line(" 7")
|
||||
<< Line(" x: 42")
|
||||
<< Line(" y: 43")
|
||||
<< Line(" width: 10")
|
||||
<< Line("}")
|
||||
;
|
||||
checkIndent(data);
|
||||
}
|
||||
|
||||
void tst_CodeFormatter::expressionEnd()
|
||||
{
|
||||
QList<Line> data;
|
||||
data << Line("Rectangle {")
|
||||
<< Line(" foo: bar +")
|
||||
<< Line(" foo(4")
|
||||
<< Line(" + 5)")
|
||||
<< Line(" + 7")
|
||||
<< Line(" x: 42")
|
||||
<< Line(" + 43")
|
||||
<< Line(" + 10")
|
||||
<< Line("}")
|
||||
;
|
||||
checkIndent(data);
|
||||
}
|
||||
|
||||
void tst_CodeFormatter::expressionEndParen()
|
||||
{
|
||||
QList<Line> data;
|
||||
data << Line("Rectangle {")
|
||||
<< Line(" foo: bar")
|
||||
<< Line(" (foo(4")
|
||||
<< Line(" + 5)")
|
||||
<< Line(" + 7,")
|
||||
<< Line(" abc)")
|
||||
<< Line(" x: a + b(fpp, ba + 12) + foo(")
|
||||
<< Line(" bar,")
|
||||
<< Line(" 10)")
|
||||
<< Line(" + 10")
|
||||
<< Line("}")
|
||||
;
|
||||
checkIndent(data);
|
||||
}
|
||||
|
||||
void tst_CodeFormatter::expressionEndBracket()
|
||||
{
|
||||
QList<Line> data;
|
||||
data << Line("Rectangle {")
|
||||
<< Line(" foo: bar")
|
||||
<< Line(" [foo[4")
|
||||
<< Line(" + 5]")
|
||||
<< Line(" + 7,")
|
||||
<< Line(" abc]")
|
||||
<< Line(" x: a + b[fpp, ba + 12] + foo[")
|
||||
<< Line(" bar,")
|
||||
<< Line(" 10]")
|
||||
<< Line(" + 10")
|
||||
<< Line("}")
|
||||
;
|
||||
checkIndent(data);
|
||||
}
|
||||
|
||||
void tst_CodeFormatter::objectBinding()
|
||||
{
|
||||
QList<Line> data;
|
||||
data << Line("Rectangle {")
|
||||
<< Line(" foo: bar")
|
||||
<< Line(" x: 3")
|
||||
<< Line(" foo: Gradient {")
|
||||
<< Line(" x: 12")
|
||||
<< Line(" y: x")
|
||||
<< Line(" }")
|
||||
<< Line(" Item {")
|
||||
<< Line(" states: State {}")
|
||||
<< Line(" }")
|
||||
<< Line(" x: 1")
|
||||
<< Line("}")
|
||||
;
|
||||
checkIndent(data);
|
||||
}
|
||||
|
||||
void tst_CodeFormatter::arrayBinding()
|
||||
{
|
||||
QList<Line> data;
|
||||
data << Line("Rectangle {")
|
||||
<< Line(" foo: bar")
|
||||
<< Line(" x: 3")
|
||||
<< Line(" foo: [")
|
||||
<< Line(" State {")
|
||||
<< Line(" y: x")
|
||||
<< Line(" },")
|
||||
<< Line(" State")
|
||||
<< Line(" {")
|
||||
<< Line(" }")
|
||||
<< Line(" ]")
|
||||
<< Line(" foo: [")
|
||||
<< Line(" 1 +")
|
||||
<< Line(" 2")
|
||||
<< Line(" + 345 * foo(")
|
||||
<< Line(" bar, car,")
|
||||
<< Line(" dar),")
|
||||
<< Line(" x, y,")
|
||||
<< Line(" z,")
|
||||
<< Line(" ]")
|
||||
<< Line(" x: 1")
|
||||
<< Line("}")
|
||||
;
|
||||
checkIndent(data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void tst_CodeFormatter::moreIfThenElse()
|
||||
{
|
||||
QList<Line> data;
|
||||
data << Line("Image {")
|
||||
<< Line(" source: {")
|
||||
<< Line(" if(type == 1) {")
|
||||
<< Line(" \"pics/blueStone.png\";")
|
||||
<< Line(" } else if (type == 2) {")
|
||||
<< Line(" \"pics/head.png\";")
|
||||
<< Line(" } else {")
|
||||
<< Line(" \"pics/redStone.png\";")
|
||||
<< Line(" }")
|
||||
<< Line(" }")
|
||||
<< Line("}");
|
||||
checkIndent(data);
|
||||
}
|
||||
|
||||
|
||||
void tst_CodeFormatter::functionDeclaration()
|
||||
{
|
||||
QList<Line> data;
|
||||
data << Line("Rectangle {")
|
||||
<< Line(" foo: bar")
|
||||
<< Line(" function foo(a, b, c) {")
|
||||
<< Line(" if (a)")
|
||||
<< Line(" b;")
|
||||
<< Line(" }")
|
||||
<< Line(" property alias boo :")
|
||||
<< Line(" foo")
|
||||
<< Line(" Item {")
|
||||
<< Line(" property variant g : Gradient {")
|
||||
<< Line(" v: 12")
|
||||
<< Line(" }")
|
||||
<< Line(" function bar(")
|
||||
<< Line(" a, b,")
|
||||
<< Line(" c)")
|
||||
<< Line(" {")
|
||||
<< Line(" var b")
|
||||
<< Line(" }")
|
||||
<< Line(" function bar(a,")
|
||||
<< Line(" a, b,")
|
||||
<< Line(" c)")
|
||||
<< Line(" {")
|
||||
<< Line(" var b")
|
||||
<< Line(" }")
|
||||
<< Line(" }")
|
||||
<< Line(" x: 1")
|
||||
<< Line("}")
|
||||
;
|
||||
checkIndent(data);
|
||||
}
|
||||
|
||||
void tst_CodeFormatter::functionExpression()
|
||||
{
|
||||
QList<Line> data;
|
||||
data << Line("Rectangle {")
|
||||
<< Line("onFoo: {", 4)
|
||||
<< Line(" function foo(a, b, c) {")
|
||||
<< Line(" if (a)")
|
||||
<< Line(" b;")
|
||||
<< Line(" }")
|
||||
<< Line(" return function(a, b) { return a + b; }")
|
||||
<< Line(" return function foo(a, b) {")
|
||||
<< Line(" return a")
|
||||
<< Line(" }")
|
||||
<< Line("}")
|
||||
<< Line("}")
|
||||
;
|
||||
checkIndent(data);
|
||||
}
|
||||
|
||||
void tst_CodeFormatter::propertyDeclarations()
|
||||
{
|
||||
QList<Line> data;
|
||||
data << Line("Rectangle {")
|
||||
<< Line(" foo: bar")
|
||||
<< Line(" property int foo : 2 +")
|
||||
<< Line(" x")
|
||||
<< Line(" property list<Foo> bar")
|
||||
<< Line(" property alias boo :")
|
||||
<< Line(" foo")
|
||||
<< Line(" Item {")
|
||||
<< Line(" property variant g : Gradient {")
|
||||
<< Line(" v: 12")
|
||||
<< Line(" }")
|
||||
<< Line(" default property Item g")
|
||||
<< Line(" default property Item g : parent.foo")
|
||||
<< Line(" }")
|
||||
<< Line(" x: 1")
|
||||
<< Line("}")
|
||||
;
|
||||
checkIndent(data);
|
||||
}
|
||||
|
||||
void tst_CodeFormatter::signalDeclarations()
|
||||
{
|
||||
QList<Line> data;
|
||||
data << Line("Rectangle {")
|
||||
<< Line(" foo: bar")
|
||||
<< Line(" signal foo")
|
||||
<< Line(" x: bar")
|
||||
<< Line(" signal bar(a, int b)")
|
||||
<< Line(" signal bar2()")
|
||||
<< Line(" Item {")
|
||||
<< Line(" signal property")
|
||||
<< Line(" signal import(a, b);")
|
||||
<< Line(" }")
|
||||
<< Line(" x: 1")
|
||||
<< Line("}")
|
||||
;
|
||||
checkIndent(data);
|
||||
}
|
||||
|
||||
void tst_CodeFormatter::ifBinding1()
|
||||
{
|
||||
QList<Line> data;
|
||||
data << Line("A.Rectangle {")
|
||||
<< Line(" foo: bar")
|
||||
<< Line(" x: if (a) b")
|
||||
<< Line(" x: if (a)")
|
||||
<< Line(" b")
|
||||
<< Line(" x: if (a) b;")
|
||||
<< Line(" x: if (a)")
|
||||
<< Line(" b;")
|
||||
<< Line(" x: if (a) b; else c")
|
||||
<< Line(" x: if (a) b")
|
||||
<< Line(" else c")
|
||||
<< Line(" x: if (a) b;")
|
||||
<< Line(" else c")
|
||||
<< Line(" x: if (a) b;")
|
||||
<< Line(" else")
|
||||
<< Line(" c")
|
||||
<< Line(" x: if (a)")
|
||||
<< Line(" b")
|
||||
<< Line(" else")
|
||||
<< Line(" c")
|
||||
<< Line(" x: if (a) b; else c;")
|
||||
<< Line(" x: 1")
|
||||
<< Line("}")
|
||||
;
|
||||
checkIndent(data);
|
||||
}
|
||||
|
||||
void tst_CodeFormatter::ifBinding2()
|
||||
{
|
||||
QList<Line> data;
|
||||
data << Line("A.Rectangle {")
|
||||
<< Line(" foo: bar")
|
||||
<< Line(" x: if (a) b +")
|
||||
<< Line(" 5 +")
|
||||
<< Line(" 5 * foo(")
|
||||
<< Line(" 1, 2)")
|
||||
<< Line(" else a =")
|
||||
<< Line(" foo(15,")
|
||||
<< Line(" bar(")
|
||||
<< Line(" 1),")
|
||||
<< Line(" bar)")
|
||||
<< Line(" x: if (a) b")
|
||||
<< Line(" + 5")
|
||||
<< Line(" + 5")
|
||||
<< Line(" x: if (a)")
|
||||
<< Line(" b")
|
||||
<< Line(" + 5")
|
||||
<< Line(" + 5")
|
||||
<< Line("}")
|
||||
;
|
||||
checkIndent(data);
|
||||
}
|
||||
|
||||
void tst_CodeFormatter::ifStatementWithoutBraces1()
|
||||
{
|
||||
QList<Line> data;
|
||||
data << Line("Rectangle {")
|
||||
<< Line(" x: if (a)")
|
||||
<< Line(" if (b)")
|
||||
<< Line(" foo")
|
||||
<< Line(" else if (c)")
|
||||
<< Line(" foo")
|
||||
<< Line(" else")
|
||||
<< Line(" if (d)")
|
||||
<< Line(" foo;")
|
||||
<< Line(" else")
|
||||
<< Line(" a + b + ")
|
||||
<< Line(" c")
|
||||
<< Line(" else")
|
||||
<< Line(" foo;")
|
||||
<< Line(" y: 2")
|
||||
<< Line("}")
|
||||
;
|
||||
checkIndent(data);
|
||||
}
|
||||
|
||||
void tst_CodeFormatter::ifStatementWithoutBraces2()
|
||||
{
|
||||
QList<Line> data;
|
||||
data << Line("Rectangle {")
|
||||
<< Line(" x: {")
|
||||
<< Line(" if (a)")
|
||||
<< Line(" if (b)")
|
||||
<< Line(" foo;")
|
||||
<< Line(" if (a) b();")
|
||||
<< Line(" if (a) b(); else")
|
||||
<< Line(" foo;")
|
||||
<< Line(" if (a)")
|
||||
<< Line(" if (b)")
|
||||
<< Line(" foo;")
|
||||
<< Line(" else if (c)")
|
||||
<< Line(" foo;")
|
||||
<< Line(" else")
|
||||
<< Line(" if (d)")
|
||||
<< Line(" foo;")
|
||||
<< Line(" else")
|
||||
<< Line(" e")
|
||||
<< Line(" else")
|
||||
<< Line(" foo;")
|
||||
<< Line(" }")
|
||||
<< Line(" foo: bar")
|
||||
<< Line("}")
|
||||
;
|
||||
checkIndent(data);
|
||||
}
|
||||
|
||||
void tst_CodeFormatter::ifStatementWithBraces1()
|
||||
{
|
||||
QList<Line> data;
|
||||
data << Line("Rectangle {")
|
||||
<< Line("onClicked: {", 4)
|
||||
<< Line(" if (a) {")
|
||||
<< Line(" if (b) {")
|
||||
<< Line(" foo;")
|
||||
<< Line(" } else if (c) {")
|
||||
<< Line(" foo")
|
||||
<< Line(" } else {")
|
||||
<< Line(" if (d) {")
|
||||
<< Line(" foo")
|
||||
<< Line(" } else {")
|
||||
<< Line(" foo;")
|
||||
<< Line(" }")
|
||||
<< Line(" }")
|
||||
<< Line(" } else {")
|
||||
<< Line(" foo;")
|
||||
<< Line(" }")
|
||||
<< Line("}")
|
||||
<< Line("}");
|
||||
checkIndent(data);
|
||||
}
|
||||
|
||||
void tst_CodeFormatter::ifStatementWithBraces2()
|
||||
{
|
||||
QList<Line> data;
|
||||
data << Line("Rectangle {")
|
||||
<< Line("onClicked:", 4)
|
||||
<< Line(" if (a)")
|
||||
<< Line(" {")
|
||||
<< Line(" if (b)")
|
||||
<< Line(" {")
|
||||
<< Line(" foo")
|
||||
<< Line(" }")
|
||||
<< Line(" else if (c)")
|
||||
<< Line(" {")
|
||||
<< Line(" foo;")
|
||||
<< Line(" }")
|
||||
<< Line(" else")
|
||||
<< Line(" {")
|
||||
<< Line(" if (d)")
|
||||
<< Line(" {")
|
||||
<< Line(" foo;")
|
||||
<< Line(" }")
|
||||
<< Line(" else")
|
||||
<< Line(" {")
|
||||
<< Line(" foo")
|
||||
<< Line(" }")
|
||||
<< Line(" }")
|
||||
<< Line(" }")
|
||||
<< Line(" else")
|
||||
<< Line(" {")
|
||||
<< Line(" foo")
|
||||
<< Line(" }")
|
||||
<< Line("}");
|
||||
checkIndent(data);
|
||||
}
|
||||
|
||||
void tst_CodeFormatter::ifStatementMixed()
|
||||
{
|
||||
QList<Line> data;
|
||||
data << Line("Rectangle {")
|
||||
<< Line("onClicked:", 4)
|
||||
<< Line(" if (foo)")
|
||||
<< Line(" if (bar)")
|
||||
<< Line(" {")
|
||||
<< Line(" foo;")
|
||||
<< Line(" }")
|
||||
<< Line(" else")
|
||||
<< Line(" if (car)")
|
||||
<< Line(" {}")
|
||||
<< Line(" else doo")
|
||||
<< Line(" else abc")
|
||||
<< Line("}");
|
||||
checkIndent(data);
|
||||
}
|
||||
|
||||
void tst_CodeFormatter::ifStatementAndComments()
|
||||
{
|
||||
QList<Line> data;
|
||||
data << Line("Rectangle {")
|
||||
<< Line("onClicked: {", 4)
|
||||
<< Line(" if (foo)")
|
||||
<< Line(" ; // bla")
|
||||
<< Line(" else if (bar)")
|
||||
<< Line(" ;")
|
||||
<< Line(" if (foo)")
|
||||
<< Line(" ; /*bla")
|
||||
<< Line(" bla */")
|
||||
<< Line(" else if (bar)")
|
||||
<< Line(" // foobar")
|
||||
<< Line(" ;")
|
||||
<< Line(" else if (bar)")
|
||||
<< Line(" /* bla")
|
||||
<< Line(" bla */")
|
||||
<< Line(" ;")
|
||||
<< Line("}")
|
||||
<< Line("}");
|
||||
checkIndent(data);
|
||||
}
|
||||
|
||||
void tst_CodeFormatter::ifStatementLongCondition()
|
||||
{
|
||||
QList<Line> data;
|
||||
data << Line("Rectangle {")
|
||||
<< Line("onClicked: {", 4)
|
||||
<< Line(" if (foo &&")
|
||||
<< Line(" bar")
|
||||
<< Line(" || (a + b > 4")
|
||||
<< Line(" && foo(bar)")
|
||||
<< Line(" )")
|
||||
<< Line(" ) {")
|
||||
<< Line(" foo;")
|
||||
<< Line(" }")
|
||||
<< Line("}");
|
||||
checkIndent(data);
|
||||
}
|
||||
|
||||
void tst_CodeFormatter::strayElse()
|
||||
{
|
||||
QList<Line> data;
|
||||
data << Line("Rectangle {")
|
||||
<< Line("onClicked: {", 4)
|
||||
<< Line(" while( true ) {}")
|
||||
<< Line(" else", -1)
|
||||
<< Line(" else {", -1)
|
||||
<< Line(" }", -1)
|
||||
<< Line("}");
|
||||
checkIndent(data);
|
||||
}
|
||||
|
||||
void tst_CodeFormatter::oneLineIf()
|
||||
{
|
||||
QList<Line> data;
|
||||
data << Line("Rectangle {")
|
||||
<< Line(" onClicked: { if (showIt) show(); }")
|
||||
<< Line(" x: 2")
|
||||
<< Line("};")
|
||||
;
|
||||
checkIndent(data);
|
||||
}
|
||||
|
||||
void tst_CodeFormatter::forStatement()
|
||||
{
|
||||
QList<Line> data;
|
||||
data << Line("for (var i = 0; i < 20; ++i) {")
|
||||
<< Line(" print(i);")
|
||||
<< Line("}")
|
||||
<< Line("for (var x in [a, b, c, d])")
|
||||
<< Line(" x += 5")
|
||||
<< Line("var z")
|
||||
<< Line("for (var x in [a, b, c, d])")
|
||||
<< Line(" for (;;)")
|
||||
<< Line(" {")
|
||||
<< Line(" for (a(); b(); c())")
|
||||
<< Line(" for (a();")
|
||||
<< Line(" b(); c())")
|
||||
<< Line(" for (a(); b(); c())")
|
||||
<< Line(" print(3*d)")
|
||||
<< Line(" }")
|
||||
<< Line("z = 2")
|
||||
;
|
||||
checkIndent(data);
|
||||
}
|
||||
|
||||
void tst_CodeFormatter::whileStatement()
|
||||
{
|
||||
QList<Line> data;
|
||||
data << Line("while (i < 20) {")
|
||||
<< Line(" print(i);")
|
||||
<< Line("}")
|
||||
<< Line("while (x in [a, b, c, d])")
|
||||
<< Line(" x += 5")
|
||||
<< Line("var z")
|
||||
<< Line("while (a + b > 0")
|
||||
<< Line(" && b + c > 0)")
|
||||
<< Line(" for (;;)")
|
||||
<< Line(" {")
|
||||
<< Line(" for (a(); b(); c())")
|
||||
<< Line(" while (a())")
|
||||
<< Line(" for (a(); b(); c())")
|
||||
<< Line(" print(3*d)")
|
||||
<< Line(" }")
|
||||
<< Line("z = 2")
|
||||
;
|
||||
checkIndent(data);
|
||||
}
|
||||
|
||||
void tst_CodeFormatter::tryStatement()
|
||||
{
|
||||
QList<Line> data;
|
||||
data << Line("try {")
|
||||
<< Line(" print(i);")
|
||||
<< Line("} catch (foo) {")
|
||||
<< Line(" print(foo)")
|
||||
<< Line("} finally {")
|
||||
<< Line(" var z")
|
||||
<< Line(" while (a + b > 0")
|
||||
<< Line(" && b + c > 0)")
|
||||
<< Line(" ;")
|
||||
<< Line("}")
|
||||
<< Line("z = 2")
|
||||
;
|
||||
checkIndent(data);
|
||||
}
|
||||
|
||||
void tst_CodeFormatter::doWhile()
|
||||
{
|
||||
QList<Line> data;
|
||||
data << Line("function foo() {")
|
||||
<< Line(" do { if (c) foo; } while(a);")
|
||||
<< Line(" do {")
|
||||
<< Line(" if(a);")
|
||||
<< Line(" } while(a);")
|
||||
<< Line(" do")
|
||||
<< Line(" foo;")
|
||||
<< Line(" while(a);")
|
||||
<< Line(" do foo; while(a);")
|
||||
<< Line("};")
|
||||
;
|
||||
checkIndent(data);
|
||||
}
|
||||
|
||||
void tst_CodeFormatter::cStyleComments()
|
||||
{
|
||||
QList<Line> data;
|
||||
data << Line("/*")
|
||||
<< Line(" ")
|
||||
<< Line(" foo")
|
||||
<< Line(" ")
|
||||
<< Line(" foo")
|
||||
<< Line(" ")
|
||||
<< Line("*/")
|
||||
<< Line("Rectangle {")
|
||||
<< Line(" /*")
|
||||
<< Line(" ")
|
||||
<< Line(" foo")
|
||||
<< Line(" ")
|
||||
<< Line(" */")
|
||||
<< Line(" /* bar */")
|
||||
<< Line("}")
|
||||
<< Line("Item {")
|
||||
<< Line(" /* foo */")
|
||||
<< Line(" /*")
|
||||
<< Line(" ")
|
||||
<< Line(" foo")
|
||||
<< Line(" ")
|
||||
<< Line(" */")
|
||||
<< Line(" /* bar */")
|
||||
;
|
||||
checkIndent(data);
|
||||
}
|
||||
|
||||
void tst_CodeFormatter::cppStyleComments()
|
||||
{
|
||||
QList<Line> data;
|
||||
data << Line("// abc")
|
||||
<< Line("Item { ")
|
||||
<< Line(" // ghij")
|
||||
<< Line(" // def")
|
||||
<< Line(" // ghij")
|
||||
<< Line(" x: 4 // hik")
|
||||
<< Line(" // doo")
|
||||
<< Line("} // ba")
|
||||
<< Line("// ba")
|
||||
;
|
||||
checkIndent(data);
|
||||
}
|
||||
|
||||
void tst_CodeFormatter::ternary()
|
||||
{
|
||||
QList<Line> data;
|
||||
data << Line("function foo() {")
|
||||
<< Line(" var i = a ? b : c;")
|
||||
<< Line(" foo += a_bigger_condition ?")
|
||||
<< Line(" b")
|
||||
<< Line(" : c;")
|
||||
<< Line(" foo += a_bigger_condition")
|
||||
<< Line(" ? b")
|
||||
<< Line(" : c;")
|
||||
<< Line(" var i = a ?")
|
||||
<< Line(" b : c;")
|
||||
<< Line(" var i = aooo ? b")
|
||||
<< Line(" : c +")
|
||||
<< Line(" 2;")
|
||||
<< Line(" var i = (a ? b : c) + (foo")
|
||||
<< Line(" bar);")
|
||||
<< Line("}")
|
||||
;
|
||||
checkIndent(data);
|
||||
}
|
||||
|
||||
void tst_CodeFormatter::switch1()
|
||||
{
|
||||
QList<Line> data;
|
||||
data << Line("function foo() {")
|
||||
<< Line(" switch (a) {")
|
||||
<< Line(" case 1:")
|
||||
<< Line(" foo;")
|
||||
<< Line(" if (a);")
|
||||
<< Line(" case 2:")
|
||||
<< Line(" case 3: {")
|
||||
<< Line(" foo")
|
||||
<< Line(" }")
|
||||
<< Line(" case 4:")
|
||||
<< Line(" {")
|
||||
<< Line(" foo;")
|
||||
<< Line(" }")
|
||||
<< Line(" case bar:")
|
||||
<< Line(" break")
|
||||
<< Line(" }")
|
||||
<< Line(" case 4:")
|
||||
<< Line(" {")
|
||||
<< Line(" if (a) {")
|
||||
<< Line(" }")
|
||||
<< Line(" }")
|
||||
<< Line("}")
|
||||
;
|
||||
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(" else if (b)")
|
||||
// << Line(" {")
|
||||
// << Line(" fpp;")
|
||||
// << Line(" }")
|
||||
// << Line(" else")
|
||||
// << Line(" {")
|
||||
// << 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);
|
||||
//}
|
||||
|
||||
void tst_CodeFormatter::qmlKeywords()
|
||||
{
|
||||
QList<Line> data;
|
||||
data << Line("Rectangle {")
|
||||
<< Line(" on: 2")
|
||||
<< Line(" property: 2")
|
||||
<< Line(" signal: 2")
|
||||
<< Line(" list: 2")
|
||||
<< Line(" as: 2")
|
||||
<< Line(" import: 2")
|
||||
<< Line(" Item {")
|
||||
<< Line(" }")
|
||||
<< Line(" x: 2")
|
||||
<< Line("};")
|
||||
;
|
||||
checkIndent(data);
|
||||
}
|
||||
|
||||
void tst_CodeFormatter::expressionContinuation()
|
||||
{
|
||||
QList<Line> data;
|
||||
data << Line("var x = 1 ? 2")
|
||||
<< Line(" + 3 : 4")
|
||||
<< Line("++x")
|
||||
<< Line("++y--")
|
||||
<< Line("x +=")
|
||||
<< Line(" y++")
|
||||
<< Line("var z")
|
||||
;
|
||||
checkIndent(data);
|
||||
}
|
||||
|
||||
QTEST_APPLESS_MAIN(tst_CodeFormatter)
|
||||
#include "tst_codeformatter.moc"
|
||||
|
||||
|
@@ -1,3 +1,3 @@
|
||||
TEMPLATE = subdirs
|
||||
|
||||
SUBDIRS += lookup
|
||||
SUBDIRS += lookup codeformatter
|
||||
|
Reference in New Issue
Block a user