forked from qt-creator/qt-creator
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 <tim.jenssen@theqtcompany.com>
This commit is contained in:
@@ -35,6 +35,8 @@
|
|||||||
#include <qmljs/parser/qmljsast_p.h>
|
#include <qmljs/parser/qmljsast_p.h>
|
||||||
#include <qmljs/qmljsdocument.h>
|
#include <qmljs/qmljsdocument.h>
|
||||||
#include <qmljs/qmljsutils.h>
|
#include <qmljs/qmljsutils.h>
|
||||||
|
#include <qmljs/qmljspropertyreader.h>
|
||||||
|
#include <qmljs/qmljsrewriter.h>
|
||||||
#include <qmljstools/qmljsrefactoringchanges.h>
|
#include <qmljstools/qmljsrefactoringchanges.h>
|
||||||
#include <projectexplorer/session.h>
|
#include <projectexplorer/session.h>
|
||||||
#include <projectexplorer/projectnodes.h>
|
#include <projectexplorer/projectnodes.h>
|
||||||
@@ -61,6 +63,7 @@ class Operation: public QmlJSQuickFixOperation
|
|||||||
QString m_idName, m_componentName;
|
QString m_idName, m_componentName;
|
||||||
SourceLocation m_firstSourceLocation;
|
SourceLocation m_firstSourceLocation;
|
||||||
SourceLocation m_lastSourceLocation;
|
SourceLocation m_lastSourceLocation;
|
||||||
|
UiObjectInitializer *m_initializer;
|
||||||
public:
|
public:
|
||||||
void init()
|
void init()
|
||||||
{
|
{
|
||||||
@@ -78,7 +81,8 @@ public:
|
|||||||
: QmlJSQuickFixOperation(interface, 0),
|
: QmlJSQuickFixOperation(interface, 0),
|
||||||
m_idName(idOfObject(objDef)),
|
m_idName(idOfObject(objDef)),
|
||||||
m_firstSourceLocation(objDef->firstSourceLocation()),
|
m_firstSourceLocation(objDef->firstSourceLocation()),
|
||||||
m_lastSourceLocation(objDef->lastSourceLocation())
|
m_lastSourceLocation(objDef->lastSourceLocation()),
|
||||||
|
m_initializer(objDef->initializer)
|
||||||
{
|
{
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
@@ -88,7 +92,8 @@ public:
|
|||||||
: QmlJSQuickFixOperation(interface, 0),
|
: QmlJSQuickFixOperation(interface, 0),
|
||||||
m_idName(idOfObject(objDef)),
|
m_idName(idOfObject(objDef)),
|
||||||
m_firstSourceLocation(objDef->qualifiedTypeNameId->firstSourceLocation()),
|
m_firstSourceLocation(objDef->qualifiedTypeNameId->firstSourceLocation()),
|
||||||
m_lastSourceLocation(objDef->lastSourceLocation())
|
m_lastSourceLocation(objDef->lastSourceLocation()),
|
||||||
|
m_initializer(objDef->initializer)
|
||||||
{
|
{
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
@@ -98,8 +103,33 @@ public:
|
|||||||
{
|
{
|
||||||
QString componentName = m_componentName;
|
QString componentName = m_componentName;
|
||||||
QString path = QFileInfo(fileName()).path();
|
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)
|
if (!confirm)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -112,18 +142,44 @@ public:
|
|||||||
QString imports;
|
QString imports;
|
||||||
UiProgram *prog = currentFile->qmljsDocument()->qmlProgram();
|
UiProgram *prog = currentFile->qmljsDocument()->qmlProgram();
|
||||||
if (prog && prog->headers) {
|
if (prog && prog->headers) {
|
||||||
const int start = currentFile->startOf(prog->headers->firstSourceLocation());
|
const unsigned int start = currentFile->startOf(prog->headers->firstSourceLocation());
|
||||||
const int end = currentFile->startOf(prog->members->member->firstSourceLocation());
|
const unsigned int end = currentFile->startOf(prog->members->member->firstSourceLocation());
|
||||||
imports = currentFile->textOf(start, end);
|
imports = currentFile->textOf(start, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
const int start = currentFile->startOf(m_firstSourceLocation);
|
const unsigned int start = currentFile->startOf(m_firstSourceLocation);
|
||||||
const int end = currentFile->startOf(m_lastSourceLocation);
|
const unsigned int end = currentFile->startOf(m_lastSourceLocation);
|
||||||
const QString txt = imports + currentFile->textOf(start, end)
|
QString newComponentSource = imports + currentFile->textOf(start, end)
|
||||||
+ QLatin1String("}\n");
|
+ 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
|
// stop if we can't create the new file
|
||||||
if (!refactoring.createFile(newFileName, txt))
|
if (!refactoring.createFile(newFileName, newComponentSource))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (path == QFileInfo(fileName()).path()) {
|
if (path == QFileInfo(fileName()).path()) {
|
||||||
@@ -151,10 +207,14 @@ public:
|
|||||||
Core::VcsManager::msgToAddToVcsFailed(QStringList(newFileName), versionControl));
|
Core::VcsManager::msgToAddToVcsFailed(QStringList(newFileName), versionControl));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString replacement = componentName + QLatin1String(" {\n");
|
QString replacement = componentName + QLatin1String(" {\n");
|
||||||
if (!m_idName.isEmpty())
|
if (!m_idName.isEmpty())
|
||||||
replacement += QLatin1String("id: ") + m_idName + QLatin1Char('\n');
|
replacement += QLatin1String("id: ") + m_idName + QLatin1Char('\n');
|
||||||
|
|
||||||
|
foreach (const QString &property, result)
|
||||||
|
replacement += property + QLatin1String(": ") + propertyReader.readAstValue(property) + QLatin1Char('\n');
|
||||||
|
|
||||||
Utils::ChangeSet changes;
|
Utils::ChangeSet changes;
|
||||||
changes.replace(start, end, replacement);
|
changes.replace(start, end, replacement);
|
||||||
currentFile->setChangeSet(changes);
|
currentFile->setChangeSet(changes);
|
||||||
@@ -174,6 +234,7 @@ void ComponentFromObjectDef::match(const QmlJSQuickFixInterface &interface, Quic
|
|||||||
for (int i = path.size() - 1; i >= 0; --i) {
|
for (int i = path.size() - 1; i >= 0; --i) {
|
||||||
Node *node = path.at(i);
|
Node *node = path.at(i);
|
||||||
if (UiObjectDefinition *objDef = cast<UiObjectDefinition *>(node)) {
|
if (UiObjectDefinition *objDef = cast<UiObjectDefinition *>(node)) {
|
||||||
|
|
||||||
if (!interface->currentFile()->isCursorOn(objDef->qualifiedTypeNameId))
|
if (!interface->currentFile()->isCursorOn(objDef->qualifiedTypeNameId))
|
||||||
return;
|
return;
|
||||||
// check that the node is not the root node
|
// check that the node is not the root node
|
||||||
|
@@ -51,6 +51,10 @@ ComponentNameDialog::~ComponentNameDialog()
|
|||||||
|
|
||||||
bool ComponentNameDialog::go(QString *proposedName,
|
bool ComponentNameDialog::go(QString *proposedName,
|
||||||
QString *proposedPath,
|
QString *proposedPath,
|
||||||
|
const QStringList &properties,
|
||||||
|
const QStringList &sourcePreview,
|
||||||
|
const QString &oldFileName,
|
||||||
|
QStringList *result,
|
||||||
QWidget *parent)
|
QWidget *parent)
|
||||||
{
|
{
|
||||||
Q_ASSERT(proposedName);
|
Q_ASSERT(proposedName);
|
||||||
@@ -60,20 +64,81 @@ bool ComponentNameDialog::go(QString *proposedName,
|
|||||||
d.ui->componentNameEdit->setNamespacesEnabled(false);
|
d.ui->componentNameEdit->setNamespacesEnabled(false);
|
||||||
d.ui->componentNameEdit->setLowerCaseFileName(false);
|
d.ui->componentNameEdit->setLowerCaseFileName(false);
|
||||||
d.ui->componentNameEdit->setForceFirstCapitalLetter(true);
|
d.ui->componentNameEdit->setForceFirstCapitalLetter(true);
|
||||||
|
if (proposedName->isEmpty())
|
||||||
|
*proposedName = QLatin1String("MyComponent");
|
||||||
d.ui->componentNameEdit->setText(*proposedName);
|
d.ui->componentNameEdit->setText(*proposedName);
|
||||||
d.ui->pathEdit->setExpectedKind(Utils::PathChooser::ExistingDirectory);
|
d.ui->pathEdit->setExpectedKind(Utils::PathChooser::ExistingDirectory);
|
||||||
d.ui->pathEdit->setHistoryCompleter(QLatin1String("QmlJs.Component.History"));
|
d.ui->pathEdit->setHistoryCompleter(QLatin1String("QmlJs.Component.History"));
|
||||||
d.ui->pathEdit->setPath(*proposedPath);
|
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()) {
|
if (QDialog::Accepted == d.exec()) {
|
||||||
*proposedName = d.ui->componentNameEdit->text();
|
*proposedName = d.ui->componentNameEdit->text();
|
||||||
*proposedPath = d.ui->pathEdit->path();
|
*proposedPath = d.ui->pathEdit->path();
|
||||||
|
|
||||||
|
if (result)
|
||||||
|
*result = d.propertiesToKeep();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
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()
|
void ComponentNameDialog::choosePath()
|
||||||
{
|
{
|
||||||
QString dir = QFileDialog::getExistingDirectory(this, tr("Choose a path"),
|
QString dir = QFileDialog::getExistingDirectory(this, tr("Choose a path"),
|
||||||
|
@@ -40,7 +40,16 @@ public:
|
|||||||
explicit ComponentNameDialog(QWidget *parent = 0);
|
explicit ComponentNameDialog(QWidget *parent = 0);
|
||||||
~ComponentNameDialog();
|
~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:
|
public slots:
|
||||||
void choosePath();
|
void choosePath();
|
||||||
@@ -51,6 +60,7 @@ protected:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::ComponentNameDialog *ui;
|
Ui::ComponentNameDialog *ui;
|
||||||
|
QStringList m_sourcePreview;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
|
@@ -7,67 +7,50 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>495</width>
|
<width>495</width>
|
||||||
<height>138</height>
|
<height>311</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>Move Component into Separate File</string>
|
<string>Move Component into Separate File</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QGridLayout" name="gridLayout_2">
|
||||||
<property name="spacing">
|
<item row="1" column="0" colspan="3">
|
||||||
<number>0</number>
|
<widget class="QLabel" name="label">
|
||||||
</property>
|
<property name="text">
|
||||||
<item>
|
<string>Property assignments for</string>
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
|
||||||
<property name="horizontalSpacing">
|
|
||||||
<number>8</number>
|
|
||||||
</property>
|
</property>
|
||||||
<property name="verticalSpacing">
|
<property name="alignment">
|
||||||
<number>10</number>
|
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||||
</property>
|
</property>
|
||||||
<item row="2" column="1" colspan="2">
|
</widget>
|
||||||
<widget class="QLabel" name="messageLabel">
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="1">
|
|
||||||
<widget class="Utils::ClassNameValidatingLineEdit" name="componentNameEdit"/>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0">
|
|
||||||
<widget class="QLabel" name="choosePathLabel">
|
|
||||||
<property name="text">
|
|
||||||
<string>Path:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="0">
|
|
||||||
<widget class="QLabel" name="componentNameLabel">
|
|
||||||
<property name="text">
|
|
||||||
<string>Component name:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="1">
|
|
||||||
<widget class="Utils::PathChooser" name="pathEdit" native="true"/>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item row="2" column="0" colspan="2">
|
||||||
<spacer name="verticalSpacer">
|
<widget class="QListWidget" name="listWidget">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="2">
|
||||||
|
<widget class="QPlainTextEdit" name="plainTextEdit"/>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<spacer name="verticalSpacer_2">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Vertical</enum>
|
<enum>Qt::Vertical</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeHint" stdset="0">
|
<property name="sizeHint" stdset="0">
|
||||||
<size>
|
<size>
|
||||||
<width>0</width>
|
<width>20</width>
|
||||||
<height>0</height>
|
<height>40</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
</spacer>
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item row="4" column="2">
|
||||||
<widget class="QDialogButtonBox" name="buttonBox">
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Horizontal</enum>
|
||||||
@@ -77,6 +60,41 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="0" column="0" colspan="3">
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="componentNameLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Component name:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="Utils::ClassNameValidatingLineEdit" name="componentNameEdit">
|
||||||
|
<property name="placeholderText">
|
||||||
|
<string>Component Name</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QLabel" name="messageLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="choosePathLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Path:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="Utils::PathChooser" name="pathEdit" native="true"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
|
Reference in New Issue
Block a user