summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorErik Verbruggen <erik.verbruggen@nokia.com>2010-06-30 13:42:35 +0200
committerErik Verbruggen <erik.verbruggen@nokia.com>2010-06-30 13:42:35 +0200
commit21b211fed0ae36de57dbfb9ca60239dd259b9a60 (patch)
tree50f131ff1f670aad0d2b75c26aa796f0bbfbe24a
parent3267ba12abe9cb9787c472ba52b499b862776b54 (diff)
downloadqt-creator-21b211fed0ae36de57dbfb9ca60239dd259b9a60.tar.gz
Created a utility rewriting class from existing visitors.
-rw-r--r--src/libs/qmljs/qmljs-lib.pri7
-rw-r--r--src/libs/qmljs/qmljsrewriter.cpp627
-rw-r--r--src/libs/qmljs/qmljsrewriter.h110
-rw-r--r--src/plugins/qmldesigner/designercore/filemanager/addarraymembervisitor.cpp1
-rw-r--r--src/plugins/qmldesigner/designercore/filemanager/addobjectvisitor.cpp1
-rw-r--r--src/plugins/qmldesigner/designercore/filemanager/addpropertyvisitor.cpp3
-rw-r--r--src/plugins/qmldesigner/designercore/filemanager/changepropertyvisitor.cpp7
-rw-r--r--src/plugins/qmldesigner/designercore/filemanager/qmlrewriter.cpp5
-rw-r--r--src/plugins/qmldesigner/designercore/filemanager/removepropertyvisitor.cpp6
-rw-r--r--src/plugins/qmldesigner/designercore/filemanager/removeuiobjectmembervisitor.cpp2
10 files changed, 767 insertions, 2 deletions
diff --git a/src/libs/qmljs/qmljs-lib.pri b/src/libs/qmljs/qmljs-lib.pri
index c74ad3478b..648566fc5b 100644
--- a/src/libs/qmljs/qmljs-lib.pri
+++ b/src/libs/qmljs/qmljs-lib.pri
@@ -5,6 +5,7 @@ contains(CONFIG, dll) {
}
include(parser/parser.pri)
+include(../utils/utils.pri)
DEPENDPATH += $$PWD
INCLUDEPATH += $$PWD/..
@@ -20,7 +21,8 @@ HEADERS += \
$$PWD/qmljscheck.h \
$$PWD/qmljsscopebuilder.h \
$$PWD/qmljslineinfo.h \
- $$PWD/qmljscompletioncontextfinder.h
+ $$PWD/qmljscompletioncontextfinder.h \
+ $$PWD/qmljsrewriter.h
SOURCES += \
$$PWD/qmljsbind.cpp \
@@ -32,7 +34,8 @@ SOURCES += \
$$PWD/qmljscheck.cpp \
$$PWD/qmljsscopebuilder.cpp \
$$PWD/qmljslineinfo.cpp \
- $$PWD/qmljscompletioncontextfinder.cpp
+ $$PWD/qmljscompletioncontextfinder.cpp \
+ $$PWD/qmljsrewriter.cpp
OTHER_FILES += \
$$PWD/parser/qmljs.g
diff --git a/src/libs/qmljs/qmljsrewriter.cpp b/src/libs/qmljs/qmljsrewriter.cpp
new file mode 100644
index 0000000000..2c7a46c795
--- /dev/null
+++ b/src/libs/qmljs/qmljsrewriter.cpp
@@ -0,0 +1,627 @@
+/**************************************************************************
+**
+** 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 "qmljsrewriter.h"
+
+#include <qmljs/parser/qmljsast_p.h>
+#include <qmljs/parser/qmljsengine_p.h>
+#include <utils/changeset.h>
+
+// ### FIXME: remove these includes:
+#include <QtGui/QTextBlock>
+#include <QtGui/QTextCursor>
+#include <QtGui/QTextDocument>
+
+using namespace QmlJS;
+using namespace QmlJS::AST;
+using namespace Utils;
+
+Rewriter::Rewriter(const QString &originalText,
+ ChangeSet *changeSet,
+ const QStringList &propertyOrder)
+ : m_originalText(originalText)
+ , m_changeSet(changeSet)
+ , m_propertyOrder(propertyOrder)
+{
+ Q_ASSERT(changeSet);
+}
+
+void Rewriter::addBinding(AST::UiObjectInitializer *ast,
+ const QString &propertyName,
+ const QString &propertyValue,
+ BindingType bindingType)
+{
+ UiObjectMemberList *insertAfter = searchMemberToInsertAfter(ast->members,
+ propertyName,
+ m_propertyOrder);
+ SourceLocation endOfPreviousMember;
+ SourceLocation startOfNextMember;
+
+ if (insertAfter == 0 || insertAfter->member == 0) {
+ // insert as first member
+ endOfPreviousMember = ast->lbraceToken;
+
+ if (ast->members && ast->members->member)
+ startOfNextMember = ast->members->member->firstSourceLocation();
+ else
+ startOfNextMember = ast->rbraceToken;
+ } else {
+ endOfPreviousMember = insertAfter->member->lastSourceLocation();
+
+ if (insertAfter->next && insertAfter->next->member)
+ startOfNextMember = insertAfter->next->member->firstSourceLocation();
+ else
+ startOfNextMember = ast->rbraceToken;
+ }
+ const bool isOneLiner = endOfPreviousMember.startLine == startOfNextMember.startLine;
+ bool needsPreceedingSemicolon = false;
+ bool needsTrailingSemicolon = false;
+
+ if (isOneLiner) {
+ if (insertAfter == 0) { // we're inserting after an lbrace
+ if (ast->members) { // we're inserting before a member (and not the rbrace)
+ needsTrailingSemicolon = bindingType == ScriptBinding;
+ }
+ } else { // we're inserting after a member, not after the lbrace
+ if (endOfPreviousMember.isValid()) { // there already is a semicolon after the previous member
+ if (insertAfter->next && insertAfter->next->member) { // and the after us there is a member, not an rbrace, so:
+ needsTrailingSemicolon = bindingType == ScriptBinding;
+ }
+ } else { // there is no semicolon after the previous member (probably because there is an rbrace after us/it, so:
+ needsPreceedingSemicolon = true;
+ }
+ }
+ }
+
+ QString newPropertyTemplate;
+ switch (bindingType) {
+ case ArrayBinding:
+ newPropertyTemplate = QLatin1String("%1: [\n%2\n]");
+ break;
+
+ case ObjectBinding:
+ newPropertyTemplate = QLatin1String("%1: %2");
+ break;
+
+ case ScriptBinding:
+ newPropertyTemplate = QLatin1String("%1: %2");
+ break;
+
+ default:
+ Q_ASSERT(!"unknown property type");
+ }
+
+ if (isOneLiner) {
+ if (needsPreceedingSemicolon)
+ newPropertyTemplate.prepend(QLatin1Char(';'));
+ newPropertyTemplate.prepend(QLatin1Char(' '));
+ if (needsTrailingSemicolon)
+ newPropertyTemplate.append(QLatin1Char(';'));
+ } else {
+ newPropertyTemplate.prepend(QLatin1Char('\n'));
+ }
+
+ m_changeSet->replace(endOfPreviousMember.end(), 0,
+ newPropertyTemplate.arg(propertyName, propertyValue));
+}
+
+UiObjectMemberList *Rewriter::searchMemberToInsertAfter(UiObjectMemberList *members,
+ const QStringList &propertyOrder)
+{
+ const int objectDefinitionInsertionPoint = propertyOrder.indexOf(QString::null);
+
+ UiObjectMemberList *lastObjectDef = 0;
+ UiObjectMemberList *lastNonObjectDef = 0;
+
+ for (UiObjectMemberList *iter = members; iter; iter = iter->next) {
+ UiObjectMember *member = iter->member;
+ int idx = -1;
+
+ if (cast<UiObjectDefinition*>(member))
+ lastObjectDef = iter;
+ else if (UiArrayBinding *arrayBinding = cast<UiArrayBinding*>(member))
+ idx = propertyOrder.indexOf(flatten(arrayBinding->qualifiedId));
+ else if (UiObjectBinding *objectBinding = cast<UiObjectBinding*>(member))
+ idx = propertyOrder.indexOf(flatten(objectBinding->qualifiedId));
+ else if (UiScriptBinding *scriptBinding = cast<UiScriptBinding*>(member))
+ idx = propertyOrder.indexOf(flatten(scriptBinding->qualifiedId));
+ else if (cast<UiPublicMember*>(member))
+ idx = propertyOrder.indexOf(QLatin1String("property"));
+
+ if (idx < objectDefinitionInsertionPoint)
+ lastNonObjectDef = iter;
+ }
+
+ if (lastObjectDef)
+ return lastObjectDef;
+ else
+ return lastNonObjectDef;
+}
+
+UiObjectMemberList *Rewriter::searchMemberToInsertAfter(UiObjectMemberList *members,
+ const QString &propertyName,
+ const QStringList &propertyOrder)
+{
+ if (!members)
+ return 0; // empty members
+
+ QHash<QString, UiObjectMemberList *> orderedMembers;
+
+ for (UiObjectMemberList *iter = members; iter; iter = iter->next) {
+ UiObjectMember *member = iter->member;
+
+ if (UiArrayBinding *arrayBinding = cast<UiArrayBinding*>(member))
+ orderedMembers[flatten(arrayBinding->qualifiedId)] = iter;
+ else if (UiObjectBinding *objectBinding = cast<UiObjectBinding*>(member))
+ orderedMembers[flatten(objectBinding->qualifiedId)] = iter;
+ else if (cast<UiObjectDefinition*>(member))
+ orderedMembers[QString::null] = iter;
+ else if (UiScriptBinding *scriptBinding = cast<UiScriptBinding*>(member))
+ orderedMembers[flatten(scriptBinding->qualifiedId)] = iter;
+ else if (cast<UiPublicMember*>(member))
+ orderedMembers[QLatin1String("property")] = iter;
+ }
+
+ int idx = propertyOrder.indexOf(propertyName);
+ if (idx == -1)
+ idx = propertyOrder.indexOf(QString());
+ if (idx == -1)
+ idx = propertyOrder.size() - 1;
+
+ for (; idx > 0; --idx) {
+ const QString prop = propertyOrder.at(idx - 1);
+ UiObjectMemberList *candidate = orderedMembers.value(prop, 0);
+ if (candidate != 0)
+ return candidate;
+ }
+
+ return 0;
+}
+
+QString Rewriter::flatten(UiQualifiedId *first)
+{
+ QString flatId;
+
+ for (UiQualifiedId* current = first; current; current = current->next) {
+ if (current != first)
+ flatId += '.';
+
+ flatId += current->name->asString();
+ }
+
+ return flatId;
+}
+
+void Rewriter::changeProperty(UiObjectInitializer *ast,
+ const QString &propertyName,
+ const QString &newValue,
+ BindingType binding)
+{
+ QString prefix, suffix;
+ int dotIdx = propertyName.indexOf(QLatin1Char('.'));
+ if (dotIdx != -1) {
+ prefix = propertyName.left(dotIdx);
+ suffix = propertyName.mid(dotIdx + 1);
+ }
+
+ for (UiObjectMemberList *members = ast->members; members; members = members->next) {
+ UiObjectMember *member = members->member;
+
+ // for non-grouped properties:
+ if (isMatchingPropertyMember(propertyName, member)) {
+ switch (binding) {
+ case ArrayBinding:
+ insertIntoArray(cast<UiArrayBinding*>(member), newValue);
+ break;
+
+ case ObjectBinding:
+ replaceMemberValue(member, newValue, false);
+ break;
+
+ case ScriptBinding:
+ replaceMemberValue(member, newValue, nextMemberOnSameLine(members));
+ break;
+
+ default:
+ Q_ASSERT(!"Unhandled QmlRefactoring::PropertyType");
+ }
+
+ break;
+ }
+ // for grouped properties:
+ else if (!prefix.isEmpty()) {
+ if (UiObjectDefinition *def = cast<UiObjectDefinition *>(member)) {
+ if (flatten(def->qualifiedTypeNameId) == prefix) {
+ changeProperty(def->initializer, suffix, newValue, binding);
+ }
+ }
+ }
+ }
+}
+
+void Rewriter::replaceMemberValue(UiObjectMember *propertyMember,
+ const QString &newValue,
+ bool needsSemicolon)
+{
+ QString replacement = newValue;
+ int startOffset = -1;
+ int endOffset = -1;
+ if (UiObjectBinding *objectBinding = AST::cast<UiObjectBinding *>(propertyMember)) {
+ startOffset = objectBinding->qualifiedTypeNameId->identifierToken.offset;
+ endOffset = objectBinding->initializer->rbraceToken.end();
+ } else if (UiScriptBinding *scriptBinding = AST::cast<UiScriptBinding *>(propertyMember)) {
+ startOffset = scriptBinding->statement->firstSourceLocation().offset;
+ endOffset = scriptBinding->statement->lastSourceLocation().end();
+ } else if (UiArrayBinding *arrayBinding = AST::cast<UiArrayBinding *>(propertyMember)) {
+ startOffset = arrayBinding->lbracketToken.offset;
+ endOffset = arrayBinding->rbracketToken.end();
+ } else if (UiPublicMember *publicMember = AST::cast<UiPublicMember*>(propertyMember)) {
+ if (publicMember->expression) {
+ startOffset = publicMember->expression->firstSourceLocation().offset;
+ if (publicMember->semicolonToken.isValid())
+ endOffset = publicMember->semicolonToken.end();
+ else
+ endOffset = publicMember->expression->lastSourceLocation().offset;
+ } else {
+ startOffset = publicMember->lastSourceLocation().end();
+ endOffset = startOffset;
+ if (publicMember->semicolonToken.isValid())
+ startOffset = publicMember->semicolonToken.offset;
+ replacement.prepend(QLatin1String(": "));
+ }
+ } else {
+ return;
+ }
+
+ if (needsSemicolon)
+ replacement += ';';
+
+ m_changeSet->replace(startOffset, endOffset - startOffset, replacement);
+}
+
+bool Rewriter::isMatchingPropertyMember(const QString &propertyName,
+ UiObjectMember *member)
+{
+ if (UiPublicMember *publicMember = cast<UiPublicMember*>(member))
+ return publicMember->name->asString() == propertyName;
+ else if (UiObjectBinding *objectBinding = cast<UiObjectBinding*>(member))
+ return flatten(objectBinding->qualifiedId) == propertyName;
+ else if (UiScriptBinding *scriptBinding = cast<UiScriptBinding*>(member))
+ return flatten(scriptBinding->qualifiedId) == propertyName;
+ else if (UiArrayBinding *arrayBinding = cast<UiArrayBinding*>(member))
+ return flatten(arrayBinding->qualifiedId) == propertyName;
+ else
+ return false;
+}
+
+bool Rewriter::nextMemberOnSameLine(UiObjectMemberList *members)
+{
+ if (members && members->next && members->next->member) {
+ return members->next->member->firstSourceLocation().startLine == members->member->lastSourceLocation().startLine;
+ } else {
+ return false;
+ }
+}
+
+void Rewriter::insertIntoArray(UiArrayBinding *ast, const QString &newValue)
+{
+ if (!ast)
+ return;
+
+ UiObjectMember *lastMember = 0;
+ for (UiArrayMemberList *iter = ast->members; iter; iter = iter->next) {
+ lastMember = iter->member;
+ }
+
+ if (!lastMember)
+ return;
+
+ const int insertionPoint = lastMember->lastSourceLocation().end();
+ m_changeSet->replace(insertionPoint, 0, QLatin1String(",\n") + newValue);
+}
+
+void Rewriter::removeProperty(UiObjectInitializer *ast, const QString &propertyName)
+{
+ QString prefix;
+ int dotIdx = propertyName.indexOf(QLatin1Char('.'));
+ if (dotIdx != -1)
+ prefix = propertyName.left(dotIdx);
+
+ for (UiObjectMemberList *it = ast->members; it; it = it->next) {
+ UiObjectMember *member = it->member;
+
+ // run full name match (for ungrouped properties):
+ if (isMatchingPropertyMember(propertyName, member)) {
+ removeMember(member);
+ }
+ // check for grouped properties:
+ else if (!prefix.isEmpty()) {
+ if (UiObjectDefinition *def = cast<UiObjectDefinition *>(member)) {
+ if (flatten(def->qualifiedTypeNameId) == prefix) {
+ removeGroupedProperty(def, propertyName);
+ }
+ }
+ }
+ }
+}
+
+void Rewriter::removeGroupedProperty(UiObjectDefinition *ast,
+ const QString &propertyName)
+{
+ int dotIdx = propertyName.indexOf(QLatin1Char('.'));
+ if (dotIdx == -1)
+ return;
+
+ const QString propName = propertyName.mid(dotIdx + 1);
+
+ UiObjectMember *wanted = 0;
+ unsigned memberCount = 0;
+ for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
+ ++memberCount;
+ UiObjectMember *member = it->member;
+
+ if (!wanted && isMatchingPropertyMember(propName, member)) {
+ wanted = member;
+ }
+ }
+
+ if (!wanted)
+ return;
+ if (memberCount == 1)
+ removeMember(ast);
+ else
+ removeMember(wanted);
+}
+
+void Rewriter::removeMember(UiObjectMember *member)
+{
+ int start = member->firstSourceLocation().offset;
+ int end = member->lastSourceLocation().end();
+
+ includeSurroundingWhitespace(start, end);
+
+ m_changeSet->replace(start, end - start, QLatin1String(""));
+}
+
+bool Rewriter::includeSurroundingWhitespace(int &start, int &end) const
+{
+ bool includeStartingWhitespace = true;
+ bool paragraphFound = false;
+
+ if (end >= 0) {
+ QChar c = m_originalText.at(end);
+ while (c.isSpace()) {
+ ++end;
+
+ if (c == QChar::ParagraphSeparator) {
+ paragraphFound = true;
+ break;
+ } else if (end == m_originalText.length()) {
+ break;
+ }
+
+ c = m_originalText.at(end);
+ }
+
+ includeStartingWhitespace = paragraphFound;
+ }
+
+ if (includeStartingWhitespace) {
+ while (start > 0) {
+ const QChar c = m_originalText.at(start - 1);
+
+ if (!c.isSpace())
+ break;
+ else if (c == QChar::ParagraphSeparator)
+ break;
+
+ --start;
+ }
+ }
+
+ return paragraphFound;
+}
+
+void Rewriter::includeLeadingEmptyLine(int &start) const
+{
+ QTextDocument doc(m_originalText);
+
+ if (start == 0)
+ return;
+
+ if (doc.characterAt(start - 1) != QChar::ParagraphSeparator)
+ return;
+
+ QTextCursor tc(&doc);
+ tc.setPosition(start);
+ const int blockNr = tc.blockNumber();
+ if (blockNr == 0)
+ return;
+
+ const QTextBlock prevBlock = tc.block().previous();
+ const QString trimmedPrevBlockText = prevBlock.text().trimmed();
+ if (!trimmedPrevBlockText.isEmpty())
+ return;
+
+ start = prevBlock.position();
+}
+
+#if 0
+UiObjectMemberList *QMLRewriter::searchMemberToInsertAfter(UiObjectMemberList *members, const QStringList &propertyOrder)
+{
+ const int objectDefinitionInsertionPoint = propertyOrder.indexOf(QString::null);
+
+ UiObjectMemberList *lastObjectDef = 0;
+ UiObjectMemberList *lastNonObjectDef = 0;
+
+ for (UiObjectMemberList *iter = members; iter; iter = iter->next) {
+ UiObjectMember *member = iter->member;
+ int idx = -1;
+
+ if (cast<UiObjectDefinition*>(member))
+ lastObjectDef = iter;
+ else if (UiArrayBinding *arrayBinding = cast<UiArrayBinding*>(member))
+ idx = propertyOrder.indexOf(flatten(arrayBinding->qualifiedId));
+ else if (UiObjectBinding *objectBinding = cast<UiObjectBinding*>(member))
+ idx = propertyOrder.indexOf(flatten(objectBinding->qualifiedId));
+ else if (UiScriptBinding *scriptBinding = cast<UiScriptBinding*>(member))
+ idx = propertyOrder.indexOf(flatten(scriptBinding->qualifiedId));
+ else if (cast<UiPublicMember*>(member))
+ idx = propertyOrder.indexOf(QLatin1String("property"));
+
+ if (idx < objectDefinitionInsertionPoint)
+ lastNonObjectDef = iter;
+ }
+
+ if (lastObjectDef)
+ return lastObjectDef;
+ else
+ return lastNonObjectDef;
+}
+
+UiObjectMemberList *QMLRewriter::searchMemberToInsertAfter(UiObjectMemberList *members, const QString &propertyName, const QStringList &propertyOrder)
+{
+ if (!members)
+ return 0; // empty members
+
+ QHash<QString, UiObjectMemberList *> orderedMembers;
+
+ for (UiObjectMemberList *iter = members; iter; iter = iter->next) {
+ UiObjectMember *member = iter->member;
+
+ if (UiArrayBinding *arrayBinding = cast<UiArrayBinding*>(member))
+ orderedMembers[flatten(arrayBinding->qualifiedId)] = iter;
+ else if (UiObjectBinding *objectBinding = cast<UiObjectBinding*>(member))
+ orderedMembers[flatten(objectBinding->qualifiedId)] = iter;
+ else if (cast<UiObjectDefinition*>(member))
+ orderedMembers[QString::null] = iter;
+ else if (UiScriptBinding *scriptBinding = cast<UiScriptBinding*>(member))
+ orderedMembers[flatten(scriptBinding->qualifiedId)] = iter;
+ else if (cast<UiPublicMember*>(member))
+ orderedMembers[QLatin1String("property")] = iter;
+ }
+
+ int idx = propertyOrder.indexOf(propertyName);
+ if (idx == -1)
+ idx = propertyOrder.indexOf(QString());
+ if (idx == -1)
+ idx = propertyOrder.size() - 1;
+
+ for (; idx > 0; --idx) {
+ const QString prop = propertyOrder.at(idx - 1);
+ UiObjectMemberList *candidate = orderedMembers.value(prop, 0);
+ if (candidate != 0)
+ return candidate;
+ }
+
+ return 0;
+}
+
+#endif
+
+void Rewriter::appendToArrayBinding(UiArrayBinding *arrayBinding,
+ const QString &content)
+{
+ UiObjectMember *lastMember = 0;
+ for (UiArrayMemberList *iter = arrayBinding->members; iter; iter = iter->next)
+ if (iter->member)
+ lastMember = iter->member;
+
+ if (!lastMember)
+ return; // an array binding cannot be empty, so there will (or should) always be a last member.
+
+ const int insertionPoint = lastMember->lastSourceLocation().end();
+
+ m_changeSet->replace(insertionPoint, 0, QLatin1String(",\n") + content);
+}
+
+void Rewriter::addObject(UiObjectInitializer *ast, const QString &content)
+{
+ UiObjectMemberList *insertAfter = searchMemberToInsertAfter(ast->members, m_propertyOrder);
+
+ int insertionPoint;
+ QString textToInsert;
+ if (insertAfter && insertAfter->member) {
+ insertionPoint = insertAfter->member->lastSourceLocation().end();
+ textToInsert += QLatin1String("\n");
+ } else {
+ insertionPoint = ast->lbraceToken.end();
+ }
+
+ textToInsert += content;
+ m_changeSet->replace(insertionPoint, 0, QLatin1String("\n") + textToInsert);
+}
+
+void Rewriter::removeObjectMember(UiObjectMember *member, UiObjectMember *parent)
+{
+ int start = member->firstSourceLocation().offset;
+ int end = member->lastSourceLocation().end();
+
+ if (UiArrayBinding *parentArray = cast<UiArrayBinding *>(parent)) {
+ extendToLeadingOrTrailingComma(parentArray, member, start, end);
+ } else {
+ includeSurroundingWhitespace(start, end);
+ }
+
+ includeLeadingEmptyLine(start);
+ m_changeSet->replace(start, end - start, QLatin1String(""));
+}
+
+void Rewriter::extendToLeadingOrTrailingComma(UiArrayBinding *parentArray,
+ UiObjectMember *member,
+ int &start,
+ int &end) const
+{
+ UiArrayMemberList *currentMember = 0;
+ for (UiArrayMemberList *it = parentArray->members; it; it = it->next) {
+ if (it->member == member) {
+ currentMember = it;
+ break;
+ }
+ }
+
+ if (!currentMember)
+ return;
+
+ if (currentMember->commaToken.isValid()) {
+ // leading comma
+ start = currentMember->commaToken.offset;
+ if (includeSurroundingWhitespace(start, end))
+ --end;
+ } else if (currentMember->next && currentMember->next->commaToken.isValid()) {
+ // trailing comma
+ end = currentMember->next->commaToken.end();
+ includeSurroundingWhitespace(start, end);
+ } else {
+ // array with 1 element, so remove the complete binding
+ start = parentArray->firstSourceLocation().offset;
+ end = parentArray->lastSourceLocation().end();
+ includeSurroundingWhitespace(start, end);
+ }
+}
diff --git a/src/libs/qmljs/qmljsrewriter.h b/src/libs/qmljs/qmljsrewriter.h
new file mode 100644
index 0000000000..274da0c180
--- /dev/null
+++ b/src/libs/qmljs/qmljsrewriter.h
@@ -0,0 +1,110 @@
+/**************************************************************************
+**
+** 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 QMLJSREWRITER_H
+#define QMLJSREWRITER_H
+
+#include <qmljs/qmljs_global.h>
+
+#include <qmljs/parser/qmljsastfwd_p.h>
+
+#include <QtCore/QStringList>
+
+namespace Utils {
+class ChangeSet;
+}
+
+namespace QmlJS {
+
+class QMLJS_EXPORT Rewriter
+{
+public:
+ enum BindingType {
+ ScriptBinding,
+ ObjectBinding,
+ ArrayBinding
+ };
+
+public:
+ Rewriter(const QString &originalText,
+ Utils::ChangeSet *changeSet,
+ const QStringList &propertyOrder);
+
+ void addBinding(AST::UiObjectInitializer *ast,
+ const QString &propertyName,
+ const QString &propertyValue,
+ BindingType bindingType);
+
+ void changeProperty(AST::UiObjectInitializer *ast,
+ const QString &propertyName,
+ const QString &newValue,
+ BindingType binding);
+
+ void removeProperty(AST::UiObjectInitializer *ast, const QString &propertyName);
+
+ void appendToArrayBinding(AST::UiArrayBinding *arrayBinding,
+ const QString &content);
+ void addObject(AST::UiObjectInitializer *ast, const QString &content);
+ void removeObjectMember(AST::UiObjectMember *member, AST::UiObjectMember *parent);
+
+ static AST::UiObjectMemberList *searchMemberToInsertAfter(AST::UiObjectMemberList *members, const QStringList &propertyOrder);
+ static AST::UiObjectMemberList *searchMemberToInsertAfter(AST::UiObjectMemberList *members, const QString &propertyName, const QStringList &propertyOrder);
+ static QString flatten(AST::UiQualifiedId *first);
+
+ bool includeSurroundingWhitespace(int &start, int &end) const;
+ void includeLeadingEmptyLine(int &start) const;
+
+private:
+ void replaceMemberValue(AST::UiObjectMember *propertyMember,
+ const QString &newValue,
+ bool needsSemicolon);
+ static bool isMatchingPropertyMember(const QString &propertyName,
+ AST::UiObjectMember *member);
+ static bool nextMemberOnSameLine(AST::UiObjectMemberList *members);
+
+ void insertIntoArray(AST::UiArrayBinding* ast, const QString &newValue);
+
+ void removeMember(AST::UiObjectMember *member);
+ void removeGroupedProperty(AST::UiObjectDefinition *ast,
+ const QString &propertyName);
+
+ void extendToLeadingOrTrailingComma(AST::UiArrayBinding *parentArray,
+ AST::UiObjectMember *member,
+ int &start,
+ int &end) const;
+
+private:
+ QString m_originalText;
+ Utils::ChangeSet *m_changeSet;
+ const QStringList m_propertyOrder;
+};
+
+} // namespace QmlJS
+
+#endif // QMLJSREWRITER_H
diff --git a/src/plugins/qmldesigner/designercore/filemanager/addarraymembervisitor.cpp b/src/plugins/qmldesigner/designercore/filemanager/addarraymembervisitor.cpp
index 7d71dcdbff..3652dfe886 100644
--- a/src/plugins/qmldesigner/designercore/filemanager/addarraymembervisitor.cpp
+++ b/src/plugins/qmldesigner/designercore/filemanager/addarraymembervisitor.cpp
@@ -84,6 +84,7 @@ bool AddArrayMemberVisitor::visit(QmlJS::AST::UiObjectDefinition *ast)
return !didRewriting();
}
+// FIXME: duplicate code in the QmlJS::Rewriter class, remove this
void AddArrayMemberVisitor::insertInto(QmlJS::AST::UiArrayBinding *arrayBinding)
{
UiObjectMember *lastMember = 0;
diff --git a/src/plugins/qmldesigner/designercore/filemanager/addobjectvisitor.cpp b/src/plugins/qmldesigner/designercore/filemanager/addobjectvisitor.cpp
index e02dec647e..2ded16aa2a 100644
--- a/src/plugins/qmldesigner/designercore/filemanager/addobjectvisitor.cpp
+++ b/src/plugins/qmldesigner/designercore/filemanager/addobjectvisitor.cpp
@@ -70,6 +70,7 @@ bool AddObjectVisitor::visit(QmlJS::AST::UiObjectDefinition *ast)
return !didRewriting();
}
+// FIXME: duplicate code in the QmlJS::Rewriter class, remove this
void AddObjectVisitor::insertInto(QmlJS::AST::UiObjectInitializer *ast)
{
UiObjectMemberList *insertAfter = searchMemberToInsertAfter(ast->members, m_propertyOrder);
diff --git a/src/plugins/qmldesigner/designercore/filemanager/addpropertyvisitor.cpp b/src/plugins/qmldesigner/designercore/filemanager/addpropertyvisitor.cpp
index d2b1fc81fb..3a35d4d2c9 100644
--- a/src/plugins/qmldesigner/designercore/filemanager/addpropertyvisitor.cpp
+++ b/src/plugins/qmldesigner/designercore/filemanager/addpropertyvisitor.cpp
@@ -58,6 +58,7 @@ bool AddPropertyVisitor::visit(QmlJS::AST::UiObjectDefinition *ast)
return false;
if (ast->firstSourceLocation().offset == m_parentLocation) {
+ // FIXME: change this to use the QmlJS::Rewriter class
addInMembers(ast->initializer);
return false;
}
@@ -71,6 +72,7 @@ bool AddPropertyVisitor::visit(QmlJS::AST::UiObjectBinding *ast)
return false;
if (ast->qualifiedTypeNameId->identifierToken.offset == m_parentLocation) {
+ // FIXME: change this to use the QmlJS::Rewriter class
addInMembers(ast->initializer);
return false;
}
@@ -78,6 +80,7 @@ bool AddPropertyVisitor::visit(QmlJS::AST::UiObjectBinding *ast)
return !didRewriting();
}
+// FIXME: duplicate code in the QmlJS::Rewriter class, remove this
void AddPropertyVisitor::addInMembers(QmlJS::AST::UiObjectInitializer *initializer)
{
UiObjectMemberList *insertAfter = searchMemberToInsertAfter(initializer->members, m_name, m_propertyOrder);
diff --git a/src/plugins/qmldesigner/designercore/filemanager/changepropertyvisitor.cpp b/src/plugins/qmldesigner/designercore/filemanager/changepropertyvisitor.cpp
index 2256a342d5..73fb0f9771 100644
--- a/src/plugins/qmldesigner/designercore/filemanager/changepropertyvisitor.cpp
+++ b/src/plugins/qmldesigner/designercore/filemanager/changepropertyvisitor.cpp
@@ -58,6 +58,7 @@ bool ChangePropertyVisitor::visit(QmlJS::AST::UiObjectDefinition *ast)
const quint32 objectStart = ast->firstSourceLocation().offset;
if (objectStart == m_parentLocation) {
+ // FIXME: change this to use the QmlJS::Rewriter class
replaceInMembers(ast->initializer, m_name);
return false;
}
@@ -73,6 +74,7 @@ bool ChangePropertyVisitor::visit(QmlJS::AST::UiObjectBinding *ast)
const quint32 objectStart = ast->qualifiedTypeNameId->identifierToken.offset;
if (objectStart == m_parentLocation) {
+ // FIXME: change this to use the QmlJS::Rewriter class
replaceInMembers(ast->initializer, m_name);
return false;
}
@@ -80,6 +82,7 @@ bool ChangePropertyVisitor::visit(QmlJS::AST::UiObjectBinding *ast)
return !didRewriting();
}
+// FIXME: duplicate code in the QmlJS::Rewriter class, remove this
void ChangePropertyVisitor::replaceInMembers(UiObjectInitializer *initializer,
const QString &propertyName)
{
@@ -125,6 +128,7 @@ void ChangePropertyVisitor::replaceInMembers(UiObjectInitializer *initializer,
}
}
+// FIXME: duplicate code in the QmlJS::Rewriter class, remove this
void ChangePropertyVisitor::replaceMemberValue(UiObjectMember *propertyMember, bool needsSemicolon)
{
QString replacement = m_value;
@@ -164,6 +168,7 @@ void ChangePropertyVisitor::replaceMemberValue(UiObjectMember *propertyMember, b
setDidRewriting(true);
}
+// FIXME: duplicate code in the QmlJS::Rewriter class, remove this
bool ChangePropertyVisitor::isMatchingPropertyMember(const QString &propName,
UiObjectMember *member)
{
@@ -180,6 +185,7 @@ bool ChangePropertyVisitor::isMatchingPropertyMember(const QString &propName,
}
}
+// FIXME: duplicate code in the QmlJS::Rewriter class, remove this
bool ChangePropertyVisitor::nextMemberOnSameLine(UiObjectMemberList *members)
{
if (members && members->next && members->next->member) {
@@ -189,6 +195,7 @@ bool ChangePropertyVisitor::nextMemberOnSameLine(UiObjectMemberList *members)
}
}
+// FIXME: duplicate code in the QmlJS::Rewriter class, remove this
void ChangePropertyVisitor::insertIntoArray(QmlJS::AST::UiArrayBinding *ast)
{
if (!ast)
diff --git a/src/plugins/qmldesigner/designercore/filemanager/qmlrewriter.cpp b/src/plugins/qmldesigner/designercore/filemanager/qmlrewriter.cpp
index 83a304e931..ac83a55ff1 100644
--- a/src/plugins/qmldesigner/designercore/filemanager/qmlrewriter.cpp
+++ b/src/plugins/qmldesigner/designercore/filemanager/qmlrewriter.cpp
@@ -195,6 +195,7 @@ bool QMLRewriter::isMissingSemicolon(QmlJS::AST::Statement *stmt)
}
}
+// FIXME: duplicate code in the QmlJS::Rewriter class, remove this
QString QMLRewriter::flatten(UiQualifiedId *first)
{
QString flatId;
@@ -209,6 +210,7 @@ QString QMLRewriter::flatten(UiQualifiedId *first)
return flatId;
}
+// FIXME: duplicate code in the QmlJS::Rewriter class, remove this
bool QMLRewriter::includeSurroundingWhitespace(int &start, int &end) const
{
QTextDocument *doc = m_textModifier->textDocument();
@@ -249,6 +251,7 @@ bool QMLRewriter::includeSurroundingWhitespace(int &start, int &end) const
return paragraphFound;
}
+// FIXME: duplicate code in the QmlJS::Rewriter class, remove this
void QMLRewriter::includeLeadingEmptyLine(int &start) const
{
QTextDocument *doc = textModifier()->textDocument();
@@ -273,6 +276,7 @@ void QMLRewriter::includeLeadingEmptyLine(int &start) const
start = prevBlock.position();
}
+// FIXME: duplicate code in the QmlJS::Rewriter class, remove this
UiObjectMemberList *QMLRewriter::searchMemberToInsertAfter(UiObjectMemberList *members, const QStringList &propertyOrder)
{
const int objectDefinitionInsertionPoint = propertyOrder.indexOf(QString::null);
@@ -305,6 +309,7 @@ UiObjectMemberList *QMLRewriter::searchMemberToInsertAfter(UiObjectMemberList *m
return lastNonObjectDef;
}
+// FIXME: duplicate code in the QmlJS::Rewriter class, remove this
UiObjectMemberList *QMLRewriter::searchMemberToInsertAfter(UiObjectMemberList *members, const QString &propertyName, const QStringList &propertyOrder)
{
if (!members)
diff --git a/src/plugins/qmldesigner/designercore/filemanager/removepropertyvisitor.cpp b/src/plugins/qmldesigner/designercore/filemanager/removepropertyvisitor.cpp
index a3dcd72f77..136bf85d4c 100644
--- a/src/plugins/qmldesigner/designercore/filemanager/removepropertyvisitor.cpp
+++ b/src/plugins/qmldesigner/designercore/filemanager/removepropertyvisitor.cpp
@@ -48,6 +48,7 @@ RemovePropertyVisitor::RemovePropertyVisitor(QmlDesigner::TextModifier &modifier
bool RemovePropertyVisitor::visit(QmlJS::AST::UiObjectBinding *ast)
{
if (ast->firstSourceLocation().offset == parentLocation) {
+ // FIXME: change this to use the QmlJS::Rewriter class
removeFrom(ast->initializer);
}
@@ -57,12 +58,14 @@ bool RemovePropertyVisitor::visit(QmlJS::AST::UiObjectBinding *ast)
bool RemovePropertyVisitor::visit(QmlJS::AST::UiObjectDefinition *ast)
{
if (ast->firstSourceLocation().offset == parentLocation) {
+ // FIXME: change this to use the QmlJS::Rewriter class
removeFrom(ast->initializer);
}
return !didRewriting();
}
+// FIXME: duplicate code in the QmlJS::Rewriter class, remove this
void RemovePropertyVisitor::removeFrom(QmlJS::AST::UiObjectInitializer *ast)
{
QString prefix;
@@ -88,6 +91,7 @@ void RemovePropertyVisitor::removeFrom(QmlJS::AST::UiObjectInitializer *ast)
}
}
+// FIXME: duplicate code in the QmlJS::Rewriter class, remove this
void RemovePropertyVisitor::removeGroupedProperty(UiObjectDefinition *ast)
{
int dotIdx = propertyName.indexOf(QLatin1Char('.'));
@@ -115,6 +119,7 @@ void RemovePropertyVisitor::removeGroupedProperty(UiObjectDefinition *ast)
removeMember(wanted);
}
+// FIXME: duplicate code in the QmlJS::Rewriter class, remove this
void RemovePropertyVisitor::removeMember(UiObjectMember *member)
{
int start = member->firstSourceLocation().offset;
@@ -126,6 +131,7 @@ void RemovePropertyVisitor::removeMember(UiObjectMember *member)
setDidRewriting(true);
}
+// FIXME: duplicate code in the QmlJS::Rewriter class, remove this
bool RemovePropertyVisitor::memberNameMatchesPropertyName(const QString &propertyName, UiObjectMember *ast)
{
if (UiPublicMember *publicMember = cast<UiPublicMember*>(ast))
diff --git a/src/plugins/qmldesigner/designercore/filemanager/removeuiobjectmembervisitor.cpp b/src/plugins/qmldesigner/designercore/filemanager/removeuiobjectmembervisitor.cpp
index 3da6a1eeec..05cdc3ce72 100644
--- a/src/plugins/qmldesigner/designercore/filemanager/removeuiobjectmembervisitor.cpp
+++ b/src/plugins/qmldesigner/designercore/filemanager/removeuiobjectmembervisitor.cpp
@@ -65,6 +65,7 @@ bool RemoveUIObjectMemberVisitor::visit(QmlJS::AST::UiObjectBinding *ast) { retu
bool RemoveUIObjectMemberVisitor::visit(QmlJS::AST::UiScriptBinding *ast) { return visitObjectMember(ast); }
bool RemoveUIObjectMemberVisitor::visit(QmlJS::AST::UiArrayBinding *ast) { return visitObjectMember(ast); }
+// FIXME: duplicate code in the QmlJS::Rewriter class, remove this
bool RemoveUIObjectMemberVisitor::visitObjectMember(QmlJS::AST::UiObjectMember *ast)
{
const quint32 memberStart = ast->firstSourceLocation().offset;
@@ -106,6 +107,7 @@ UiArrayBinding *RemoveUIObjectMemberVisitor::containingArray() const
return 0;
}
+// FIXME: duplicate code in the QmlJS::Rewriter class, remove this
void RemoveUIObjectMemberVisitor::extendToLeadingOrTrailingComma(QmlJS::AST::UiArrayBinding *parentArray,
QmlJS::AST::UiObjectMember *ast,
int &start,