Files
qt-creator/src/libs/qmljs/qmljsreformatter.cpp
Eike Ziller ea27143239 Merge remote-tracking branch 'origin/3.2'
Conflicts:
	src/libs/utils/ipaddresslineedit.cpp
	src/libs/utils/logging.h
	src/plugins/analyzerbase/AnalyzerBase.pluginspec.in
	src/plugins/android/Android.pluginspec.in
	src/plugins/android/androiddeploystep.cpp
	src/plugins/android/androiddeploystep.h
	src/plugins/android/androiddeploystepfactory.cpp
	src/plugins/android/androiddeploystepwidget.cpp
	src/plugins/android/androidpackagecreationfactory.cpp
	src/plugins/android/androidpackagecreationstep.cpp
	src/plugins/android/androidpackagecreationstep.h
	src/plugins/android/androidpackagecreationwidget.cpp
	src/plugins/android/androidpackagecreationwidget.h
	src/plugins/android/javafilewizard.cpp
	src/plugins/autotoolsprojectmanager/AutotoolsProjectManager.pluginspec.in
	src/plugins/baremetal/BareMetal.pluginspec.in
	src/plugins/bazaar/Bazaar.pluginspec.in
	src/plugins/beautifier/Beautifier.pluginspec.in
	src/plugins/bineditor/BinEditor.pluginspec.in
	src/plugins/bookmarks/Bookmarks.pluginspec.in
	src/plugins/clangcodemodel/ClangCodeModel.pluginspec.in
	src/plugins/clangcodemodel/clanghighlightingsupport.cpp
	src/plugins/clangcodemodel/clangsymbolsearcher.cpp
	src/plugins/classview/ClassView.pluginspec.in
	src/plugins/clearcase/ClearCase.pluginspec.in
	src/plugins/cmakeprojectmanager/CMakeProjectManager.pluginspec.in
	src/plugins/cmakeprojectmanager/cmakeeditorfactory.cpp
	src/plugins/cmakeprojectmanager/cmakehighlighter.cpp
	src/plugins/coreplugin/Core.pluginspec.in
	src/plugins/cpaster/CodePaster.pluginspec.in
	src/plugins/cppeditor/CppEditor.pluginspec.in
	src/plugins/cppeditor/cppfilewizard.cpp
	src/plugins/cpptools/CppTools.pluginspec.in
	src/plugins/cpptools/cpphighlightingsupportinternal.cpp
	src/plugins/cpptools/cppmodelmanagerinterface.cpp
	src/plugins/cpptools/cppmodelmanagerinterface.h
	src/plugins/cvs/CVS.pluginspec.in
	src/plugins/debugger/Debugger.pluginspec.in
	src/plugins/designer/Designer.pluginspec.in
	src/plugins/diffeditor/DiffEditor.pluginspec.in
	src/plugins/emacskeys/EmacsKeys.pluginspec.in
	src/plugins/fakevim/FakeVim.pluginspec.in
	src/plugins/genericprojectmanager/GenericProjectManager.pluginspec.in
	src/plugins/git/Git.pluginspec.in
	src/plugins/git/gitorious/gitorious.cpp
	src/plugins/git/gitorious/gitorious.h
	src/plugins/git/gitorious/gitoriousclonewizard.cpp
	src/plugins/git/gitorious/gitorioushostwidget.cpp
	src/plugins/git/gitorious/gitorioushostwidget.h
	src/plugins/git/gitorious/gitorioushostwizardpage.cpp
	src/plugins/git/gitorious/gitoriousprojectwidget.cpp
	src/plugins/git/gitorious/gitoriousprojectwidget.h
	src/plugins/git/gitorious/gitoriousprojectwizardpage.cpp
	src/plugins/git/gitorious/gitoriousprojectwizardpage.h
	src/plugins/git/gitorious/gitoriousrepositorywizardpage.cpp
	src/plugins/git/gitorious/gitoriousrepositorywizardpage.h
	src/plugins/glsleditor/GLSLEditor.pluginspec.in
	src/plugins/glsleditor/glsleditorfactory.cpp
	src/plugins/glsleditor/glslfilewizard.cpp
	src/plugins/helloworld/HelloWorld.pluginspec.in
	src/plugins/help/Help.pluginspec.in
	src/plugins/imageviewer/ImageViewer.pluginspec.in
	src/plugins/ios/Ios.pluginspec.in
	src/plugins/macros/Macros.pluginspec.in
	src/plugins/mercurial/Mercurial.pluginspec.in
	src/plugins/perforce/Perforce.pluginspec.in
	src/plugins/projectexplorer/ProjectExplorer.pluginspec.in
	src/plugins/pythoneditor/PythonEditor.pluginspec.in
	src/plugins/pythoneditor/pythoneditorwidget.cpp
	src/plugins/pythoneditor/wizard/pythonfilewizard.cpp
	src/plugins/qbsprojectmanager/QbsProjectManager.pluginspec.in
	src/plugins/qbsprojectmanager/defaultpropertyprovider.cpp
	src/plugins/qmakeprojectmanager/QmakeProjectManager.pluginspec.in
	src/plugins/qmakeprojectmanager/profileeditorfactory.cpp
	src/plugins/qmldesigner/QmlDesigner.pluginspec.in
	src/plugins/qmljseditor/QmlJSEditor.pluginspec.in
	src/plugins/qmljseditor/qmljseditorfactory.cpp
	src/plugins/qmljstools/QmlJSTools.pluginspec.in
	src/plugins/qmlprofiler/QmlProfiler.pluginspec.in
	src/plugins/qmlprojectmanager/QmlProjectManager.pluginspec.in
	src/plugins/qnx/Qnx.pluginspec.in
	src/plugins/qtsupport/QtSupport.pluginspec.in
	src/plugins/remotelinux/RemoteLinux.pluginspec.in
	src/plugins/resourceeditor/ResourceEditor.pluginspec.in
	src/plugins/resourceeditor/resourcewizard.h
	src/plugins/subversion/Subversion.pluginspec.in
	src/plugins/tasklist/TaskList.pluginspec.in
	src/plugins/texteditor/TextEditor.pluginspec.in
	src/plugins/texteditor/basetexteditor_p.h
	src/plugins/texteditor/basetextmark.cpp
	src/plugins/texteditor/codeassist/basicproposalitemlistmodel.h
	src/plugins/texteditor/codeassist/defaultassistinterface.h
	src/plugins/texteditor/codeassist/iassistproposalitem.cpp
	src/plugins/texteditor/itexteditor.cpp
	src/plugins/texteditor/itexteditor.h
	src/plugins/texteditor/itextmark.cpp
	src/plugins/texteditor/plaintexteditor.cpp
	src/plugins/texteditor/plaintexteditor.h
	src/plugins/texteditor/texteditoractionhandler.cpp
	src/plugins/todo/Todo.pluginspec.in
	src/plugins/updateinfo/UpdateInfo.pluginspec.in
	src/plugins/valgrind/Valgrind.pluginspec.in
	src/plugins/vcsbase/VcsBase.pluginspec.in
	src/plugins/welcome/Welcome.pluginspec.in
	src/plugins/winrt/WinRt.pluginspec.in
	tests/auto/debugger/temporarydir.h

Change-Id: I254af8be8119fe9855287909e17d4b8ca9d2fc2f
2014-10-14 15:36:16 +02:00

1285 lines
35 KiB
C++

/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://www.qt.io/licensing. For further information
** use the contact form at http://www.qt.io/contact-us.
**
** 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 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "qmljsreformatter.h"
#include "qmljscodeformatter.h"
#include "parser/qmljsast_p.h"
#include "parser/qmljsastvisitor_p.h"
#include <QString>
#include <QTextBlock>
#include <QTextDocument>
#include <QTextCursor>
#include <limits>
using namespace QmlJS;
using namespace QmlJS::AST;
namespace {
class SimpleFormatter : public QtStyleCodeFormatter
{
protected:
class FormatterData : public QTextBlockUserData
{
public:
FormatterData(const BlockData &data) : data(data) {}
BlockData data;
};
virtual void saveBlockData(QTextBlock *block, const BlockData &data) const
{
block->setUserData(new FormatterData(data));
}
virtual bool loadBlockData(const QTextBlock &block, BlockData *data) const
{
if (!block.userData())
return false;
*data = static_cast<FormatterData *>(block.userData())->data;
return true;
}
virtual void saveLexerState(QTextBlock *block, int state) const
{
block->setUserState(state);
}
virtual int loadLexerState(const QTextBlock &block) const
{
return block.userState();
}
};
class Rewriter : protected Visitor
{
Document::Ptr _doc;
QString _result;
QString _line;
class Split {
public:
int offset;
qreal badness;
};
QList<Split> _possibleSplits;
QTextDocument _resultDocument;
SimpleFormatter _formatter;
int _indent;
int _nextComment;
int _lastNewlineOffset;
bool _hadEmptyLine;
int _binaryExpDepth;
public:
Rewriter(Document::Ptr doc)
: _doc(doc)
{
}
QString operator()(Node *node)
{
Q_ASSERT(node == _doc->ast()); // comment handling fails otherwise
_result.reserve(_doc->source().size());
_line.clear();
_possibleSplits.clear();
_indent = 0;
_nextComment = 0;
_lastNewlineOffset = -1;
_hadEmptyLine = false;
_binaryExpDepth = 0;
accept(node);
// emit the final comments
const QList<SourceLocation> &comments = _doc->engine()->comments();
for (; _nextComment < comments.size(); ++_nextComment) {
outComment(comments.at(_nextComment));
}
// ensure good ending
if (!_result.endsWith(QLatin1String("\n\n")) || !_line.isEmpty())
newLine();
return _result;
}
protected:
void accept(Node *node)
{
Node::accept(node, this);
}
void lnAcceptIndented(Node *node)
{
newLine();
accept(node);
}
void out(const char *str, const SourceLocation &lastLoc = SourceLocation())
{
out(QString::fromLatin1(str), lastLoc);
}
void outComment(const SourceLocation &commentLoc)
{
SourceLocation fixedLoc = commentLoc;
fixCommentLocation(fixedLoc);
if (precededByEmptyLine(fixedLoc))
newLine();
out(toString(fixedLoc)); // don't use the sourceloc overload here
if (followedByNewLine(fixedLoc))
newLine();
else
out(" ");
}
void out(const QString &str, const SourceLocation &lastLoc = SourceLocation())
{
if (lastLoc.isValid()) {
QList<SourceLocation> comments = _doc->engine()->comments();
for (; _nextComment < comments.size(); ++_nextComment) {
SourceLocation commentLoc = comments.at(_nextComment);
if (commentLoc.end() > lastLoc.end())
break;
outComment(commentLoc);
}
}
QStringList lines = str.split(QLatin1Char('\n'));
for (int i = 0; i < lines.size(); ++i) {
_line += lines.at(i);
if (i != lines.size() - 1)
newLine();
}
_hadEmptyLine = false;
}
QString toString(const SourceLocation &loc)
{
return _doc->source().mid(loc.offset, loc.length);
}
void out(const SourceLocation &loc)
{
if (!loc.isValid())
return;
out(toString(loc), loc);
}
int tryIndent(const QString &line)
{
// append the line to the text document
QTextCursor cursor(&_resultDocument);
cursor.movePosition(QTextCursor::End);
int cursorStartLinePos = cursor.position();
cursor.insertText(line);
// get the expected indentation
QTextBlock last = _resultDocument.lastBlock();
_formatter.updateStateUntil(last);
int indent = _formatter.indentFor(last);
// remove the line again
cursor.setPosition(cursorStartLinePos);
cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
cursor.removeSelectedText();
return indent;
}
void finishLine()
{
// remove trailing spaces
int len = _line.size();
while (len > 0 && _line.at(len - 1).isSpace())
--len;
_line.resize(len);
_line += QLatin1Char('\n');
_result += _line;
QTextCursor cursor(&_resultDocument);
cursor.movePosition(QTextCursor::End);
cursor.insertText(_line);
_line = QString(_indent, QLatin1Char(' '));
}
class BestSplit {
public:
QStringList lines;
qreal badnessFromSplits;
qreal badness()
{
const int maxLineLength = 80;
const int strongMaxLineLength = 100;
const int minContentLength = 10;
qreal result = badnessFromSplits;
foreach (const QString &line, lines) {
// really long lines should be avoided at all cost
if (line.size() > strongMaxLineLength) {
result += 50 + (line.size() - strongMaxLineLength);
// having long lines is bad
} else if (line.size() > maxLineLength) {
result += 3 + (line.size() - maxLineLength);
// and even ok-sized lines should have a cost
} else {
result += 1;
}
// having lines with little content is bad
const int contentSize = line.trimmed().size();
if (contentSize < minContentLength)
result += 0.5;
}
return result;
}
};
void adjustIndent(QString *line, QList<Split> *splits, int indent)
{
int startSpaces = 0;
while (startSpaces < line->size() && line->at(startSpaces).isSpace())
++startSpaces;
line->replace(0, startSpaces, QString(indent, QLatin1Char(' ')));
for (int i = 0; i < splits->size(); ++i) {
(*splits)[i].offset = splits->at(i).offset - startSpaces + indent;
}
}
BestSplit computeBestSplits(QStringList context, QString line, QList<Split> possibleSplits)
{
BestSplit result;
result.badnessFromSplits = 0;
result.lines = QStringList(line);
//qCDebug(qmljsLog) << "trying to split" << line << possibleSplits << context;
// try to split at every possible position
for (int i = possibleSplits.size() - 1; i >= 0; --i) {
const int splitPos = possibleSplits.at(i).offset;
const QString newContextLine = line.left(splitPos);
QStringList newContext = context;
newContext += newContextLine;
const QString restLine = line.mid(splitPos);
if (restLine.trimmed().isEmpty())
continue;
// the extra space is to avoid // comments sticking to the 0 offset
QString indentLine = newContext.join(QLatin1Char('\n')) + QLatin1String("\n ") + restLine;
int indent = tryIndent(indentLine);
QList<Split> newSplits = possibleSplits.mid(i + 1);
QString newSplitLine = restLine;
adjustIndent(&newSplitLine, &newSplits, indent);
for (int j = 0; j < newSplits.size(); ++j)
newSplits[j].offset = newSplits.at(j).offset - splitPos;
BestSplit nested = computeBestSplits(newContext, newSplitLine, newSplits);
nested.lines.prepend(newContextLine);
nested.badnessFromSplits += possibleSplits.at(i).badness;
if (nested.badness() < result.badness())
result = nested;
}
return result;
}
void newLine()
{
// if preceded by a newline, it's an empty line!
_hadEmptyLine = _line.trimmed().isEmpty();
// if the preceding line wasn't empty, reindent etc.
if (!_hadEmptyLine) {
int indentStart = 0;
while (indentStart < _line.size() && _line.at(indentStart).isSpace())
++indentStart;
_indent = tryIndent(_line);
adjustIndent(&_line, &_possibleSplits, _indent);
// maybe make multi-line?
BestSplit split = computeBestSplits(QStringList(), _line, _possibleSplits);
if (!split.lines.isEmpty() && split.lines.size() > 1) {
for (int i = 0; i < split.lines.size(); ++i) {
_line = split.lines.at(i);
if (i != split.lines.size() - 1)
finishLine();
}
}
}
finishLine();
_possibleSplits.clear();
}
void requireEmptyLine()
{
while (!_hadEmptyLine)
newLine();
}
bool acceptBlockOrIndented(Node *ast, bool finishWithSpaceOrNewline = false)
{
if (cast<Block *>(ast)) {
out(" ");
accept(ast);
if (finishWithSpaceOrNewline)
out(" ");
return true;
} else {
lnAcceptIndented(ast);
if (finishWithSpaceOrNewline)
newLine();
return false;
}
}
bool followedByNewLine(const SourceLocation &loc)
{
const QString &source = _doc->source();
int i = loc.end();
for (; i < source.size() && source.at(i).isSpace(); ++i) {
if (source.at(i) == QLatin1Char('\n'))
return true;
}
return false;
}
bool precededByEmptyLine(const SourceLocation &loc)
{
const QString &source = _doc->source();
int i = loc.offset;
// expect spaces and \n, twice
bool first = true;
for (--i;
i >= 0 && source.at(i).isSpace();
--i) {
if (source.at(i) == QLatin1Char('\n')) {
if (first)
first = false;
else
return true;
}
}
return false;
}
bool firstOnLine()
{
foreach (const QChar &c, _line) {
if (!c.isSpace())
return false;
}
return true;
}
void addPossibleSplit(qreal badness, int offset = 0)
{
Split s;
s.badness = badness;
s.offset = _line.size() + offset;
_possibleSplits += s;
}
void fixCommentLocation(SourceLocation &loc)
{
loc.offset -= 2;
loc.startColumn -= 2;
loc.length += 2;
if (_doc->source().mid(loc.offset, 2) == QLatin1String("/*"))
loc.length += 2;
}
virtual bool preVisit(Node *ast)
{
SourceLocation firstLoc;
if (ExpressionNode *expr = ast->expressionCast())
firstLoc = expr->firstSourceLocation();
else if (Statement *stmt = ast->statementCast())
firstLoc = stmt->firstSourceLocation();
else if (UiObjectMember *mem = ast->uiObjectMemberCast())
firstLoc = mem->firstSourceLocation();
else if (UiImport *import = cast<UiImport *>(ast))
firstLoc = import->firstSourceLocation();
if (firstLoc.isValid() && int(firstLoc.offset) != _lastNewlineOffset) {
_lastNewlineOffset = firstLoc.offset;
if (precededByEmptyLine(firstLoc) && !_result.endsWith(QLatin1String("\n\n")))
newLine();
}
return true;
}
virtual void postVisit(Node *ast)
{
SourceLocation lastLoc;
if (ExpressionNode *expr = ast->expressionCast())
lastLoc = expr->lastSourceLocation();
else if (Statement *stmt = ast->statementCast())
lastLoc = stmt->lastSourceLocation();
else if (UiObjectMember *mem = ast->uiObjectMemberCast())
lastLoc = mem->lastSourceLocation();
else if (UiImport *import = cast<UiImport *>(ast))
lastLoc = import->lastSourceLocation();
if (lastLoc.isValid()) {
const QList<SourceLocation> &comments = _doc->engine()->comments();
// preserve trailing comments
for (; _nextComment < comments.size(); ++_nextComment) {
SourceLocation nextCommentLoc = comments.at(_nextComment);
if (nextCommentLoc.startLine != lastLoc.startLine)
break;
fixCommentLocation(nextCommentLoc);
// there must only be whitespace between lastLoc and the comment
bool commentFollows = true;
for (quint32 i = lastLoc.end(); i < nextCommentLoc.begin(); ++i) {
if (!_doc->source().at(i).isSpace()) {
commentFollows = false;
break;
}
}
if (!commentFollows)
break;
out(" ");
out(toString(nextCommentLoc));
}
}
}
virtual bool visit(UiImport *ast)
{
out("import ", ast->importToken);
if (!ast->fileName.isNull())
out(QString::fromLatin1("\"%1\"").arg(ast->fileName.toString()));
else
accept(ast->importUri);
if (ast->versionToken.isValid()) {
out(" ");
out(ast->versionToken);
}
if (!ast->importId.isNull()) {
out(" as ", ast->asToken);
out(ast->importIdToken);
}
return false;
}
virtual bool visit(UiObjectDefinition *ast)
{
accept(ast->qualifiedTypeNameId);
out(" ");
accept(ast->initializer);
return false;
}
virtual bool visit(UiObjectInitializer *ast)
{
out(ast->lbraceToken);
if (ast->members)
lnAcceptIndented(ast->members);
newLine();
out(ast->rbraceToken);
return false;
}
virtual bool visit(UiParameterList *list)
{
for (UiParameterList *it = list; it; it = it->next) {
out(it->propertyTypeToken);
out(" ");
out(it->identifierToken);
if (it->next)
out(", ", it->commaToken);
}
return false;
}
virtual bool visit(UiPublicMember *ast)
{
if (ast->type == UiPublicMember::Property) {
if (ast->isDefaultMember)
out("default ", ast->defaultToken);
out("property ", ast->propertyToken);
if (!ast->typeModifier.isNull()) {
out(ast->typeModifierToken);
out("<");
out(ast->typeToken);
out(">");
} else {
out(ast->typeToken);
}
out(" ");
out(ast->identifierToken);
if (ast->statement) {
out(": ", ast->colonToken);
accept(ast->statement);
} else if (ast->binding) {
out(": ", ast->colonToken);
accept(ast->binding);
}
} else { // signal
out("signal ");
out(ast->identifierToken);
if (ast->parameters) {
out("(");
accept(ast->parameters);
out(")");
}
}
return false;
}
virtual bool visit(UiObjectBinding *ast)
{
if (ast->hasOnToken) {
accept(ast->qualifiedTypeNameId);
out(" on ");
accept(ast->qualifiedId);
} else {
accept(ast->qualifiedId);
out(": ", ast->colonToken);
accept(ast->qualifiedTypeNameId);
}
out(" ");
accept(ast->initializer);
return false;
}
virtual bool visit(UiScriptBinding *ast)
{
accept(ast->qualifiedId);
out(": ", ast->colonToken);
accept(ast->statement);
return false;
}
virtual bool visit(UiArrayBinding *ast)
{
accept(ast->qualifiedId);
out(ast->colonToken);
out(" ");
out(ast->lbracketToken);
lnAcceptIndented(ast->members);
newLine();
out(ast->rbracketToken);
return false;
}
virtual bool visit(ThisExpression *ast) { out(ast->thisToken); return true; }
virtual bool visit(NullExpression *ast) { out(ast->nullToken); return true; }
virtual bool visit(TrueLiteral *ast) { out(ast->trueToken); return true; }
virtual bool visit(FalseLiteral *ast) { out(ast->falseToken); return true; }
virtual bool visit(IdentifierExpression *ast) { out(ast->identifierToken); return true; }
virtual bool visit(StringLiteral *ast) { out(ast->literalToken); return true; }
virtual bool visit(NumericLiteral *ast) { out(ast->literalToken); return true; }
virtual bool visit(RegExpLiteral *ast) { out(ast->literalToken); return true; }
virtual bool visit(ArrayLiteral *ast)
{
out(ast->lbracketToken);
if (ast->elements)
accept(ast->elements);
if (ast->elements && ast->elision)
out(", ", ast->commaToken);
if (ast->elision)
accept(ast->elision);
out(ast->rbracketToken);
return false;
}
virtual bool visit(ObjectLiteral *ast)
{
out(ast->lbraceToken);
lnAcceptIndented(ast->properties);
newLine();
out(ast->rbraceToken);
return false;
}
virtual bool visit(ElementList *ast)
{
for (ElementList *it = ast; it; it = it->next) {
if (it->elision)
accept(it->elision);
if (it->elision && it->expression)
out(", ");
if (it->expression)
accept(it->expression);
if (it->next)
out(", ", ast->commaToken);
}
return false;
}
virtual bool visit(PropertyAssignmentList *ast)
{
for (PropertyAssignmentList *it = ast; it; it = it->next) {
PropertyNameAndValue *assignment = AST::cast<PropertyNameAndValue *>(it->assignment);
if (assignment) {
accept(assignment->name);
out(": ", assignment->colonToken);
accept(assignment->value);
if (it->next) {
out(",", ast->commaToken); // always invalid?
newLine();
}
continue;
}
PropertyGetterSetter *getterSetter = AST::cast<PropertyGetterSetter *>(it->assignment);
if (getterSetter) {
switch (getterSetter->type) {
case PropertyGetterSetter::Getter:
out("get");
break;
case PropertyGetterSetter::Setter:
out("set");
break;
}
accept(getterSetter->name);
out("(", getterSetter->lparenToken);
accept(getterSetter->formals);
out("(", getterSetter->rparenToken);
out(" {", getterSetter->lbraceToken);
accept(getterSetter->functionBody);
out(" }", getterSetter->rbraceToken);
}
}
return false;
}
virtual bool visit(NestedExpression *ast)
{
out(ast->lparenToken);
accept(ast->expression);
out(ast->rparenToken);
return false;
}
virtual bool visit(IdentifierPropertyName *ast) { out(ast->id.toString()); return true; }
virtual bool visit(StringLiteralPropertyName *ast) { out(ast->id.toString()); return true; }
virtual bool visit(NumericLiteralPropertyName *ast) { out(QString::number(ast->id)); return true; }
virtual bool visit(ArrayMemberExpression *ast)
{
accept(ast->base);
out(ast->lbracketToken);
accept(ast->expression);
out(ast->rbracketToken);
return false;
}
virtual bool visit(FieldMemberExpression *ast)
{
accept(ast->base);
out(ast->dotToken);
out(ast->identifierToken);
return false;
}
virtual bool visit(NewMemberExpression *ast)
{
out("new ", ast->newToken);
accept(ast->base);
out(ast->lparenToken);
accept(ast->arguments);
out(ast->rparenToken);
return false;
}
virtual bool visit(NewExpression *ast)
{
out("new ", ast->newToken);
accept(ast->expression);
return false;
}
virtual bool visit(CallExpression *ast)
{
accept(ast->base);
out(ast->lparenToken);
addPossibleSplit(0);
accept(ast->arguments);
out(ast->rparenToken);
return false;
}
virtual bool visit(PostIncrementExpression *ast)
{
accept(ast->base);
out(ast->incrementToken);
return false;
}
virtual bool visit(PostDecrementExpression *ast)
{
accept(ast->base);
out(ast->decrementToken);
return false;
}
virtual bool visit(PreIncrementExpression *ast)
{
out(ast->incrementToken);
accept(ast->expression);
return false;
}
virtual bool visit(PreDecrementExpression *ast)
{
out(ast->decrementToken);
accept(ast->expression);
return false;
}
virtual bool visit(DeleteExpression *ast)
{
out("delete ", ast->deleteToken);
accept(ast->expression);
return false;
}
virtual bool visit(VoidExpression *ast)
{
out("void ", ast->voidToken);
accept(ast->expression);
return false;
}
virtual bool visit(TypeOfExpression *ast)
{
out("typeof ", ast->typeofToken);
accept(ast->expression);
return false;
}
virtual bool visit(UnaryPlusExpression *ast)
{
out(ast->plusToken);
accept(ast->expression);
return false;
}
virtual bool visit(UnaryMinusExpression *ast)
{
out(ast->minusToken);
accept(ast->expression);
return false;
}
virtual bool visit(TildeExpression *ast)
{
out(ast->tildeToken);
accept(ast->expression);
return false;
}
virtual bool visit(NotExpression *ast)
{
out(ast->notToken);
accept(ast->expression);
return false;
}
virtual bool visit(BinaryExpression *ast)
{
++_binaryExpDepth;
accept(ast->left);
// in general, avoid splitting at the operator
// but && and || are ok
qreal splitBadness = 30;
if (ast->op == QSOperator::And
|| ast->op == QSOperator::Or)
splitBadness = 0;
addPossibleSplit(splitBadness);
out(" ");
out(ast->operatorToken);
out(" ");
accept(ast->right);
--_binaryExpDepth;
return false;
}
virtual bool visit(ConditionalExpression *ast)
{
accept(ast->expression);
out(" ? ", ast->questionToken);
accept(ast->ok);
out(" : ", ast->colonToken);
accept(ast->ko);
return false;
}
virtual bool visit(Block *ast)
{
out(ast->lbraceToken);
lnAcceptIndented(ast->statements);
newLine();
out(ast->rbraceToken);
return false;
}
virtual bool visit(VariableStatement *ast)
{
out("var ", ast->declarationKindToken);
accept(ast->declarations);
return false;
}
virtual bool visit(VariableDeclaration *ast)
{
out(ast->identifierToken);
if (ast->expression) {
out(" = ");
accept(ast->expression);
}
return false;
}
virtual bool visit(EmptyStatement *ast)
{
out(ast->semicolonToken);
return false;
}
virtual bool visit(IfStatement *ast)
{
out(ast->ifToken);
out(" ");
out(ast->lparenToken);
accept(ast->expression);
out(ast->rparenToken);
acceptBlockOrIndented(ast->ok, ast->ko);
if (ast->ko) {
out(ast->elseToken);
if (cast<Block *>(ast->ko) || cast<IfStatement *>(ast->ko)) {
out(" ");
accept(ast->ko);
} else {
lnAcceptIndented(ast->ko);
}
}
return false;
}
virtual bool visit(DoWhileStatement *ast)
{
out(ast->doToken);
acceptBlockOrIndented(ast->statement, true);
out(ast->whileToken);
out(" ");
out(ast->lparenToken);
accept(ast->expression);
out(ast->rparenToken);
return false;
}
virtual bool visit(WhileStatement *ast)
{
out(ast->whileToken);
out(" ");
out(ast->lparenToken);
accept(ast->expression);
out(ast->rparenToken);
acceptBlockOrIndented(ast->statement);
return false;
}
virtual bool visit(ForStatement *ast)
{
out(ast->forToken);
out(" ");
out(ast->lparenToken);
accept(ast->initialiser);
out("; ", ast->firstSemicolonToken);
accept(ast->condition);
out("; ", ast->secondSemicolonToken);
accept(ast->expression);
out(ast->rparenToken);
acceptBlockOrIndented(ast->statement);
return false;
}
virtual bool visit(LocalForStatement *ast)
{
out(ast->forToken);
out(" ");
out(ast->lparenToken);
out(ast->varToken);
out(" ");
accept(ast->declarations);
out("; ", ast->firstSemicolonToken);
accept(ast->condition);
out("; ", ast->secondSemicolonToken);
accept(ast->expression);
out(")", ast->rparenToken);
acceptBlockOrIndented(ast->statement);
return false;
}
virtual bool visit(ForEachStatement *ast)
{
out(ast->forToken);
out(" ");
out(ast->lparenToken);
accept(ast->initialiser);
out(" in ", ast->inToken);
accept(ast->expression);
out(ast->rparenToken);
acceptBlockOrIndented(ast->statement);
return false;
}
virtual bool visit(LocalForEachStatement *ast)
{
out(ast->forToken);
out(" ");
out(ast->lparenToken);
out(ast->varToken);
out(" ");
accept(ast->declaration);
out(" in ", ast->inToken);
accept(ast->expression);
out(ast->rparenToken);
acceptBlockOrIndented(ast->statement);
return false;
}
virtual bool visit(ContinueStatement *ast)
{
out(ast->continueToken);
if (!ast->label.isNull()) {
out(" ");
out(ast->identifierToken);
}
return false;
}
virtual bool visit(BreakStatement *ast)
{
out(ast->breakToken);
if (!ast->label.isNull()) {
out(" ");
out(ast->identifierToken);
}
return false;
}
virtual bool visit(ReturnStatement *ast)
{
out(ast->returnToken);
if (ast->expression) {
out(" ");
accept(ast->expression);
}
return false;
}
virtual bool visit(ThrowStatement *ast)
{
out(ast->throwToken);
if (ast->expression) {
out(" ");
accept(ast->expression);
}
return false;
}
virtual bool visit(WithStatement *ast)
{
out(ast->withToken);
out(" ");
out(ast->lparenToken);
accept(ast->expression);
out(ast->rparenToken);
acceptBlockOrIndented(ast->statement);
return false;
}
virtual bool visit(SwitchStatement *ast)
{
out(ast->switchToken);
out(" ");
out(ast->lparenToken);
accept(ast->expression);
out(ast->rparenToken);
out(" ");
accept(ast->block);
return false;
}
virtual bool visit(CaseBlock *ast)
{
out(ast->lbraceToken);
newLine();
accept(ast->clauses);
if (ast->clauses && ast->defaultClause)
newLine();
accept(ast->defaultClause);
if (ast->moreClauses)
newLine();
accept(ast->moreClauses);
newLine();
out(ast->rbraceToken);
return false;
}
virtual bool visit(CaseClause *ast)
{
out("case ", ast->caseToken);
accept(ast->expression);
out(ast->colonToken);
lnAcceptIndented(ast->statements);
return false;
}
virtual bool visit(DefaultClause *ast)
{
out(ast->defaultToken);
out(ast->colonToken);
lnAcceptIndented(ast->statements);
return false;
}
virtual bool visit(LabelledStatement *ast)
{
out(ast->identifierToken);
out(": ", ast->colonToken);
accept(ast->statement);
return false;
}
virtual bool visit(TryStatement *ast)
{
out("try ", ast->tryToken);
accept(ast->statement);
if (ast->catchExpression) {
out(" ");
accept(ast->catchExpression);
}
if (ast->finallyExpression) {
out(" ");
accept(ast->finallyExpression);
}
return false;
}
virtual bool visit(Catch *ast)
{
out(ast->catchToken);
out(" ");
out(ast->lparenToken);
out(ast->identifierToken);
out(") ", ast->rparenToken);
accept(ast->statement);
return false;
}
virtual bool visit(Finally *ast)
{
out("finally ", ast->finallyToken);
accept(ast->statement);
return false;
}
virtual bool visit(FunctionDeclaration *ast)
{
return visit(static_cast<FunctionExpression *>(ast));
}
virtual bool visit(FunctionExpression *ast)
{
out("function ", ast->functionToken);
if (!ast->name.isNull())
out(ast->identifierToken);
out(ast->lparenToken);
accept(ast->formals);
out(ast->rparenToken);
out(" ");
out(ast->lbraceToken);
if (ast->body) {
lnAcceptIndented(ast->body);
newLine();
}
out(ast->rbraceToken);
return false;
}
virtual bool visit(UiHeaderItemList *ast)
{
for (UiHeaderItemList *it = ast; it; it = it->next) {
accept(it->headerItem);
newLine();
}
requireEmptyLine();
return false;
}
virtual bool visit(UiObjectMemberList *ast)
{
for (UiObjectMemberList *it = ast; it; it = it->next) {
accept(it->member);
if (it->next)
newLine();
}
return false;
}
virtual bool visit(UiArrayMemberList *ast)
{
for (UiArrayMemberList *it = ast; it; it = it->next) {
accept(it->member);
if (it->next) {
out(",", ast->commaToken); // ### comma token seems to be generally invalid
newLine();
}
}
return false;
}
virtual bool visit(UiQualifiedId *ast)
{
for (UiQualifiedId *it = ast; it; it = it->next) {
out(it->identifierToken);
if (it->next)
out(".");
}
return false;
}
virtual bool visit(Elision *ast)
{
for (Elision *it = ast; it; it = it->next) {
if (it->next)
out(", ", ast->commaToken);
}
return false;
}
virtual bool visit(ArgumentList *ast)
{
for (ArgumentList *it = ast; it; it = it->next) {
accept(it->expression);
if (it->next) {
out(", ", it->commaToken);
addPossibleSplit(-1);
}
}
return false;
}
virtual bool visit(StatementList *ast)
{
for (StatementList *it = ast; it; it = it->next) {
// ### work around parser bug: skip empty statements with wrong tokens
if (EmptyStatement *emptyStatement = cast<EmptyStatement *>(it->statement)) {
if (toString(emptyStatement->semicolonToken) != QLatin1String(";"))
continue;
}
accept(it->statement);
if (it->next)
newLine();
}
return false;
}
virtual bool visit(SourceElements *ast)
{
for (SourceElements *it = ast; it; it = it->next) {
accept(it->element);
if (it->next)
newLine();
}
return false;
}
virtual bool visit(VariableDeclarationList *ast)
{
for (VariableDeclarationList *it = ast; it; it = it->next) {
accept(it->declaration);
if (it->next)
out(", ", it->commaToken);
}
return false;
}
virtual bool visit(CaseClauses *ast)
{
for (CaseClauses *it = ast; it; it = it->next) {
accept(it->clause);
if (it->next)
newLine();
}
return false;
}
virtual bool visit(FormalParameterList *ast)
{
for (FormalParameterList *it = ast; it; it = it->next) {
if (it->commaToken.isValid())
out(", ", it->commaToken);
out(it->identifierToken);
}
return false;
}
};
} // anonymous namespace
QString QmlJS::reformat(const Document::Ptr &doc)
{
Rewriter rewriter(doc);
return rewriter(doc->ast());
}