From 5c45e9ec343d8edaddfd0a1f92ca4bc2a307923c Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 29 Apr 2016 10:31:21 +0200 Subject: [PATCH] QmlJSEditor: Improving ComponentFromObjectDef Now the use can choose which properties are defined inside the component and which properties are kept in the original file. To make the concept clear the dialog contains a snippet of the generated code. Change-Id: I92f9f241c9780345dd03b991232c4a811356c266 Reviewed-by: Tim Jenssen --- .../qmljscomponentfromobjectdef.cpp | 79 +++++++++++-- .../qmljseditor/qmljscomponentnamedialog.cpp | 65 +++++++++++ .../qmljseditor/qmljscomponentnamedialog.h | 12 +- .../qmljseditor/qmljscomponentnamedialog.ui | 106 ++++++++++-------- 4 files changed, 208 insertions(+), 54 deletions(-) diff --git a/src/plugins/qmljseditor/qmljscomponentfromobjectdef.cpp b/src/plugins/qmljseditor/qmljscomponentfromobjectdef.cpp index ffb7f173216..eca12dc55f7 100644 --- a/src/plugins/qmljseditor/qmljscomponentfromobjectdef.cpp +++ b/src/plugins/qmljseditor/qmljscomponentfromobjectdef.cpp @@ -35,6 +35,8 @@ #include #include #include +#include +#include #include #include #include @@ -61,6 +63,7 @@ class Operation: public QmlJSQuickFixOperation QString m_idName, m_componentName; SourceLocation m_firstSourceLocation; SourceLocation m_lastSourceLocation; + UiObjectInitializer *m_initializer; public: void init() { @@ -78,7 +81,8 @@ public: : QmlJSQuickFixOperation(interface, 0), m_idName(idOfObject(objDef)), m_firstSourceLocation(objDef->firstSourceLocation()), - m_lastSourceLocation(objDef->lastSourceLocation()) + m_lastSourceLocation(objDef->lastSourceLocation()), + m_initializer(objDef->initializer) { init(); } @@ -88,7 +92,8 @@ public: : QmlJSQuickFixOperation(interface, 0), m_idName(idOfObject(objDef)), m_firstSourceLocation(objDef->qualifiedTypeNameId->firstSourceLocation()), - m_lastSourceLocation(objDef->lastSourceLocation()) + m_lastSourceLocation(objDef->lastSourceLocation()), + m_initializer(objDef->initializer) { init(); } @@ -98,8 +103,33 @@ public: { QString componentName = m_componentName; QString path = QFileInfo(fileName()).path(); - bool confirm = ComponentNameDialog::go(&componentName, &path, Core::ICore::dialogParent()); + QmlJS::PropertyReader propertyReader(currentFile->qmljsDocument(), m_initializer); + QStringList result; + QStringList sourcePreview; + + if (!m_idName.isEmpty()) + sourcePreview.append(QLatin1String(" id: ") + m_idName); + else + sourcePreview.append(QString()); + + QStringList sortedPropertiesWithoutId; + + foreach (const QString &property, propertyReader.properties()) + if (property != QLatin1String("id")) + sortedPropertiesWithoutId.append(property); + + sortedPropertiesWithoutId.sort(); + + foreach (const QString &property, sortedPropertiesWithoutId) + sourcePreview.append(QLatin1String(" ") + property + QLatin1String(": ") + propertyReader.readAstValue(property)); + + bool confirm = ComponentNameDialog::go(&componentName, &path, + sortedPropertiesWithoutId, + sourcePreview, + QFileInfo(fileName()).fileName(), + &result, + Core::ICore::dialogParent()); if (!confirm) return; @@ -112,18 +142,44 @@ public: QString imports; UiProgram *prog = currentFile->qmljsDocument()->qmlProgram(); if (prog && prog->headers) { - const int start = currentFile->startOf(prog->headers->firstSourceLocation()); - const int end = currentFile->startOf(prog->members->member->firstSourceLocation()); + const unsigned int start = currentFile->startOf(prog->headers->firstSourceLocation()); + const unsigned int end = currentFile->startOf(prog->members->member->firstSourceLocation()); imports = currentFile->textOf(start, end); } - const int start = currentFile->startOf(m_firstSourceLocation); - const int end = currentFile->startOf(m_lastSourceLocation); - const QString txt = imports + currentFile->textOf(start, end) + const unsigned int start = currentFile->startOf(m_firstSourceLocation); + const unsigned int end = currentFile->startOf(m_lastSourceLocation); + QString newComponentSource = imports + currentFile->textOf(start, end) + QLatin1String("}\n"); + //Remove properties from resulting code... + + Utils::ChangeSet changeSet; + QmlJS::Rewriter rewriter(newComponentSource, &changeSet, QStringList()); + + QmlJS::Dialect dialect = QmlJS::Dialect::Qml; + + QmlJS::Document::MutablePtr doc = QmlJS::Document::create(newFileName, dialect); + doc->setSource(newComponentSource); + doc->parseQml(); + + if (doc->isParsedCorrectly()) { + + UiObjectMember *astRootNode = 0; + if (UiProgram *program = doc->qmlProgram()) + if (program->members) + astRootNode = program->members->member; + + foreach (const QString &property, result) + rewriter.removeBindingByName(initializerOfObject(astRootNode), property); + } else { + qWarning() << Q_FUNC_INFO << "parsing failed:" << newComponentSource; + } + + changeSet.apply(&newComponentSource); + // stop if we can't create the new file - if (!refactoring.createFile(newFileName, txt)) + if (!refactoring.createFile(newFileName, newComponentSource)) return; if (path == QFileInfo(fileName()).path()) { @@ -151,10 +207,14 @@ public: Core::VcsManager::msgToAddToVcsFailed(QStringList(newFileName), versionControl)); } } + QString replacement = componentName + QLatin1String(" {\n"); if (!m_idName.isEmpty()) replacement += QLatin1String("id: ") + m_idName + QLatin1Char('\n'); + foreach (const QString &property, result) + replacement += property + QLatin1String(": ") + propertyReader.readAstValue(property) + QLatin1Char('\n'); + Utils::ChangeSet changes; changes.replace(start, end, replacement); currentFile->setChangeSet(changes); @@ -174,6 +234,7 @@ void ComponentFromObjectDef::match(const QmlJSQuickFixInterface &interface, Quic for (int i = path.size() - 1; i >= 0; --i) { Node *node = path.at(i); if (UiObjectDefinition *objDef = cast(node)) { + if (!interface->currentFile()->isCursorOn(objDef->qualifiedTypeNameId)) return; // check that the node is not the root node diff --git a/src/plugins/qmljseditor/qmljscomponentnamedialog.cpp b/src/plugins/qmljseditor/qmljscomponentnamedialog.cpp index d45b340d9aa..3f9ce30019a 100644 --- a/src/plugins/qmljseditor/qmljscomponentnamedialog.cpp +++ b/src/plugins/qmljseditor/qmljscomponentnamedialog.cpp @@ -51,6 +51,10 @@ ComponentNameDialog::~ComponentNameDialog() bool ComponentNameDialog::go(QString *proposedName, QString *proposedPath, + const QStringList &properties, + const QStringList &sourcePreview, + const QString &oldFileName, + QStringList *result, QWidget *parent) { Q_ASSERT(proposedName); @@ -60,20 +64,81 @@ bool ComponentNameDialog::go(QString *proposedName, d.ui->componentNameEdit->setNamespacesEnabled(false); d.ui->componentNameEdit->setLowerCaseFileName(false); d.ui->componentNameEdit->setForceFirstCapitalLetter(true); + if (proposedName->isEmpty()) + *proposedName = QLatin1String("MyComponent"); d.ui->componentNameEdit->setText(*proposedName); d.ui->pathEdit->setExpectedKind(Utils::PathChooser::ExistingDirectory); d.ui->pathEdit->setHistoryCompleter(QLatin1String("QmlJs.Component.History")); d.ui->pathEdit->setPath(*proposedPath); + d.ui->label->setText(tr("Property assignments for %1:").arg(oldFileName)); + d.m_sourcePreview = sourcePreview; + + d.setProperties(properties); + + d.generateCodePreview(); + + d.connect(d.ui->listWidget, &QListWidget::itemChanged, &d, &ComponentNameDialog::generateCodePreview); + d.connect(d.ui->componentNameEdit, &QLineEdit::textChanged, &d, &ComponentNameDialog::generateCodePreview); if (QDialog::Accepted == d.exec()) { *proposedName = d.ui->componentNameEdit->text(); *proposedPath = d.ui->pathEdit->path(); + + if (result) + *result = d.propertiesToKeep(); return true; } return false; } +void ComponentNameDialog::setProperties(const QStringList &properties) +{ + ui->listWidget->addItems(properties); + + for (int i = 0; i < ui->listWidget->count(); ++i) { + QListWidgetItem *item = ui->listWidget->item(i); + item->setFlags(Qt::ItemIsUserCheckable | Qt:: ItemIsEnabled); + if (item->text() == QLatin1String("x") + || item->text() == QLatin1String("y")) + ui->listWidget->item(i)->setCheckState(Qt::Checked); + else + ui->listWidget->item(i)->setCheckState(Qt::Unchecked); + } +} + +QStringList ComponentNameDialog::propertiesToKeep() const +{ + QStringList result; + for (int i = 0; i < ui->listWidget->count(); ++i) { + QListWidgetItem *item = ui->listWidget->item(i); + + if (item->checkState() == Qt::Checked) + result.append(item->text()); + } + + return result; +} + +void ComponentNameDialog::generateCodePreview() +{ + const QString componentName = ui->componentNameEdit->text(); + + ui->plainTextEdit->clear(); + ui->plainTextEdit->appendPlainText(componentName + QLatin1String(" {")); + if (!m_sourcePreview.first().isEmpty()) + ui->plainTextEdit->appendPlainText(m_sourcePreview.first()); + + for (int i = 0; i < ui->listWidget->count(); ++i) { + QListWidgetItem *item = ui->listWidget->item(i); + + if (item->checkState() == Qt::Checked) + ui->plainTextEdit->appendPlainText(m_sourcePreview.at(i + 1)); + } + + ui->plainTextEdit->appendPlainText(QLatin1String("}")); +} + void ComponentNameDialog::choosePath() { QString dir = QFileDialog::getExistingDirectory(this, tr("Choose a path"), diff --git a/src/plugins/qmljseditor/qmljscomponentnamedialog.h b/src/plugins/qmljseditor/qmljscomponentnamedialog.h index b7c5a7335f1..45309142361 100644 --- a/src/plugins/qmljseditor/qmljscomponentnamedialog.h +++ b/src/plugins/qmljseditor/qmljscomponentnamedialog.h @@ -40,7 +40,16 @@ public: explicit ComponentNameDialog(QWidget *parent = 0); ~ComponentNameDialog(); - static bool go(QString *proposedName, QString *proposedPath, QWidget *parent = 0); + static bool go(QString *proposedName, QString *proposedPath, + const QStringList &properties, const QStringList &sourcePreview, const QString &oldFileName, + QStringList *result, + QWidget *parent = 0); + + void setProperties(const QStringList &properties); + + QStringList propertiesToKeep() const; + + void generateCodePreview(); public slots: void choosePath(); @@ -51,6 +60,7 @@ protected: private: Ui::ComponentNameDialog *ui; + QStringList m_sourcePreview; }; } // namespace Internal diff --git a/src/plugins/qmljseditor/qmljscomponentnamedialog.ui b/src/plugins/qmljseditor/qmljscomponentnamedialog.ui index 1f70caf25ed..17ff4b9b973 100644 --- a/src/plugins/qmljseditor/qmljscomponentnamedialog.ui +++ b/src/plugins/qmljseditor/qmljscomponentnamedialog.ui @@ -7,67 +7,50 @@ 0 0 495 - 138 + 311 Move Component into Separate File - - - 0 - - - - - 8 + + + + + Property assignments for - - 10 + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - - - - - - - - - - - - Path: - - - - - - - Component name: - - - - - - - + - - + + + + + 0 + 0 + + + + + + + + + Qt::Vertical - 0 - 0 + 20 + 40 - + Qt::Horizontal @@ -77,6 +60,41 @@ + + + + + + Component name: + + + + + + + Component Name + + + + + + + + + + + + + + Path: + + + + + + + +