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
tests/auto/qml/qmldesigner/bauhaustests/tst_bauhaus
tests/auto/qml/qmldesigner/coretests/tst_qmldesigner_core
tests/auto/qml/qmldesigner/propertyeditortests/tst_propertyeditor
tests/auto/profilewriter/tst_profilewriter
......@@ -606,23 +606,6 @@ bool Qt4PriFileNode::saveModifiedEditors(const QString &path)
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,
const QStringList &filePaths,
QStringList *notChanged,
......@@ -637,101 +620,67 @@ void Qt4PriFileNode::changeFiles(const FileType fileType,
if (!saveModifiedEditors(m_projectFilePath))
return;
ProFileReader *reader = m_project->createProFileReader(m_qt4ProFileNode);
ProFile *includeFile = reader->parsedProFile(m_projectFilePath);
m_project->destroyProFileReader(reader);
if (!includeFile) {
m_project->proFileParseError(tr("Error while changing pro file %1.").arg(m_projectFilePath));
return;
QStringList lines;
ProFile *includeFile;
{
QString contents;
{
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);
QDir priFileDir = QDir(m_qt4ProFileNode->m_projectDir);
if (change == AddToProFile) {
ProVariable *proVar = 0;
// 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);
}
ProWriter::addFiles(includeFile, &lines, priFileDir, filePaths, vars);
notChanged->clear();
} else { // RemoveFromProFile
QList<ProVariable *> proVars;
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);
}
}
*notChanged = ProWriter::removeFiles(includeFile, &lines, priFileDir, filePaths, vars);
}
// save file
save(includeFile);
save(lines);
includeFile->deref();
}
void Qt4PriFileNode::save(ProFile *includeFile)
void Qt4PriFileNode::save(const QStringList &lines)
{
Core::ICore *core = Core::ICore::instance();
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;
foreach (Core::IFile *file, allFileHandles)
if (file->fileName() == includeFile->fileName())
if (file->fileName() == m_projectFilePath)
modifiedFileHandle = file;
if (modifiedFileHandle)
fileManager->blockFileChange(modifiedFileHandle);
ProWriter pw;
const bool ok = pw.write(includeFile, includeFile->fileName());
Q_UNUSED(ok)
m_project->qt4ProjectManager()->notifyChanged(includeFile->fileName());
QFile qfile(m_projectFilePath);
if (qfile.open(QIODevice::WriteOnly | QIODevice::Text)) {
foreach (const QString &str, lines) {
qfile.write(str.toLatin1()); // yes, really latin1
qfile.write("\n");
}
qfile.close();
}
m_project->qt4ProjectManager()->notifyChanged(m_projectFilePath);
if (modifiedFileHandle)
fileManager->unblockFileChange(modifiedFileHandle);
......
......@@ -174,7 +174,7 @@ private slots:
void scheduleUpdate();
private:
void save(ProFile *includeFile);
void save(const QStringList &lines);
bool priFileWritable(const QString &path);
bool saveModifiedEditors(const QString &path);
......
This diff is collapsed.
......@@ -32,13 +32,11 @@
#include "namespace_global.h"
#include <QtCore/QTextStream>
#include <QStringList>
QT_BEGIN_NAMESPACE
class QDir;
class ProFile;
class ProValue;
class ProItem;
class ProBlock;
QT_END_NAMESPACE
namespace Qt4ProjectManager {
......@@ -47,26 +45,12 @@ namespace Internal {
class ProWriter
{
public:
bool write(ProFile *profile, const QString &fileName);
QString contents(ProFile *profile);
protected:
QString fixComment(const QString &comment, const QString &indent) const;
void writeValue(ProValue *value, const QString &indent);
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;
static void addFiles(ProFile *profile, QStringList *lines,
const QDir &proFileDir, const QStringList &filePaths,
const QStringList &vars);
static QStringList removeFiles(ProFile *profile, QStringList *lines,
const QDir &proFileDir, const QStringList &filePaths,
const QStringList &vars);
};
} // 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