Commit bfac509b authored by Erik Verbruggen's avatar Erik Verbruggen
Browse files

Added import rewriting.

parent 7d79f4f8
......@@ -27,8 +27,8 @@
**
**************************************************************************/
#include <qmljsast_p.h>
#include <qmljsengine_p.h>
#include <qmljs/parser/qmljsast_p.h>
#include <qmljs/parser/qmljsengine_p.h>
#include "changeimportsvisitor.h"
......@@ -37,44 +37,67 @@ using namespace QmlJS::AST;
using namespace QmlDesigner;
using namespace QmlDesigner::Internal;
using namespace QmlDesigner::Internal;
ChangeImportsVisitor::ChangeImportsVisitor(TextModifier &textModifier, const QSet<Import> &addedImports, const QSet<Import> &removedImports, const QString &source):
QMLRewriter(textModifier),
CopyPasteUtil(source),
m_addedImports(addedImports),
m_removedImports(removedImports)
{
}
ChangeImportsVisitor::ChangeImportsVisitor(TextModifier &textModifier,
const QString &source):
QMLRewriter(textModifier), m_source(source)
{}
bool ChangeImportsVisitor::visit(QmlJS::AST::UiProgram *ast)
bool ChangeImportsVisitor::add(QmlJS::AST::UiProgram *ast, const Import &import)
{
if (ast->imports)
accept(ast->imports);
setDidRewriting(false);
if (!ast)
return false;
if (ast->imports && ast->imports->import) {
int insertionPoint = 0;
if (ast->members && ast->members->member) {
insertionPoint = ast->members->member->firstSourceLocation().begin();
} else {
insertionPoint = m_source.length();
}
while (insertionPoint > 0) {
--insertionPoint;
const QChar c = m_source.at(insertionPoint);
if (!c.isSpace() && c != QLatin1Char(';'))
break;
}
replace(insertionPoint, 0, QLatin1String("\n") + import.toString(false));
} else {
replace(0, 0, import.toString(false) + QLatin1String("\n\n"));
}
setDidRewriting(true);
return false;
return true;
}
bool ChangeImportsVisitor::visit(QmlJS::AST::UiImportList *ast)
bool ChangeImportsVisitor::remove(QmlJS::AST::UiProgram *ast, const Import &import)
{
setDidRewriting(false);
if (!ast)
return false;
quint32 prevEnd = 0;
for (UiImportList *it = ast; it; it = it->next) {
UiImport *imp = it->import;
if (!imp)
continue;
if (m_removedImports.remove(createImport(imp)))
replace(prevEnd, imp->lastSourceLocation().end() - prevEnd, "");
prevEnd = imp->lastSourceLocation().end();
for (UiImportList *iter = ast->imports; iter; iter = iter->next) {
if (equals(iter->import, import)) {
int start = iter->firstSourceLocation().begin();
int end = iter->lastSourceLocation().end();
includeSurroundingWhitespace(start, end);
replace(start, end - start, QString());
setDidRewriting(true);
}
}
foreach (const Import &i, m_addedImports) {
replace(prevEnd, 0, i.toString(false) + "\n");
}
return didRewriting();
}
return false;
bool ChangeImportsVisitor::equals(QmlJS::AST::UiImport *ast, const Import &import)
{
if (import.isLibraryImport()) {
return flatten(ast->importUri) == import.url();
} else if (import.isFileImport()) {
return ast->fileName->asString() == import.file();
} else {
return false;
}
}
......@@ -32,27 +32,24 @@
#include <QtCore/QSet>
#include <model/copypasteutil.h>
#include "import.h"
#include "qmlrewriter.h"
namespace QmlDesigner {
namespace Internal {
class ChangeImportsVisitor: public QMLRewriter, protected QmlDesigner::Internal::CopyPasteUtil
class ChangeImportsVisitor: public QMLRewriter
{
public:
ChangeImportsVisitor(QmlDesigner::TextModifier &textModifier,
const QSet<QmlDesigner::Import> &addedImports,
const QSet<QmlDesigner::Import> &removedImports, const QString &source);
ChangeImportsVisitor(QmlDesigner::TextModifier &textModifier, const QString &source);
protected:
virtual bool visit(QmlJS::AST::UiProgram *ast);
virtual bool visit(QmlJS::AST::UiImportList *ast);
bool add(QmlJS::AST::UiProgram *ast, const Import &import);
bool remove(QmlJS::AST::UiProgram *ast, const Import &import);
private:
QSet<QmlDesigner::Import> m_addedImports;
QSet<QmlDesigner::Import> m_removedImports;
static bool equals(QmlJS::AST::UiImport *ast, const Import &import);
QString m_source;
};
} // namespace Internal
......
......@@ -12,7 +12,8 @@ SOURCES += \
$$PWD/astobjecttextextractor.cpp \
$$PWD/objectlengthcalculator.cpp \
$$PWD/firstdefinitionfinder.cpp \
$$PWD/moveobjectbeforeobjectvisitor.cpp
$$PWD/moveobjectbeforeobjectvisitor.cpp \
$$PWD/changeimportsvisitor.cpp
HEADERS += \
$$PWD/qmlrewriter.h \
$$PWD/qmlrefactoring.h \
......@@ -27,4 +28,5 @@ HEADERS += \
$$PWD/astobjecttextextractor.h \
$$PWD/objectlengthcalculator.h \
$$PWD/firstdefinitionfinder.h \
$$PWD/moveobjectbeforeobjectvisitor.h
$$PWD/moveobjectbeforeobjectvisitor.h \
$$PWD/changeimportsvisitor.h
......@@ -32,6 +32,7 @@
#include "addarraymembervisitor.h"
#include "addobjectvisitor.h"
#include "addpropertyvisitor.h"
#include "changeimportsvisitor.h"
#include "changeobjecttypevisitor.h"
#include "changepropertyvisitor.h"
#include "moveobjectvisitor.h"
......@@ -70,11 +71,16 @@ bool QmlRefactoring::reparseDocument()
}
}
bool QmlRefactoring::changeImports(const QSet<QmlDesigner::Import> &/*addedImports*/, const QSet<QmlDesigner::Import> &/*removedImports*/)
bool QmlRefactoring::addImport(const Import &import)
{
// ChangeImportsVisitor visit(*textModifier, addedImports, removedImports, qmlDocument->source());
// visit(qmlDocument->program());
return false;
ChangeImportsVisitor visitor(*textModifier, qmlDocument->source());
return visitor.add(qmlDocument->qmlProgram(), import);
}
bool QmlRefactoring::removeImport(const Import &import)
{
ChangeImportsVisitor visitor(*textModifier, qmlDocument->source());
return visitor.remove(qmlDocument->qmlProgram(), import);
}
bool QmlRefactoring::addToArrayMemberList(int parentLocation, const QString &propertyName, const QString &content)
......
......@@ -55,7 +55,8 @@ public:
bool reparseDocument();
bool changeImports(const QSet<QmlDesigner::Import> &addedImports, const QSet<QmlDesigner::Import> &removedImports);
bool addImport(const Import &import);
bool removeImport(const Import &import);
bool addToArrayMemberList(int parentLocation, const QString &propertyName, const QString &content);
bool addToObjectMemberList(int parentLocation, const QString &content);
......
......@@ -31,7 +31,6 @@
#define IMPORT_H
#include <QtCore/QString>
#include <QtCore/QUrl>
#include "corelib_global.h"
......@@ -40,7 +39,7 @@ namespace QmlDesigner {
class CORESHARED_EXPORT Import
{
public:
static Import createLibraryImport(const QUrl &url, const QString &version = QString(), const QString &alias = QString());
static Import createLibraryImport(const QString &url, const QString &version = QString(), const QString &alias = QString());
static Import createFileImport(const QString &file, const QString &version = QString(), const QString &alias = QString());
static Import empty();
......@@ -50,7 +49,7 @@ public:
bool hasVersion() const { return !m_version.isEmpty(); }
bool hasAlias() const { return !m_alias.isEmpty(); }
QUrl url() const { return m_url; }
QString url() const { return m_url; }
QString file() const { return m_file; }
QString version() const { return m_version; }
QString alias() const { return m_alias; }
......@@ -60,10 +59,10 @@ public:
bool operator==(const Import &other) const;
private:
Import(const QUrl &url, const QString &file, const QString &version, const QString &alias);
Import(const QString &url, const QString &file, const QString &version, const QString &alias);
private:
QUrl m_url;
QString m_url;
QString m_file;
QString m_version;
QString m_alias;
......
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 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 <qmljsast_p.h>
#include <qmljsengine_p.h>
#include "copypasteutil.h"
using namespace QmlJS;
using namespace QmlJS::AST;
namespace QmlDesigner {
namespace Internal {
Import CopyPasteUtil::createImport(QmlJS::AST::UiImport *ast)
{
QString version;
if (ast->versionToken.isValid())
version = textAt(ast->versionToken);
QString alias;
if (ast->importId)
alias = ast->importId->asString();
if (ast->fileName)
return Import::createFileImport(ast->fileName->asString(), version, alias);
if (ast->importUri && ast->importUri->name)
return Import::createLibraryImport(ast->importUri->name->asString(), version, alias);
return Import::empty();
}
} // namespace Internal
} // namespace QmlDesigner
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 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 COPYPASTEUTIL_H
#define COPYPASTEUTIL_H
#include <qmljsastfwd_p.h>
#include "import.h"
namespace QmlDesigner {
namespace Internal {
class CopyPasteUtil {
public:
CopyPasteUtil(const QString &originalSource): m_originalSource(originalSource)
{}
Import createImport(QmlJS::AST::UiImport *ast);
protected:
QString textAt(const QmlJS::AST::SourceLocation &loc) const
{ return m_originalSource.mid(loc.offset, loc.length); }
QString textAt(const QmlJS::AST::SourceLocation &firstSourceLocation, const QmlJS::AST::SourceLocation &lastSourceLocation) const
{ return m_originalSource.mid(firstSourceLocation.offset, lastSourceLocation.end() - firstSourceLocation.offset); }
QString originalSource() const { return m_originalSource; }
private:
QString m_originalSource;
};
} // namespace Internal
} // namespace QmlDesigner
#endif // COPYPASTEUTIL_H
......@@ -33,22 +33,22 @@
namespace QmlDesigner {
Import Import::createLibraryImport(const QUrl &url, const QString &version, const QString &alias)
Import Import::createLibraryImport(const QString &url, const QString &version, const QString &alias)
{
return Import(url, QString(), version, alias);
}
Import Import::createFileImport(const QString &file, const QString &version, const QString &alias)
{
return Import(QUrl(), file, version, alias);
return Import(QString(), file, version, alias);
}
Import Import::empty()
{
return Import(QUrl(), QString(), QString(), QString());
return Import(QString(), QString(), QString(), QString());
}
Import::Import(const QUrl &url, const QString &file, const QString &version, const QString &alias):
Import::Import(const QString &url, const QString &file, const QString &version, const QString &alias):
m_url(url),
m_file(file),
m_version(version),
......@@ -63,7 +63,7 @@ QString Import::toString(bool addSemicolon) const
if (isFileImport())
result += '"' + file() + '"';
else if (isLibraryImport())
result += url().toString();
result += url();
else
return QString();
......@@ -86,7 +86,7 @@ bool Import::operator==(const Import &other) const
uint qHash(const Import &import)
{
return ::qHash(import.url().toString()) ^ ::qHash(import.file()) ^ ::qHash(import.version()) ^ ::qHash(import.alias());
return ::qHash(import.url()) ^ ::qHash(import.file()) ^ ::qHash(import.version()) ^ ::qHash(import.alias());
}
} // namespace QmlDesigner
......@@ -133,6 +133,18 @@ void ModelToTextMerger::nodeTypeChanged(const ModelNode &node,const QString &/*t
schedule(new ChangeTypeRewriteAction(node));
}
void ModelToTextMerger::addImport(const Import &import)
{
if (!import.isEmpty())
schedule(new AddImportRewriteAction(import));
}
void ModelToTextMerger::removeImport(const Import &import)
{
if (!import.isEmpty())
schedule(new RemoveImportRewriteAction(import));
}
void ModelToTextMerger::nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent, const NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags propertyChange)
{
if (isInHierarchy(oldPropertyParent) && isInHierarchy(newPropertyParent)) { // the node is moved
......
......@@ -74,6 +74,9 @@ public:
void nodeSlidAround(const ModelNode &movingNode, const ModelNode &inFrontOfNode);
void nodeTypeChanged(const ModelNode &node,const QString &type, int majorVersion, int minorVersion);
void addImport(const Import &import);
void removeImport(const Import &import);
protected:
RewriterView *view();
......
......@@ -36,6 +36,37 @@ using namespace QmlDesigner;
using namespace QmlDesigner::Internal;
using namespace QmlDesigner;
namespace { // anonymous
static inline QString toInfo(const Import &import)
{
QString txt;
if (import.isEmpty()) {
return QLatin1String("empty import");
} else if (import.isFileImport()) {
txt = QLatin1String("import file \"%1\"");
txt = txt.arg(import.url());
} else if (import.isLibraryImport()) {
txt = QLatin1String("import library \"%1\"");
txt = txt.arg(import.file());
} else {
return QLatin1String("unknown type of import");
}
if (import.hasVersion())
txt += QString::fromLatin1("with version \"%1\"").arg(import.version());
else
txt += QLatin1String("without version");
if (import.hasAlias())
txt += QString::fromLatin1("aliassed as \"%1\"").arg(import.alias());
else
txt += QLatin1String("unaliassed");
return txt;
}
static inline QString toString(QmlRefactoring::PropertyType type)
{
switch (type) {
......@@ -46,6 +77,8 @@ static inline QString toString(QmlRefactoring::PropertyType type)
}
}
} // namespace anonymous
bool AddPropertyRewriteAction::execute(QmlRefactoring &refactoring, ModelNodePositionStorage &positionStore)
{
const int nodeLocation = positionStore.nodeOffset(m_property.parentModelNode());
......@@ -302,3 +335,39 @@ QString MoveNodeRewriteAction::info() const
return QString("MoveNodeRewriteAction for an invalid node");
}
}
bool AddImportRewriteAction::execute(QmlDesigner::QmlRefactoring &refactoring,
ModelNodePositionStorage &/*positionStore*/)
{
const bool result = refactoring.addImport(m_import);
if (!result)
qDebug() << "*** AddImportRewriteAction::execute failed in changeImports ("
<< m_import.toString()
<< ") **"
<< info();
return result;
}
QString AddImportRewriteAction::info() const
{
return toInfo(m_import);
}
bool RemoveImportRewriteAction::execute(QmlDesigner::QmlRefactoring &refactoring,
ModelNodePositionStorage &/*positionStore*/)
{
const bool result = refactoring.addImport(m_import);
if (!result)
qDebug() << "*** RemoveImportRewriteAction::execute failed in changeImports ("
<< m_import.toString()
<< ") **"
<< info();
return result;
}
QString RemoveImportRewriteAction::info() const
{
return toInfo(m_import);
}
......@@ -38,10 +38,12 @@
namespace QmlDesigner {
namespace Internal {
class AddImportRewriteAction;
class AddPropertyRewriteAction;
class ChangeIdRewriteAction;
class ChangePropertyRewriteAction;
class ChangeTypeRewriteAction;
class RemoveImportRewriteAction;
class RemoveNodeRewriteAction;
class RemovePropertyRewriteAction;
class ReparentNodeRewriteAction;
......@@ -53,10 +55,12 @@ public:
virtual bool execute(QmlDesigner::QmlRefactoring &refactoring, ModelNodePositionStorage &positionStore) = 0;
virtual QString info() const = 0;
virtual AddImportRewriteAction const *asAddImportRewriteAction() const { return 0; }
virtual AddPropertyRewriteAction const *asAddPropertyRewriteAction() const { return 0; }
virtual ChangeIdRewriteAction const *asChangeIdRewriteAction() const { return 0; }
virtual ChangePropertyRewriteAction const *asChangePropertyRewriteAction() const { return 0; }
virtual ChangeTypeRewriteAction const *asChangeTypeRewriteAction() const { return 0; }
virtual RemoveImportRewriteAction const * asRemoveImportRewriteAction() const { return 0; }
virtual RemoveNodeRewriteAction const *asRemoveNodeRewriteAction() const { return 0; }
virtual RemovePropertyRewriteAction const *asRemovePropertyRewriteAction() const { return 0; }
virtual ReparentNodeRewriteAction const *asReparentNodeRewriteAction() const { return 0; }
......@@ -255,6 +259,42 @@ private:
ModelNode m_newTrailingNode;
};
class AddImportRewriteAction: public RewriteAction
{
public:
AddImportRewriteAction(const Import &import):
m_import(import)
{}
virtual bool execute(QmlDesigner::QmlRefactoring &refactoring, ModelNodePositionStorage &positionStore);
virtual QString info() const;
virtual AddImportRewriteAction const *asAddImportRewriteAction() const { return this; }
Import import() const { return m_import; }
private:
Import m_import;
};
class RemoveImportRewriteAction: public RewriteAction
{
public:
RemoveImportRewriteAction(const Import &import):
m_import(import)
{}
virtual bool execute(QmlDesigner::QmlRefactoring &refactoring, ModelNodePositionStorage &positionStore);
virtual QString info() const;
virtual RemoveImportRewriteAction const *asRemoveImportRewriteAction() const { return this; }
Import import() const { return m_import; }
private:
Import m_import;
};