Commit 6c7b2d1c authored by Oswald Buddenhagen's avatar Oswald Buddenhagen

actually edit pro files instead of rewriting them from the "AST"

that way the file formatting is better preserved.
parent 93b6820e
...@@ -76,3 +76,4 @@ tests/manual/cplusplus/cplusplus0 ...@@ -76,3 +76,4 @@ tests/manual/cplusplus/cplusplus0
tests/auto/qml/qmldesigner/bauhaustests/tst_bauhaus tests/auto/qml/qmldesigner/bauhaustests/tst_bauhaus
tests/auto/qml/qmldesigner/coretests/tst_qmldesigner_core tests/auto/qml/qmldesigner/coretests/tst_qmldesigner_core
tests/auto/qml/qmldesigner/propertyeditortests/tst_propertyeditor tests/auto/qml/qmldesigner/propertyeditortests/tst_propertyeditor
tests/auto/profilewriter/tst_profilewriter
...@@ -606,23 +606,6 @@ bool Qt4PriFileNode::saveModifiedEditors(const QString &path) ...@@ -606,23 +606,6 @@ bool Qt4PriFileNode::saveModifiedEditors(const QString &path)
return true; return true;
} }
static void findProVariables(ProBlock *block, const QStringList &vars,
QList<ProVariable *> *proVars)
{
foreach (ProItem *item, block->items()) {
if (item->kind() == ProItem::BlockKind) {
ProBlock *subBlock = static_cast<ProBlock *>(item);
if (subBlock->blockKind() == ProBlock::VariableKind) {
ProVariable *proVar = static_cast<ProVariable*>(subBlock);
if (vars.contains(proVar->variable()))
*proVars << proVar;
} else {
findProVariables(subBlock, vars, proVars);
}
}
}
}
void Qt4PriFileNode::changeFiles(const FileType fileType, void Qt4PriFileNode::changeFiles(const FileType fileType,
const QStringList &filePaths, const QStringList &filePaths,
QStringList *notChanged, QStringList *notChanged,
...@@ -637,101 +620,67 @@ void Qt4PriFileNode::changeFiles(const FileType fileType, ...@@ -637,101 +620,67 @@ void Qt4PriFileNode::changeFiles(const FileType fileType,
if (!saveModifiedEditors(m_projectFilePath)) if (!saveModifiedEditors(m_projectFilePath))
return; return;
ProFileReader *reader = m_project->createProFileReader(m_qt4ProFileNode); QStringList lines;
ProFile *includeFile = reader->parsedProFile(m_projectFilePath); ProFile *includeFile;
m_project->destroyProFileReader(reader); {
if (!includeFile) { QString contents;
m_project->proFileParseError(tr("Error while changing pro file %1.").arg(m_projectFilePath)); {
return; QFile qfile(m_projectFilePath);
if (qfile.open(QIODevice::ReadOnly | QIODevice::Text)) {
contents = QString::fromLatin1(qfile.readAll()); // yes, really latin1
qfile.close();
lines = contents.split(QLatin1Char('\n'));
while (lines.last().isEmpty())
lines.removeLast();
} else {
m_project->proFileParseError(tr("Error while reading PRO file %1: %2")
.arg(m_projectFilePath, qfile.errorString()));
return;
}
}
ProFileReader *reader = m_project->createProFileReader(m_qt4ProFileNode);
includeFile = reader->parsedProFile(m_projectFilePath, contents);
m_project->destroyProFileReader(reader);
} }
const QStringList vars = varNames(fileType); const QStringList vars = varNames(fileType);
QDir priFileDir = QDir(m_qt4ProFileNode->m_projectDir); QDir priFileDir = QDir(m_qt4ProFileNode->m_projectDir);
if (change == AddToProFile) { if (change == AddToProFile) {
ProVariable *proVar = 0; ProWriter::addFiles(includeFile, &lines, priFileDir, filePaths, vars);
notChanged->clear();
// Check if variable item exists as child of root item
foreach (ProItem *item, includeFile->items()) {
if (item->kind() == ProItem::BlockKind) {
ProBlock *block = static_cast<ProBlock *>(item);
if (block->blockKind() == ProBlock::VariableKind) {
proVar = static_cast<ProVariable*>(block);
if (vars.contains(proVar->variable())
&& proVar->variableOperator() != ProVariable::RemoveOperator
&& proVar->variableOperator() != ProVariable::ReplaceOperator)
break;
proVar = 0;
}
}
}
if (!proVar) {
// Create & append new variable item
// TODO: This will always store e.g. a source file in SOURCES and not OBJECTIVE_SOURCES
proVar = new ProVariable(vars.first(), includeFile);
proVar->setVariableOperator(ProVariable::AddOperator);
includeFile->appendItem(proVar);
}
const QString &proFilePath = includeFile->fileName();
foreach (const QString &filePath, filePaths) {
if (filePath == proFilePath)
continue;
const QString &relativeFilePath = priFileDir.relativeFilePath(filePath);
proVar->appendItem(new ProValue(relativeFilePath, proVar));
notChanged->removeOne(filePath);
}
} else { // RemoveFromProFile } else { // RemoveFromProFile
QList<ProVariable *> proVars; *notChanged = ProWriter::removeFiles(includeFile, &lines, priFileDir, filePaths, vars);
findProVariables(includeFile, vars, &proVars);
QStringList relativeFilePaths;
foreach (const QString &absoluteFilePath, filePaths)
relativeFilePaths << priFileDir.relativeFilePath(absoluteFilePath);
foreach (ProVariable *proVar, proVars) {
if (proVar->variableOperator() != ProVariable::RemoveOperator
&& proVar->variableOperator() != ProVariable::ReplaceOperator) {
QList<ProItem *> values = proVar->items();
for (int i = values.count(); --i >= 0; ) {
ProItem *item = values.at(i);
if (item->kind() == ProItem::ValueKind) {
ProValue *val = static_cast<ProValue *>(item);
if (relativeFilePaths.contains(val->value())) {
notChanged->removeOne(priFileDir.absoluteFilePath(val->value()));
delete values.takeAt(i);
}
}
}
proVar->setItems(values);
}
}
} }
// save file // save file
save(includeFile); save(lines);
includeFile->deref(); includeFile->deref();
} }
void Qt4PriFileNode::save(ProFile *includeFile) void Qt4PriFileNode::save(const QStringList &lines)
{ {
Core::ICore *core = Core::ICore::instance(); Core::ICore *core = Core::ICore::instance();
Core::FileManager *fileManager = core->fileManager(); Core::FileManager *fileManager = core->fileManager();
QList<Core::IFile *> allFileHandles = fileManager->managedFiles(includeFile->fileName()); QList<Core::IFile *> allFileHandles = fileManager->managedFiles(m_projectFilePath);
Core::IFile *modifiedFileHandle = 0; Core::IFile *modifiedFileHandle = 0;
foreach (Core::IFile *file, allFileHandles) foreach (Core::IFile *file, allFileHandles)
if (file->fileName() == includeFile->fileName()) if (file->fileName() == m_projectFilePath)
modifiedFileHandle = file; modifiedFileHandle = file;
if (modifiedFileHandle) if (modifiedFileHandle)
fileManager->blockFileChange(modifiedFileHandle); fileManager->blockFileChange(modifiedFileHandle);
ProWriter pw; QFile qfile(m_projectFilePath);
const bool ok = pw.write(includeFile, includeFile->fileName()); if (qfile.open(QIODevice::WriteOnly | QIODevice::Text)) {
Q_UNUSED(ok) foreach (const QString &str, lines) {
m_project->qt4ProjectManager()->notifyChanged(includeFile->fileName()); qfile.write(str.toLatin1()); // yes, really latin1
qfile.write("\n");
}
qfile.close();
}
m_project->qt4ProjectManager()->notifyChanged(m_projectFilePath);
if (modifiedFileHandle) if (modifiedFileHandle)
fileManager->unblockFileChange(modifiedFileHandle); fileManager->unblockFileChange(modifiedFileHandle);
......
...@@ -174,7 +174,7 @@ private slots: ...@@ -174,7 +174,7 @@ private slots:
void scheduleUpdate(); void scheduleUpdate();
private: private:
void save(ProFile *includeFile); void save(const QStringList &lines);
bool priFileWritable(const QString &path); bool priFileWritable(const QString &path);
bool saveModifiedEditors(const QString &path); bool saveModifiedEditors(const QString &path);
......
This diff is collapsed.
...@@ -32,13 +32,11 @@ ...@@ -32,13 +32,11 @@
#include "namespace_global.h" #include "namespace_global.h"
#include <QtCore/QTextStream> #include <QStringList>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QDir;
class ProFile; class ProFile;
class ProValue;
class ProItem;
class ProBlock;
QT_END_NAMESPACE QT_END_NAMESPACE
namespace Qt4ProjectManager { namespace Qt4ProjectManager {
...@@ -47,26 +45,12 @@ namespace Internal { ...@@ -47,26 +45,12 @@ namespace Internal {
class ProWriter class ProWriter
{ {
public: public:
bool write(ProFile *profile, const QString &fileName); static void addFiles(ProFile *profile, QStringList *lines,
QString contents(ProFile *profile); const QDir &proFileDir, const QStringList &filePaths,
const QStringList &vars);
protected: static QStringList removeFiles(ProFile *profile, QStringList *lines,
QString fixComment(const QString &comment, const QString &indent) const; const QDir &proFileDir, const QStringList &filePaths,
void writeValue(ProValue *value, const QString &indent); const QStringList &vars);
void writeOther(ProItem *item, const QString &indent);
void writeBlock(ProBlock *block, const QString &indent);
void writeItem(ProItem *item, const QString &indent);
private:
enum ProWriteState {
NewLine = 0x01,
FirstItem = 0x02,
LastItem = 0x04
};
QTextStream m_out;
int m_writeState;
QString m_comment;
}; };
} // namespace Internal } // namespace Internal
......
/**************************************************************************
**
** 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 "profileevaluator.h"
#include "prowriter.h"
#include <QtTest/QtTest>
//#include <QtCore/QSet>
#define BASE_DIR "/some/stuff"
class tst_ProFileWriter : public QObject
{
Q_OBJECT
private slots:
void edit_data();
void edit();
void multiVar();
};
void tst_ProFileWriter::edit_data()
{
QTest::addColumn<bool>("add");
QTest::addColumn<QStringList>("files");
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("output");
struct Case {
bool add;
const char *title;
const char * const *files;
const char *input;
const char *output;
};
static const char *f_foo[] = { "foo", 0 };
static const char *f_foo_bar[] = { "foo", "bar", 0 };
static const Case cases[] = {
// Adding entries
{
true, "add new", f_foo,
"# test file",
"# test file\n"
"\n"
"SOURCES += \\\n"
" foo"
},
{
true, "add new ignoring scoped", f_foo,
"unix:SOURCES = some files",
"unix:SOURCES = some files\n"
"\n"
"SOURCES += \\\n"
" foo"
},
{
true, "add to existing", f_foo,
"SOURCES = some files",
"SOURCES = some files \\\n"
" foo"
},
{
true, "add to existing after comment", f_foo,
"SOURCES = some files # comment",
"SOURCES = some files \\ # comment\n"
" foo"
},
{
true, "add to existing after comment line", f_foo,
"SOURCES = some \\\n"
" # comment\n"
" files",
"SOURCES = some \\\n"
" # comment\n"
" files \\\n"
" foo"
},
// Removing entries
{
false, "remove fail", f_foo,
"SOURCES = bak bar",
"SOURCES = bak bar"
},
{
false, "remove one-line middle", f_foo,
"SOURCES = bak foo bar",
"SOURCES = bak bar"
},
{
false, "remove one-line trailing", f_foo,
"SOURCES = bak bar foo",
"SOURCES = bak bar"
},
{
false, "remove multi-line single leading", f_foo,
"SOURCES = foo \\\n"
" bak \\\n"
" bar",
"SOURCES = \\\n"
" bak \\\n"
" bar"
},
{
false, "remove multi-line single middle", f_foo,
"SOURCES = bak \\\n"
" foo \\\n"
" bar",
"SOURCES = bak \\\n"
" bar"
},
{
false, "remove multi-line single trailing", f_foo,
"SOURCES = bak \\\n"
" bar \\\n"
" foo",
"SOURCES = bak \\\n"
" bar"
},
{
false, "remove multi-line single leading with comment", f_foo,
"SOURCES = foo \\ # comment\n"
" bak \\\n"
" bar",
"SOURCES = \\ # foo # comment\n"
" bak \\\n"
" bar"
},
{
false, "remove multi-line single middle with comment", f_foo,
"SOURCES = bak \\\n"
" foo \\ # comment\n"
" bar",
"SOURCES = bak \\\n"
" \\ # foo # comment\n"
" bar"
},
{
false, "remove multi-line single trailing with comment", f_foo,
"SOURCES = bak \\\n"
" bar \\\n"
" foo # comment",
"SOURCES = bak \\\n"
" bar\n"
" # foo # comment"
},
{
false, "remove multi-line single trailing after empty line", f_foo,
"SOURCES = bak \\\n"
" bar \\\n"
" \\\n"
" foo",
"SOURCES = bak \\\n"
" bar\n"
},
{
false, "remove multi-line single trailing after comment line", f_foo,
"SOURCES = bak \\\n"
" bar \\\n"
" # just a comment\n"
" foo",
"SOURCES = bak \\\n"
" bar\n"
" # just a comment"
},
{
false, "remove multi-line single trailing after empty line with comment", f_foo,
"SOURCES = bak \\\n"
" bar \\\n"
" \\ # just a comment\n"
" foo",
"SOURCES = bak \\\n"
" bar\n"
" # just a comment"
},
{
false, "remove multiple one-line middle", f_foo_bar,
"SOURCES = bak foo bar baz",
"SOURCES = bak baz"
},
{
false, "remove multiple one-line trailing", f_foo_bar,
"SOURCES = bak baz foo bar",
"SOURCES = bak baz"
},
{
false, "remove multiple one-line interleaved", f_foo_bar,
"SOURCES = bak foo baz bar",
"SOURCES = bak baz"
},
{
false, "remove multiple one-line middle with comment", f_foo_bar,
"SOURCES = bak foo bar baz # comment",
"SOURCES = bak baz # bar # foo # comment"
},
{
false, "remove multi-line multiple trailing with empty line with comment", f_foo_bar,
"SOURCES = bak \\\n"
" bar \\\n"
" \\ # just a comment\n"
" foo",
"SOURCES = bak\n"
" # just a comment"
},
};
for (uint i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
const Case *_case = &cases[i];
QStringList files;
for (const char * const *file = _case->files; *file; file++)
files << QString::fromLatin1(BASE_DIR "/") + QString::fromLatin1(*file);
QTest::newRow(_case->title)
<< _case->add
<< files
<< QString::fromLatin1(_case->input)
<< QString::fromLatin1(_case->output);
}
}
void tst_ProFileWriter::edit()
{
QFETCH(bool, add);
QFETCH(QStringList, files);
QFETCH(QString, input);
QFETCH(QString, output);
QDir baseDir(BASE_DIR);
QStringList lines = input.split(QLatin1String("\n"));
QStringList vars; vars << QLatin1String("SOURCES");
ProFileOption option;
ProFileEvaluator reader(&option);
ProFile *proFile = reader.parsedProFile(BASE_DIR "/test.pro", input);
QVERIFY(proFile);
if (add)
Qt4ProjectManager::Internal::ProWriter::addFiles(proFile, &lines, baseDir, files, vars);
else
Qt4ProjectManager::Internal::ProWriter::removeFiles(proFile, &lines, baseDir, files, vars);
QCOMPARE(lines.join(QLatin1String("\n")), output);
}
void tst_ProFileWriter::multiVar()
{
QDir baseDir(BASE_DIR);
QString input = QLatin1String(
"SOURCES = foo bar\n"
"# comment line\n"
"HEADERS = baz bak"
);
QStringList lines = input.split(QLatin1String("\n"));
QString output = QLatin1String(
"SOURCES = bar\n"
"# comment line\n"
"HEADERS = baz"
);
QStringList files; files
<< QString::fromLatin1(BASE_DIR "/foo")
<< QString::fromLatin1(BASE_DIR "/bak");
QStringList vars; vars << QLatin1String("SOURCES") << QLatin1String("HEADERS");
ProFileOption option;
ProFileEvaluator reader(&option);
ProFile *proFile = reader.parsedProFile(BASE_DIR "/test.pro", input);
QVERIFY(proFile);
Qt4ProjectManager::Internal::ProWriter::removeFiles(proFile, &lines, baseDir, files, vars);
QCOMPARE(lines.join(QLatin1String("\n")), output);
}
QTEST_MAIN(tst_ProFileWriter)
#include "main.moc"
load(qttest_p4)
include(../../../src/shared/proparser/proparser.pri)
SOURCES += main.cpp
QT -= gui
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment