Enable macro editing for the Clang indexer

Refactor much of the code from Environment* classes to NameValue* classes
to share it with the preprocessor macro settings.

Change-Id: Ica4ee817aa338230c422b30d91240d266248d226
Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
This commit is contained in:
Marco Bubke
2019-05-07 16:51:22 +02:00
parent f636f06b45
commit 4bae5de36b
84 changed files with 2990 additions and 1197 deletions

View File

@@ -87,6 +87,11 @@ add_qtc_library(Utils
mimetypes/mimeprovider.cpp mimetypes/mimeprovider_p.h
mimetypes/mimetype.cpp mimetypes/mimetype.h mimetypes/mimetype_p.h
mimetypes/mimetypeparser.cpp mimetypes/mimetypeparser_p.h
namevaluedictionary.cpp namevaluedictionary.h
namevalueitem.cpp namevalueitem.h
namevaluemodel.cpp namevaluemodel.h
namevaluesdialog.cpp namevaluesdialog.h
namevaluevalidator.cpp namevaluevalidator.h
navigationtreeview.cpp navigationtreeview.h
networkaccessmanager.cpp networkaccessmanager.h
newclasswidget.cpp newclasswidget.h newclasswidget.ui

View File

@@ -39,11 +39,12 @@ Q_GLOBAL_STATIC_WITH_ARGS(Utils::Environment, staticSystemEnvironment,
Q_GLOBAL_STATIC(QVector<Utils::EnvironmentProvider>, environmentProviders)
static QMap<QString, QString>::iterator findKey(QMap<QString, QString> &input, Utils::OsType osType,
const QString &key)
namespace Utils {
static NameValueMap::iterator findKey(NameValueMap &input, Utils::OsType osType, const QString &key)
{
const Qt::CaseSensitivity casing
= (osType == Utils::OsTypeWindows) ? Qt::CaseInsensitive : Qt::CaseSensitive;
const Qt::CaseSensitivity casing = (osType == Utils::OsTypeWindows) ? Qt::CaseInsensitive
: Qt::CaseSensitive;
for (auto it = input.begin(); it != input.end(); ++it) {
if (key.compare(it.key(), casing) == 0)
return it;
@@ -51,12 +52,12 @@ static QMap<QString, QString>::iterator findKey(QMap<QString, QString> &input, U
return input.end();
}
static QMap<QString, QString>::const_iterator findKey(const QMap<QString, QString> &input,
Utils::OsType osType,
const QString &key)
static NameValueMap::const_iterator findKey(const NameValueMap &input,
Utils::OsType osType,
const QString &key)
{
const Qt::CaseSensitivity casing
= (osType == Utils::OsTypeWindows) ? Qt::CaseInsensitive : Qt::CaseSensitive;
const Qt::CaseSensitivity casing = (osType == Utils::OsTypeWindows) ? Qt::CaseInsensitive
: Qt::CaseSensitive;
for (auto it = input.constBegin(); it != input.constEnd(); ++it) {
if (key.compare(it.key(), casing) == 0)
return it;
@@ -64,198 +65,6 @@ static QMap<QString, QString>::const_iterator findKey(const QMap<QString, QStrin
return input.constEnd();
}
namespace Utils {
enum : char
{
#ifdef Q_OS_WIN
pathSepC = ';'
#else
pathSepC = ':'
#endif
};
void EnvironmentItem::sort(QList<EnvironmentItem> *list)
{
Utils::sort(*list, &EnvironmentItem::name);
}
QList<EnvironmentItem> EnvironmentItem::fromStringList(const QStringList &list)
{
QList<EnvironmentItem> result;
for (const QString &string : list) {
int pos = string.indexOf('=', 1);
if (pos == -1)
result.append(EnvironmentItem(string, QString(), EnvironmentItem::Unset));
else
result.append(EnvironmentItem(string.left(pos), string.mid(pos + 1)));
}
return result;
}
QStringList EnvironmentItem::toStringList(const QList<EnvironmentItem> &list)
{
return Utils::transform(list, [](const EnvironmentItem &item) {
if (item.operation == EnvironmentItem::Unset)
return QString(item.name);
return QString(item.name + '=' + item.value);
});
}
QList<EnvironmentItem> EnvironmentItem::itemsFromVariantList(const QVariantList &list)
{
return Utils::transform(list, [](const QVariant &item) {
return itemFromVariantList(item.toList());
});
}
QVariantList EnvironmentItem::toVariantList(const QList<EnvironmentItem> &list)
{
return Utils::transform(list, [](const EnvironmentItem &item) {
return QVariant(toVariantList(item));
});
}
EnvironmentItem EnvironmentItem::itemFromVariantList(const QVariantList &list)
{
QTC_ASSERT(list.size() == 3, return EnvironmentItem("", ""));
QString name = list.value(0).toString();
Operation operation = Operation(list.value(1).toInt());
QString value = list.value(2).toString();
return EnvironmentItem(name, value, operation);
}
QVariantList EnvironmentItem::toVariantList(const EnvironmentItem &item)
{
return QVariantList() << item.name << item.operation << item.value;
}
static QString expand(const Environment *e, QString value)
{
int replaceCount = 0;
for (int i = 0; i < value.size(); ++i) {
if (value.at(i) == '$') {
if ((i + 1) < value.size()) {
const QChar &c = value.at(i+1);
int end = -1;
if (c == '(')
end = value.indexOf(')', i);
else if (c == '{')
end = value.indexOf('}', i);
if (end != -1) {
const QString &name = value.mid(i + 2, end - i - 2);
Environment::const_iterator it = e->constFind(name);
if (it != e->constEnd())
value.replace(i, end - i + 1, it.value());
++replaceCount;
QTC_ASSERT(replaceCount < 100, break);
}
}
}
}
return value;
}
QDebug operator<<(QDebug debug, const EnvironmentItem &i)
{
QDebugStateSaver saver(debug);
debug.noquote();
debug.nospace();
debug << "EnvironmentItem(";
switch (i.operation) {
case EnvironmentItem::Set:
debug << "set \"" << i.name << "\" to \"" << i.value << '"';
break;
case EnvironmentItem::Unset:
debug << "unset \"" << i.name << '"';
break;
case EnvironmentItem::Prepend:
debug << "prepend to \"" << i.name << "\":\"" << i.value << '"';
break;
case EnvironmentItem::Append:
debug << "append to \"" << i.name << "\":\"" << i.value << '"';
break;
}
debug << ')';
return debug;
}
void EnvironmentItem::apply(Environment *e, Operation op) const
{
switch (op) {
case Set:
e->set(name, expand(e, value));
break;
case Unset:
e->unset(name);
break;
case Prepend: {
const Environment::const_iterator it = e->constFind(name);
if (it != e->constEnd()) {
QString v = it.value();
const QChar pathSep{QLatin1Char(pathSepC)};
int sepCount = 0;
if (v.startsWith(pathSep))
++sepCount;
if (value.endsWith(pathSep))
++sepCount;
if (sepCount == 2)
v.remove(0, 1);
else if (sepCount == 0)
v.prepend(pathSep);
v.prepend(expand(e, value));
e->set(name, v);
} else {
apply(e, Set);
}
}
break;
case Append: {
const Environment::const_iterator it = e->constFind(name);
if (it != e->constEnd()) {
QString v = it.value();
const QChar pathSep{QLatin1Char(pathSepC)};
int sepCount = 0;
if (v.endsWith(pathSep))
++sepCount;
if (value.startsWith(pathSep))
++sepCount;
if (sepCount == 2)
v.chop(1);
else if (sepCount == 0)
v.append(pathSep);
v.append(expand(e, value));
e->set(name, v);
} else {
apply(e, Set);
}
}
break;
}
}
Environment::Environment(const QStringList &env, OsType osType) : m_osType(osType)
{
for (const QString &s : env) {
int i = s.indexOf('=', 1);
if (i >= 0) {
const QString key = s.left(i);
if (!key.contains('=')) {
const QString value = s.mid(i + 1);
set(key, value);
}
}
}
}
QStringList Environment::toStringList() const
{
QStringList result;
for (auto it = m_values.constBegin(); it != m_values.constEnd(); ++it)
result.append(it.key() + '=' + it.value());
return result;
}
QProcessEnvironment Environment::toProcessEnvironment() const
{
QProcessEnvironment result;
@@ -264,52 +73,6 @@ QProcessEnvironment Environment::toProcessEnvironment() const
return result;
}
void Environment::set(const QString &key, const QString &value)
{
QTC_ASSERT(!key.contains('='), return);
auto it = findKey(m_values, m_osType, key);
if (it == m_values.end())
m_values.insert(key, value);
else
it.value() = value;
}
void Environment::unset(const QString &key)
{
QTC_ASSERT(!key.contains('='), return);
auto it = findKey(m_values, m_osType, key);
if (it != m_values.end())
m_values.erase(it);
}
void Environment::appendOrSet(const QString &key, const QString &value, const QString &sep)
{
QTC_ASSERT(!key.contains('='), return);
auto it = findKey(m_values, m_osType, key);
if (it == m_values.end()) {
m_values.insert(key, value);
} else {
// Append unless it is already there
const QString toAppend = sep + value;
if (!it.value().endsWith(toAppend))
it.value().append(toAppend);
}
}
void Environment::prependOrSet(const QString&key, const QString &value, const QString &sep)
{
QTC_ASSERT(!key.contains('='), return);
auto it = findKey(m_values, m_osType, key);
if (it == m_values.end()) {
m_values.insert(key, value);
} else {
// Prepend unless it is already there
const QString toPrepend = value + sep;
if (!it.value().startsWith(toPrepend))
it.value().prepend(toPrepend);
}
}
void Environment::appendOrSetPath(const QString &value)
{
appendOrSet("PATH", QDir::toNativeSeparators(value),
@@ -322,6 +85,34 @@ void Environment::prependOrSetPath(const QString &value)
QString(OsSpecificAspects::pathListSeparator(m_osType)));
}
void Environment::appendOrSet(const QString &key, const QString &value, const QString &sep)
{
QTC_ASSERT(!key.contains('='), return );
auto it = findKey(m_values, m_osType, key);
if (it == m_values.end()) {
m_values.insert(key, value);
} else {
// Append unless it is already there
const QString toAppend = sep + value;
if (!it.value().endsWith(toAppend))
it.value().append(toAppend);
}
}
void Environment::prependOrSet(const QString &key, const QString &value, const QString &sep)
{
QTC_ASSERT(!key.contains('='), return );
auto it = findKey(m_values, m_osType, key);
if (it == m_values.end()) {
m_values.insert(key, value);
} else {
// Prepend unless it is already there
const QString toPrepend = value + sep;
if (!it.value().startsWith(toPrepend))
it.value().prepend(toPrepend);
}
}
void Environment::prependOrSetLibrarySearchPath(const QString &value)
{
switch (m_osType) {
@@ -387,11 +178,6 @@ void Environment::setupEnglishOutput(QStringList *environment)
*environment = env.toStringList();
}
void Environment::clear()
{
m_values.clear();
}
FilePath Environment::searchInDirectory(const QStringList &execs, const FilePath &directory,
QSet<FilePath> &alreadyChecked) const
{
@@ -494,122 +280,7 @@ FilePathList Environment::path() const
return Utils::transform(pathComponents, &FilePath::fromUserInput);
}
QString Environment::value(const QString &key) const
{
const auto it = findKey(m_values, m_osType, key);
return it != m_values.end() ? it.value() : QString();
}
QString Environment::key(Environment::const_iterator it) const
{
return it.key();
}
QString Environment::value(Environment::const_iterator it) const
{
return it.value();
}
Environment::const_iterator Environment::constBegin() const
{
return m_values.constBegin();
}
Environment::const_iterator Environment::constEnd() const
{
return m_values.constEnd();
}
Environment::const_iterator Environment::constFind(const QString &name) const
{
return findKey(m_values, m_osType, name);
}
int Environment::size() const
{
return m_values.size();
}
void Environment::modify(const QList<EnvironmentItem> & list)
{
Environment resultEnvironment = *this;
for (const EnvironmentItem &item : list)
item.apply(&resultEnvironment);
*this = resultEnvironment;
}
QList<EnvironmentItem> Environment::diff(const Environment &other, bool checkAppendPrepend) const
{
QMap<QString, QString>::const_iterator thisIt = constBegin();
QMap<QString, QString>::const_iterator otherIt = other.constBegin();
QList<EnvironmentItem> result;
while (thisIt != constEnd() || otherIt != other.constEnd()) {
if (thisIt == constEnd()) {
result.append(EnvironmentItem(otherIt.key(), otherIt.value()));
++otherIt;
} else if (otherIt == other.constEnd()) {
result.append(EnvironmentItem(thisIt.key(), QString(), EnvironmentItem::Unset));
++thisIt;
} else if (thisIt.key() < otherIt.key()) {
result.append(EnvironmentItem(thisIt.key(), QString(), EnvironmentItem::Unset));
++thisIt;
} else if (thisIt.key() > otherIt.key()) {
result.append(EnvironmentItem(otherIt.key(), otherIt.value()));
++otherIt;
} else {
const QString &oldValue = thisIt.value();
const QString &newValue = otherIt.value();
if (oldValue != newValue) {
if (checkAppendPrepend && newValue.startsWith(oldValue)) {
QString appended = newValue.right(newValue.size() - oldValue.size());
if (appended.startsWith(QLatin1Char(pathSepC)))
appended.remove(0, 1);
result.append(EnvironmentItem(otherIt.key(), appended,
EnvironmentItem::Append));
} else if (checkAppendPrepend && newValue.endsWith(oldValue)) {
QString prepended = newValue.left(newValue.size() - oldValue.size());
if (prepended.endsWith(QLatin1Char(pathSepC)))
prepended.chop(1);
result.append(EnvironmentItem(otherIt.key(), prepended,
EnvironmentItem::Prepend));
} else {
result.append(EnvironmentItem(otherIt.key(), newValue));
}
}
++otherIt;
++thisIt;
}
}
return result;
}
bool Environment::hasKey(const QString &key) const
{
return m_values.contains(key);
}
OsType Environment::osType() const
{
return m_osType;
}
QString Environment::userName() const
{
return value(QString::fromLatin1(m_osType == OsTypeWindows ? "USERNAME" : "USER"));
}
bool Environment::operator!=(const Environment &other) const
{
return !(*this == other);
}
bool Environment::operator==(const Environment &other) const
{
return m_osType == other.m_osType && m_values == other.m_values;
}
void Environment::modifySystemEnvironment(const QList<EnvironmentItem> &list)
void Environment::modifySystemEnvironment(const EnvironmentItems &list)
{
staticSystemEnvironment->modify(list);
}

View File

@@ -27,8 +27,9 @@
#include "fileutils.h"
#include "hostosinfo.h"
#include "namevaluedictionary.h"
#include "namevalueitem.h"
#include "optional.h"
#include "utils_global.h"
#include <QMap>
#include <QStringList>
@@ -39,71 +40,18 @@ QT_FORWARD_DECLARE_CLASS(QDebug)
QT_FORWARD_DECLARE_CLASS(QProcessEnvironment)
namespace Utils {
class Environment;
class QTCREATOR_UTILS_EXPORT EnvironmentItem
class QTCREATOR_UTILS_EXPORT Environment final : public NameValueDictionary
{
public:
enum Operation { Set, Unset, Prepend, Append };
using NameValueDictionary::NameValueDictionary;
EnvironmentItem(const QString &n, const QString &v, Operation op = Set)
: name(n), value(v), operation(op)
{}
void apply(Environment *e) const { apply(e, operation); }
QString name;
QString value;
Operation operation;
bool operator==(const EnvironmentItem &other) const
{
return operation == other.operation && name == other.name && value == other.value;
}
bool operator!=(const EnvironmentItem &other) const
{
return !(*this == other);
}
static void sort(QList<EnvironmentItem> *list);
static QList<EnvironmentItem> fromStringList(const QStringList &list);
static QStringList toStringList(const QList<EnvironmentItem> &list);
static QList<EnvironmentItem> itemsFromVariantList(const QVariantList &list);
static QVariantList toVariantList(const QList<EnvironmentItem> &list);
static EnvironmentItem itemFromVariantList(const QVariantList &list);
static QVariantList toVariantList(const EnvironmentItem &item);
private:
void apply(Environment *e, Operation op) const;
};
QTCREATOR_UTILS_EXPORT QDebug operator<<(QDebug debug, const EnvironmentItem &i);
class QTCREATOR_UTILS_EXPORT Environment
{
public:
using const_iterator = QMap<QString, QString>::const_iterator;
explicit Environment(OsType osType = HostOsInfo::hostOs()) : m_osType(osType) {}
explicit Environment(const QStringList &env, OsType osType = HostOsInfo::hostOs());
static Environment systemEnvironment();
static void setupEnglishOutput(Environment *environment);
static void setupEnglishOutput(QProcessEnvironment *environment);
static void setupEnglishOutput(QStringList *environment);
QStringList toStringList() const;
QProcessEnvironment toProcessEnvironment() const;
QString value(const QString &key) const;
void set(const QString &key, const QString &value);
void unset(const QString &key);
void modify(const QList<EnvironmentItem> &list);
/// Return the Environment changes necessary to modify this into the other environment.
QList<EnvironmentItem> diff(const Environment &other, bool checkAppendPrepend = false) const;
bool hasKey(const QString &key) const;
OsType osType() const;
QString userName() const;
void appendOrSet(const QString &key, const QString &value, const QString &sep = QString());
void prependOrSet(const QString &key, const QString &value, const QString &sep = QString());
@@ -114,16 +62,6 @@ public:
void prependOrSetLibrarySearchPath(const QString &value);
void prependOrSetLibrarySearchPaths(const QStringList &values);
void clear();
int size() const;
QString key(Environment::const_iterator it) const;
QString value(Environment::const_iterator it) const;
Environment::const_iterator constBegin() const;
Environment::const_iterator constEnd() const;
Environment::const_iterator constFind(const QString &name) const;
using PathFilter = std::function<bool(const FilePath &)>;
FilePath searchInPath(const QString &executable,
const FilePathList &additionalDirs = FilePathList(),
@@ -138,16 +76,11 @@ public:
FilePath expandVariables(const FilePath &input) const;
QStringList expandVariables(const QStringList &input) const;
bool operator!=(const Environment &other) const;
bool operator==(const Environment &other) const;
static void modifySystemEnvironment(const QList<EnvironmentItem> &list); // use with care!!!
static void modifySystemEnvironment(const EnvironmentItems &list); // use with care!!!
private:
FilePath searchInDirectory(const QStringList &execs, const FilePath &directory,
QSet<FilePath> &alreadyChecked) const;
QMap<QString, QString> m_values;
OsType m_osType;
};
class QTCREATOR_UTILS_EXPORT EnvironmentProvider

View File

@@ -35,144 +35,19 @@
namespace Utils {
namespace Internal {
static QList<EnvironmentItem> cleanUp(
const QList<EnvironmentItem> &items)
Utils::optional<EnvironmentItems> EnvironmentDialog::getEnvironmentItems(
QWidget *parent, const EnvironmentItems &initial, const QString &placeholderText, Polisher polisher)
{
QList<EnvironmentItem> uniqueItems;
QSet<QString> uniqueSet;
for (int i = items.count() - 1; i >= 0; i--) {
EnvironmentItem item = items.at(i);
if (HostOsInfo::isWindowsHost())
item.name = item.name.toUpper();
const QString &itemName = item.name;
QString emptyName = itemName;
emptyName.remove(QLatin1Char(' '));
if (!emptyName.isEmpty() && !uniqueSet.contains(itemName)) {
uniqueItems.prepend(item);
uniqueSet.insert(itemName);
}
}
return uniqueItems;
}
class EnvironmentItemsWidget : public QWidget
{
Q_OBJECT
public:
explicit EnvironmentItemsWidget(QWidget *parent = nullptr);
void setEnvironmentItems(const QList<EnvironmentItem> &items);
QList<EnvironmentItem> environmentItems() const;
void setPlaceholderText(const QString &text);
private:
QPlainTextEdit *m_editor;
};
EnvironmentItemsWidget::EnvironmentItemsWidget(QWidget *parent) :
QWidget(parent)
{
m_editor = new QPlainTextEdit(this);
auto layout = new QVBoxLayout(this);
layout->setMargin(0);
layout->addWidget(m_editor);
}
void EnvironmentItemsWidget::setEnvironmentItems(const QList<EnvironmentItem> &items)
{
QList<EnvironmentItem> sortedItems = items;
EnvironmentItem::sort(&sortedItems);
const QStringList list = EnvironmentItem::toStringList(sortedItems);
m_editor->document()->setPlainText(list.join(QLatin1Char('\n')));
}
QList<EnvironmentItem> EnvironmentItemsWidget::environmentItems() const
{
const QStringList list = m_editor->document()->toPlainText().split(QLatin1String("\n"));
QList<EnvironmentItem> items = EnvironmentItem::fromStringList(list);
return cleanUp(items);
}
void EnvironmentItemsWidget::setPlaceholderText(const QString &text)
{
m_editor->setPlaceholderText(text);
}
class EnvironmentDialogPrivate
{
public:
EnvironmentItemsWidget *m_editor;
};
} // namespace Internal
EnvironmentDialog::EnvironmentDialog(QWidget *parent) :
QDialog(parent), d(new Internal::EnvironmentDialogPrivate)
{
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
resize(640, 480);
d->m_editor = new Internal::EnvironmentItemsWidget(this);
auto box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this);
connect(box, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(box, &QDialogButtonBox::rejected, this, &QDialog::reject);
auto helpLabel = new QLabel(this);
helpLabel->setText(tr("Enter one environment variable per line.\n"
"To set or change a variable, use VARIABLE=VALUE.\n"
"Existing variables can be referenced in a VALUE with ${OTHER}.\n"
"To clear a variable, put its name on a line with nothing else on it."));
auto layout = new QVBoxLayout(this);
layout->addWidget(d->m_editor);
layout->addWidget(helpLabel);
layout->addWidget(box);
setWindowTitle(tr("Edit Environment"));
}
EnvironmentDialog::~EnvironmentDialog()
{
delete d;
}
void EnvironmentDialog::setEnvironmentItems(const QList<EnvironmentItem> &items)
{
d->m_editor->setEnvironmentItems(items);
}
QList<EnvironmentItem> EnvironmentDialog::environmentItems() const
{
return d->m_editor->environmentItems();
}
void EnvironmentDialog::setPlaceholderText(const QString &text)
{
d->m_editor->setPlaceholderText(text);
}
QList<EnvironmentItem> EnvironmentDialog::getEnvironmentItems(bool *ok,
QWidget *parent,
const QList<EnvironmentItem> &initial,
const QString &placeholderText,
Polisher polisher)
{
EnvironmentDialog dlg(parent);
if (polisher)
polisher(&dlg);
dlg.setEnvironmentItems(initial);
dlg.setPlaceholderText(placeholderText);
bool result = dlg.exec() == QDialog::Accepted;
if (ok)
*ok = result;
if (result)
return dlg.environmentItems();
return QList<EnvironmentItem>();
return getNameValueItems(
parent,
initial,
placeholderText,
polisher,
tr("Edit Environment"),
tr("Enter one environment variable per line.\n"
"To set or change a variable, use VARIABLE=VALUE.\n"
"Existing variables can be referenced in a VALUE with ${OTHER}.\n"
"To clear a variable, put its name on a line with nothing else on it."));
}
} // namespace Utils
#include "environmentdialog.moc"

View File

@@ -25,36 +25,20 @@
#pragma once
#include "utils_global.h"
#include "environment.h"
#include <QDialog>
#include "namevaluesdialog.h"
#include <thread>
namespace Utils {
namespace Internal { class EnvironmentDialogPrivate; }
class QTCREATOR_UTILS_EXPORT EnvironmentDialog : public QDialog
class QTCREATOR_UTILS_EXPORT EnvironmentDialog : public NameValuesDialog
{
Q_OBJECT
public:
explicit EnvironmentDialog(QWidget *parent = nullptr);
~EnvironmentDialog() override;
void setEnvironmentItems(const QList<EnvironmentItem> &items);
QList<EnvironmentItem> environmentItems() const;
void setPlaceholderText(const QString &text);
using Polisher = std::function<void(QWidget*)>;
static QList<EnvironmentItem> getEnvironmentItems(bool *ok,
QWidget *parent = nullptr,
const QList<EnvironmentItem> &initial = QList<EnvironmentItem>(),
const QString &placeholderText = QString(),
Polisher polish = Polisher());
private:
Internal::EnvironmentDialogPrivate *d;
static Utils::optional<EnvironmentItems> getEnvironmentItems(QWidget *parent = nullptr,
const EnvironmentItems &initial = {},
const QString &placeholderText = {},
Polisher polish = {});
};
} // namespace Utils

View File

@@ -0,0 +1,51 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <QtGlobal>
QT_BEGIN_NAMESPACE
template<typename Type>
class QList;
class QTreeView;
QT_END_NAMESPACE
namespace Utils {
class NameValueDictionary;
class NameValueItem;
using NameValueItems = QVector<NameValueItem>;
class Environment;
using EnvironmentItem = NameValueItem;
using EnvironmentItems = NameValueItems;
class PreprocessorMacroDictionary;
using PreprocessorMacroItem = NameValueItem;
using PreprocessorMacroItems = NameValueItems;
class NameValueModel;
class EnvironmentModel;
} // namespace Utils

View File

@@ -33,360 +33,12 @@
#include <QFont>
namespace Utils {
namespace Internal {
class EnvironmentModelPrivate
const Environment &EnvironmentModel::baseEnvironment() const
{
public:
void updateResultEnvironment()
{
m_resultEnvironment = m_baseEnvironment;
m_resultEnvironment.modify(m_items);
// Add removed variables again and mark them as "<UNSET>" so
// that the user can actually see those removals:
foreach (const EnvironmentItem &item, m_items) {
if (item.operation == EnvironmentItem::Unset)
m_resultEnvironment.set(item.name, EnvironmentModel::tr("<UNSET>"));
}
}
int findInChanges(const QString &name) const
{
for (int i=0; i<m_items.size(); ++i)
if (m_items.at(i).name == name)
return i;
return -1;
}
int findInResultInsertPosition(const QString &name) const
{
Environment::const_iterator it;
int i = 0;
for (it = m_resultEnvironment.constBegin(); it != m_resultEnvironment.constEnd(); ++it, ++i)
if (m_resultEnvironment.key(it) > name)
return i;
return m_resultEnvironment.size();
}
int findInResult(const QString &name) const
{
Environment::const_iterator it;
int i = 0;
for (it = m_resultEnvironment.constBegin(); it != m_resultEnvironment.constEnd(); ++it, ++i)
if (m_resultEnvironment.key(it) == name)
return i;
return -1;
}
Environment m_baseEnvironment;
Environment m_resultEnvironment;
QList<EnvironmentItem> m_items;
};
} // namespace Internal
EnvironmentModel::EnvironmentModel(QObject *parent) :
QAbstractTableModel(parent),
d(new Internal::EnvironmentModelPrivate)
{ }
EnvironmentModel::~EnvironmentModel()
{
delete d;
return static_cast<const Environment &>(baseNameValueDictionary());
}
QString EnvironmentModel::indexToVariable(const QModelIndex &index) const
{
return d->m_resultEnvironment.key(d->m_resultEnvironment.constBegin() + index.row());
}
void EnvironmentModel::setBaseEnvironment(const Environment &env)
{
if (d->m_baseEnvironment == env)
return;
beginResetModel();
d->m_baseEnvironment = env;
d->updateResultEnvironment();
endResetModel();
setBaseNameValueDictionary(env);
}
int EnvironmentModel::rowCount(const QModelIndex &parent) const
{
if (parent.isValid())
return 0;
return d->m_resultEnvironment.size();
}
int EnvironmentModel::columnCount(const QModelIndex &parent) const
{
if (parent.isValid())
return 0;
return 2;
}
bool EnvironmentModel::changes(const QString &name) const
{
return d->findInChanges(name) >= 0;
}
Environment EnvironmentModel::baseEnvironment() const
{
return d->m_baseEnvironment;
}
QVariant EnvironmentModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::ToolTipRole) {
if (index.column() == 0) {
return d->m_resultEnvironment.key(d->m_resultEnvironment.constBegin() + index.row());
} else if (index.column() == 1) {
// Do not return "<UNSET>" when editing a previously unset variable:
if (role == Qt::EditRole) {
int pos = d->findInChanges(indexToVariable(index));
if (pos >= 0)
return d->m_items.at(pos).value;
}
QString value = d->m_resultEnvironment.value(d->m_resultEnvironment.constBegin() + index.row());
if (role == Qt::ToolTipRole && value.length() > 80) {
// Use html to enable text wrapping
value = value.toHtmlEscaped();
value.prepend(QLatin1String("<html><body>"));
value.append(QLatin1String("</body></html>"));
}
return value;
}
}
if (role == Qt::FontRole) {
// check whether this environment variable exists in d->m_items
if (changes(d->m_resultEnvironment.key(d->m_resultEnvironment.constBegin() + index.row()))) {
QFont f;
f.setBold(true);
return QVariant(f);
}
return QFont();
}
return QVariant();
}
Qt::ItemFlags EnvironmentModel::flags(const QModelIndex &index) const
{
Q_UNUSED(index)
return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled;
}
QVariant EnvironmentModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Vertical || role != Qt::DisplayRole)
return QVariant();
return section == 0 ? tr("Variable") : tr("Value");
}
/// *****************
/// Utility functions
/// *****************
QModelIndex EnvironmentModel::variableToIndex(const QString &name) const
{
int row = d->findInResult(name);
if (row == -1)
return QModelIndex();
return index(row, 0);
}
bool EnvironmentModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (!index.isValid() || role != Qt::EditRole)
return false;
// ignore changes to already set values:
if (data(index, role) == value)
return true;
const QString oldName = data(this->index(index.row(), 0, QModelIndex())).toString();
const QString oldValue = data(this->index(index.row(), 1, QModelIndex()), Qt::EditRole).toString();
int changesPos = d->findInChanges(oldName);
if (index.column() == 0) {
//fail if a variable with the same name already exists
const QString &newName = HostOsInfo::isWindowsHost()
? value.toString().toUpper() : value.toString();
if (newName.isEmpty() || newName.contains('='))
return false;
// Does the new name exist already?
if (d->m_resultEnvironment.hasKey(newName) || newName.isEmpty())
return false;
EnvironmentItem newVariable(newName, oldValue);
if (changesPos != -1)
resetVariable(oldName); // restore the original base variable again
QModelIndex newIndex = addVariable(newVariable); // add the new variable
emit focusIndex(newIndex.sibling(newIndex.row(), 1)); // hint to focus on the value
return true;
} else if (index.column() == 1) {
// We are changing an existing value:
const QString stringValue = value.toString();
if (changesPos != -1) {
// We have already changed this value
if (d->m_baseEnvironment.hasKey(oldName) && stringValue == d->m_baseEnvironment.value(oldName)) {
// ... and now went back to the base value
d->m_items.removeAt(changesPos);
} else {
// ... and changed it again
d->m_items[changesPos].value = stringValue;
d->m_items[changesPos].operation = EnvironmentItem::Set;
}
} else {
// Add a new change item:
d->m_items.append(EnvironmentItem(oldName, stringValue));
}
d->updateResultEnvironment();
emit dataChanged(index, index);
emit userChangesChanged();
return true;
}
return false;
}
QModelIndex EnvironmentModel::addVariable()
{
//: Name when inserting a new variable
return addVariable(EnvironmentItem(tr("<VARIABLE>"),
//: Value when inserting a new variable
tr("<VALUE>")));
}
QModelIndex EnvironmentModel::addVariable(const EnvironmentItem &item)
{
// Return existing index if the name is already in the result set:
int pos = d->findInResult(item.name);
if (pos >= 0 && pos < d->m_resultEnvironment.size())
return index(pos, 0, QModelIndex());
int insertPos = d->findInResultInsertPosition(item.name);
int changePos = d->findInChanges(item.name);
if (d->m_baseEnvironment.hasKey(item.name)) {
// We previously unset this!
Q_ASSERT(changePos >= 0);
// Do not insert a line here as we listed the variable as <UNSET> before!
Q_ASSERT(d->m_items.at(changePos).name == item.name);
Q_ASSERT(d->m_items.at(changePos).operation == EnvironmentItem::Unset);
Q_ASSERT(d->m_items.at(changePos).value.isEmpty());
d->m_items[changePos] = item;
emit dataChanged(index(insertPos, 0, QModelIndex()), index(insertPos, 1, QModelIndex()));
} else {
// We add something that is not in the base environment
// Insert a new line!
beginInsertRows(QModelIndex(), insertPos, insertPos);
Q_ASSERT(changePos < 0);
d->m_items.append(item);
d->updateResultEnvironment();
endInsertRows();
}
emit userChangesChanged();
return index(insertPos, 0, QModelIndex());
}
void EnvironmentModel::resetVariable(const QString &name)
{
int rowInChanges = d->findInChanges(name);
if (rowInChanges < 0)
return;
int rowInResult = d->findInResult(name);
if (rowInResult < 0)
return;
if (d->m_baseEnvironment.hasKey(name)) {
d->m_items.removeAt(rowInChanges);
d->updateResultEnvironment();
emit dataChanged(index(rowInResult, 0, QModelIndex()), index(rowInResult, 1, QModelIndex()));
emit userChangesChanged();
} else {
// Remove the line completely!
beginRemoveRows(QModelIndex(), rowInResult, rowInResult);
d->m_items.removeAt(rowInChanges);
d->updateResultEnvironment();
endRemoveRows();
emit userChangesChanged();
}
}
void EnvironmentModel::unsetVariable(const QString &name)
{
// This does not change the number of rows as we will display a <UNSET>
// in place of the original variable!
int row = d->findInResult(name);
if (row < 0)
return;
// look in d->m_items for the variable
int pos = d->findInChanges(name);
if (pos != -1) {
d->m_items[pos].operation = EnvironmentItem::Unset;
d->m_items[pos].value.clear();
d->updateResultEnvironment();
emit dataChanged(index(row, 0, QModelIndex()), index(row, 1, QModelIndex()));
emit userChangesChanged();
return;
}
d->m_items.append(EnvironmentItem(name, QString(), EnvironmentItem::Unset));
d->updateResultEnvironment();
emit dataChanged(index(row, 0, QModelIndex()), index(row, 1, QModelIndex()));
emit userChangesChanged();
}
bool EnvironmentModel::canUnset(const QString &name)
{
int pos = d->findInChanges(name);
if (pos != -1)
return d->m_items.at(pos).operation == EnvironmentItem::Unset;
else
return false;
}
bool EnvironmentModel::canReset(const QString &name)
{
return d->m_baseEnvironment.hasKey(name);
}
QList<EnvironmentItem> EnvironmentModel::userChanges() const
{
return d->m_items;
}
void EnvironmentModel::setUserChanges(const QList<EnvironmentItem> &list)
{
QList<EnvironmentItem> filtered = Utils::filtered(list, [](const EnvironmentItem &i) {
return i.name != "export " && !i.name.contains('=');
});
// We assume nobody is reordering the items here.
if (filtered == d->m_items)
return;
beginResetModel();
d->m_items = filtered;
for (EnvironmentItem &item : d->m_items) {
QString &name = item.name;
name = name.trimmed();
if (name.startsWith("export "))
name = name.mid(7).trimmed();
if (d->m_baseEnvironment.osType() == OsTypeWindows) {
// Environment variable names are case-insensitive under windows, but we still
// want to preserve the case of pre-existing variables.
auto it = d->m_baseEnvironment.constFind(name);
if (it != d->m_baseEnvironment.constEnd())
name = d->m_baseEnvironment.key(it);
}
}
d->updateResultEnvironment();
endResetModel();
emit userChangesChanged();
}
} // namespace Utils

View File

@@ -25,55 +25,17 @@
#pragma once
#include "utils_global.h"
#include <QAbstractTableModel>
#include "namevaluemodel.h"
namespace Utils {
class Environment;
class EnvironmentItem;
namespace Internal { class EnvironmentModelPrivate; }
class QTCREATOR_UTILS_EXPORT EnvironmentModel : public QAbstractTableModel
class QTCREATOR_UTILS_EXPORT EnvironmentModel : public NameValueModel
{
Q_OBJECT
public:
explicit EnvironmentModel(QObject *parent = nullptr);
~EnvironmentModel() override;
int rowCount(const QModelIndex &parent) const override;
int columnCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const override;
QModelIndex addVariable();
QModelIndex addVariable(const EnvironmentItem &item);
void resetVariable(const QString &name);
void unsetVariable(const QString &name);
bool canUnset(const QString &name);
bool canReset(const QString &name);
QString indexToVariable(const QModelIndex &index) const;
QModelIndex variableToIndex(const QString &name) const;
bool changes(const QString &key) const;
Environment baseEnvironment() const;
const Environment &baseEnvironment() const;
void setBaseEnvironment(const Environment &env);
QList<EnvironmentItem> userChanges() const;
void setUserChanges(const QList<EnvironmentItem> &list);
signals:
void userChangesChanged();
/// Hint to the view where it should make sense to focus on next
// This is a hack since there is no way for a model to suggest
// the next interesting place to focus on to the view.
void focusIndex(const QModelIndex &index);
private:
Internal::EnvironmentModelPrivate *d;
};
} // namespace Utils

View File

@@ -0,0 +1,282 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "algorithm.h"
#include "namevaluedictionary.h"
#include "qtcassert.h"
#include <QDir>
namespace Utils {
namespace {
NameValueMap::iterator findKey(NameValueMap &input, Utils::OsType osType, const QString &key)
{
const Qt::CaseSensitivity casing = (osType == Utils::OsTypeWindows) ? Qt::CaseInsensitive
: Qt::CaseSensitive;
for (auto it = input.begin(); it != input.end(); ++it) {
if (key.compare(it.key(), casing) == 0)
return it;
}
return input.end();
}
NameValueMap::const_iterator findKey(const NameValueMap &input, Utils::OsType osType, const QString &key)
{
const Qt::CaseSensitivity casing = (osType == Utils::OsTypeWindows) ? Qt::CaseInsensitive
: Qt::CaseSensitive;
for (auto it = input.constBegin(); it != input.constEnd(); ++it) {
if (key.compare(it.key(), casing) == 0)
return it;
}
return input.constEnd();
}
} // namespace
NameValueDictionary::NameValueDictionary(const QStringList &env, OsType osType)
: m_osType(osType)
{
for (const QString &s : env) {
int i = s.indexOf('=', 1);
if (i >= 0) {
const QString key = s.left(i);
if (!key.contains('=')) {
const QString value = s.mid(i + 1);
set(key, value);
}
}
}
}
NameValueDictionary::NameValueDictionary(const NameValuePairs &nameValues)
{
for (const auto &nameValue : nameValues)
set(nameValue.first, nameValue.second);
}
QStringList NameValueDictionary::toStringList() const
{
QStringList result;
for (auto it = m_values.constBegin(); it != m_values.constEnd(); ++it)
result.append(it.key() + '=' + it.value());
return result;
}
void NameValueDictionary::set(const QString &key, const QString &value)
{
QTC_ASSERT(!key.contains('='), return );
auto it = findKey(m_values, m_osType, key);
if (it == m_values.end())
m_values.insert(key, value);
else
it.value() = value;
}
void NameValueDictionary::unset(const QString &key)
{
QTC_ASSERT(!key.contains('='), return );
auto it = findKey(m_values, m_osType, key);
if (it != m_values.end())
m_values.erase(it);
}
void NameValueDictionary::clear()
{
m_values.clear();
}
QString NameValueDictionary::value(const QString &key) const
{
const auto it = findKey(m_values, m_osType, key);
return it != m_values.end() ? it.value() : QString();
}
NameValueDictionary::const_iterator NameValueDictionary::constFind(const QString &name) const
{
return findKey(m_values, m_osType, name);
}
int NameValueDictionary::size() const
{
return m_values.size();
}
void NameValueDictionary::modify(const NameValueItems &items)
{
NameValueDictionary resultKeyValueDictionary = *this;
for (const NameValueItem &item : items)
item.apply(&resultKeyValueDictionary);
*this = resultKeyValueDictionary;
}
enum : char {
#ifdef Q_OS_WIN
pathSepC = ';'
#else
pathSepC = ':'
#endif
};
NameValueItems NameValueDictionary::diff(const NameValueDictionary &other, bool checkAppendPrepend) const
{
NameValueMap::const_iterator thisIt = constBegin();
NameValueMap::const_iterator otherIt = other.constBegin();
NameValueItems result;
while (thisIt != constEnd() || otherIt != other.constEnd()) {
if (thisIt == constEnd()) {
result.append(NameValueItem(otherIt.key(), otherIt.value()));
++otherIt;
} else if (otherIt == other.constEnd()) {
result.append(NameValueItem(thisIt.key(), QString(), NameValueItem::Unset));
++thisIt;
} else if (thisIt.key() < otherIt.key()) {
result.append(NameValueItem(thisIt.key(), QString(), NameValueItem::Unset));
++thisIt;
} else if (thisIt.key() > otherIt.key()) {
result.append(NameValueItem(otherIt.key(), otherIt.value()));
++otherIt;
} else {
const QString &oldValue = thisIt.value();
const QString &newValue = otherIt.value();
if (oldValue != newValue) {
if (checkAppendPrepend && newValue.startsWith(oldValue)) {
QString appended = newValue.right(newValue.size() - oldValue.size());
if (appended.startsWith(QLatin1Char(pathSepC)))
appended.remove(0, 1);
result.append(NameValueItem(otherIt.key(), appended, NameValueItem::Append));
} else if (checkAppendPrepend && newValue.endsWith(oldValue)) {
QString prepended = newValue.left(newValue.size() - oldValue.size());
if (prepended.endsWith(QLatin1Char(pathSepC)))
prepended.chop(1);
result.append(NameValueItem(otherIt.key(), prepended, NameValueItem::Prepend));
} else {
result.append(NameValueItem(otherIt.key(), newValue));
}
}
++otherIt;
++thisIt;
}
}
return result;
}
bool NameValueDictionary::hasKey(const QString &key) const
{
return m_values.contains(key);
}
OsType NameValueDictionary::osType() const
{
return m_osType;
}
QString NameValueDictionary::userName() const
{
return value(QString::fromLatin1(m_osType == OsTypeWindows ? "USERNAME" : "USER"));
}
/** Expand environment variables in a string.
*
* KeyValueDictionary variables are accepted in the following forms:
* $SOMEVAR, ${SOMEVAR} on Unix and %SOMEVAR% on Windows.
* No escapes and quoting are supported.
* If a variable is not found, it is not substituted.
*/
QString NameValueDictionary::expandVariables(const QString &input) const
{
QString result = input;
if (m_osType == OsTypeWindows) {
for (int vStart = -1, i = 0; i < result.length();) {
if (result.at(i++) == '%') {
if (vStart > 0) {
const_iterator it = findKey(m_values, m_osType, result.mid(vStart, i - vStart - 1));
if (it != m_values.constEnd()) {
result.replace(vStart - 1, i - vStart + 1, *it);
i = vStart - 1 + it->length();
vStart = -1;
} else {
vStart = i;
}
} else {
vStart = i;
}
}
}
} else {
enum { BASE, OPTIONALVARIABLEBRACE, VARIABLE, BRACEDVARIABLE } state = BASE;
int vStart = -1;
for (int i = 0; i < result.length();) {
QChar c = result.at(i++);
if (state == BASE) {
if (c == '$')
state = OPTIONALVARIABLEBRACE;
} else if (state == OPTIONALVARIABLEBRACE) {
if (c == '{') {
state = BRACEDVARIABLE;
vStart = i;
} else if (c.isLetterOrNumber() || c == '_') {
state = VARIABLE;
vStart = i - 1;
} else {
state = BASE;
}
} else if (state == BRACEDVARIABLE) {
if (c == '}') {
const_iterator it = m_values.constFind(result.mid(vStart, i - 1 - vStart));
if (it != constEnd()) {
result.replace(vStart - 2, i - vStart + 2, *it);
i = vStart - 2 + it->length();
}
state = BASE;
}
} else if (state == VARIABLE) {
if (!c.isLetterOrNumber() && c != '_') {
const_iterator it = m_values.constFind(result.mid(vStart, i - vStart - 1));
if (it != constEnd()) {
result.replace(vStart - 1, i - vStart, *it);
i = vStart - 1 + it->length();
}
state = BASE;
}
}
}
if (state == VARIABLE) {
const_iterator it = m_values.constFind(result.mid(vStart));
if (it != constEnd())
result.replace(vStart - 1, result.length() - vStart + 1, *it);
}
}
return result;
}
QStringList NameValueDictionary::expandVariables(const QStringList &variables) const
{
return Utils::transform(variables, [this](const QString &i) { return expandVariables(i); });
}
} // namespace Utils

View File

@@ -0,0 +1,92 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "fileutils.h"
#include "hostosinfo.h"
#include "namevalueitem.h"
namespace Utils {
using NameValuePair = std::pair<QString, QString>;
using NameValuePairs = QVector<NameValuePair>;
using NameValueMap = QMap<QString, QString>;
class QTCREATOR_UTILS_EXPORT NameValueDictionary
{
public:
using const_iterator = NameValueMap::const_iterator;
explicit NameValueDictionary(OsType osType = HostOsInfo::hostOs())
: m_osType(osType)
{}
explicit NameValueDictionary(const QStringList &env, OsType osType = HostOsInfo::hostOs());
explicit NameValueDictionary(const NameValuePairs &nameValues);
QStringList toStringList() const;
QString value(const QString &key) const;
void set(const QString &key, const QString &value);
void unset(const QString &key);
void modify(const NameValueItems &items);
/// Return the KeyValueDictionary changes necessary to modify this into the other environment.
NameValueItems diff(const NameValueDictionary &other, bool checkAppendPrepend = false) const;
bool hasKey(const QString &key) const;
OsType osType() const;
QString userName() const;
void clear();
int size() const;
QString key(NameValueDictionary::const_iterator it) const { return it.key(); }
QString value(NameValueDictionary::const_iterator it) const { return it.value(); }
NameValueDictionary::const_iterator constBegin() const { return m_values.constBegin(); }
NameValueDictionary::const_iterator constEnd() const { return m_values.constEnd(); }
NameValueDictionary::const_iterator constFind(const QString &name) const;
QString expandVariables(const QString &input) const;
QStringList expandVariables(const QStringList &input) const;
friend bool operator!=(const NameValueDictionary &first, const NameValueDictionary &second)
{
return !(first == second);
}
friend bool operator==(const NameValueDictionary &first, const NameValueDictionary &second)
{
return first.m_osType == second.m_osType && first.m_values == second.m_values;
}
protected:
NameValueMap m_values;
OsType m_osType;
};
} // namespace Utils

View File

@@ -0,0 +1,199 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "namevalueitem.h"
#include "algorithm.h"
#include "namevaluedictionary.h"
#include "qtcassert.h"
#include <QDebug>
namespace Utils {
void NameValueItem::sort(NameValueItems *list)
{
Utils::sort(*list, &NameValueItem::name);
}
NameValueItems NameValueItem::fromStringList(const QStringList &list)
{
NameValueItems result;
for (const QString &string : list) {
int pos = string.indexOf('=', 1);
if (pos == -1)
result.append(NameValueItem(string, QString(), NameValueItem::Unset));
else
result.append(NameValueItem(string.left(pos), string.mid(pos + 1)));
}
return result;
}
QStringList NameValueItem::toStringList(const NameValueItems &list)
{
return Utils::transform<QStringList>(list, [](const NameValueItem &item) {
if (item.operation == NameValueItem::Unset)
return QString(item.name);
return QString(item.name + '=' + item.value);
});
}
NameValueItems NameValueItem::itemsFromVariantList(const QVariantList &list)
{
return Utils::transform<NameValueItems>(list, [](const QVariant &item) {
return itemFromVariantList(item.toList());
});
}
QVariantList NameValueItem::toVariantList(const NameValueItems &list)
{
return Utils::transform<QVariantList>(list, [](const NameValueItem &item) {
return QVariant(toVariantList(item));
});
}
NameValueItem NameValueItem::itemFromVariantList(const QVariantList &list)
{
QTC_ASSERT(list.size() == 3, return NameValueItem("", ""));
QString key = list.value(0).toString();
Operation operation = Operation(list.value(1).toInt());
QString value = list.value(2).toString();
return NameValueItem(key, value, operation);
}
QVariantList NameValueItem::toVariantList(const NameValueItem &item)
{
return QVariantList() << item.name << item.operation << item.value;
}
static QString expand(const NameValueDictionary *dictionary, QString value)
{
int replaceCount = 0;
for (int i = 0; i < value.size(); ++i) {
if (value.at(i) == '$') {
if ((i + 1) < value.size()) {
const QChar &c = value.at(i + 1);
int end = -1;
if (c == '(')
end = value.indexOf(')', i);
else if (c == '{')
end = value.indexOf('}', i);
if (end != -1) {
const QString &key = value.mid(i + 2, end - i - 2);
NameValueDictionary::const_iterator it = dictionary->constFind(key);
if (it != dictionary->constEnd())
value.replace(i, end - i + 1, it.value());
++replaceCount;
QTC_ASSERT(replaceCount < 100, break);
}
}
}
}
return value;
}
enum : char {
#ifdef Q_OS_WIN
pathSepC = ';'
#else
pathSepC = ':'
#endif
};
void NameValueItem::apply(NameValueDictionary *dictionary, Operation op) const
{
switch (op) {
case Set:
dictionary->set(name, expand(dictionary, value));
break;
case Unset:
dictionary->unset(name);
break;
case Prepend: {
const NameValueDictionary::const_iterator it = dictionary->constFind(name);
if (it != dictionary->constEnd()) {
QString v = it.value();
const QChar pathSep{QLatin1Char(pathSepC)};
int sepCount = 0;
if (v.startsWith(pathSep))
++sepCount;
if (value.endsWith(pathSep))
++sepCount;
if (sepCount == 2)
v.remove(0, 1);
else if (sepCount == 0)
v.prepend(pathSep);
v.prepend(expand(dictionary, value));
dictionary->set(name, v);
} else {
apply(dictionary, Set);
}
} break;
case Append: {
const NameValueDictionary::const_iterator it = dictionary->constFind(name);
if (it != dictionary->constEnd()) {
QString v = it.value();
const QChar pathSep{QLatin1Char(pathSepC)};
int sepCount = 0;
if (v.endsWith(pathSep))
++sepCount;
if (value.startsWith(pathSep))
++sepCount;
if (sepCount == 2)
v.chop(1);
else if (sepCount == 0)
v.append(pathSep);
v.append(expand(dictionary, value));
dictionary->set(name, v);
} else {
apply(dictionary, Set);
}
} break;
}
}
QDebug operator<<(QDebug debug, const NameValueItem &i)
{
QDebugStateSaver saver(debug);
debug.noquote();
debug.nospace();
debug << "KeyValueItem(";
switch (i.operation) {
case NameValueItem::Set:
debug << "set \"" << i.name << "\" to \"" << i.value << '"';
break;
case NameValueItem::Unset:
debug << "unset \"" << i.name << '"';
break;
case NameValueItem::Prepend:
debug << "prepend to \"" << i.name << "\":\"" << i.value << '"';
break;
case NameValueItem::Append:
debug << "append to \"" << i.name << "\":\"" << i.value << '"';
break;
}
debug << ')';
return debug;
}
} // namespace Utils

View File

@@ -0,0 +1,80 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "environmentfwd.h"
#include "utils_global.h"
#include <QStringList>
#include <QVariantList>
#include <QVector>
namespace Utils {
class QTCREATOR_UTILS_EXPORT NameValueItem
{
public:
enum Operation : char { Set, Unset, Prepend, Append };
NameValueItem() = default;
NameValueItem(const QString &key, const QString &value, Operation operation = Set)
: name(key)
, value(value)
, operation(operation)
{}
void apply(NameValueDictionary *dictionary) const { apply(dictionary, operation); }
static void sort(NameValueItems *list);
static NameValueItems fromStringList(const QStringList &list);
static QStringList toStringList(const NameValueItems &list);
static NameValueItems itemsFromVariantList(const QVariantList &list);
static QVariantList toVariantList(const NameValueItems &list);
static NameValueItem itemFromVariantList(const QVariantList &list);
static QVariantList toVariantList(const NameValueItem &item);
friend bool operator==(const NameValueItem &first, const NameValueItem &second)
{
return first.operation == second.operation && first.name == second.name
&& first.value == second.value;
}
friend bool operator!=(const NameValueItem &first, const NameValueItem &second)
{
return !(first == second);
}
public:
QString name;
QString value;
Operation operation = Unset;
private:
void apply(NameValueDictionary *dictionary, Operation op) const;
};
QTCREATOR_UTILS_EXPORT QDebug operator<<(QDebug debug, const NameValueItem &i);
} // namespace Utils

View File

@@ -0,0 +1,397 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "namevaluemodel.h"
#include <utils/algorithm.h>
#include <utils/hostosinfo.h>
#include <utils/namevaluedictionary.h>
#include <QFont>
#include <QString>
namespace Utils {
namespace Internal {
class NameValueModelPrivate
{
public:
void updateResultNameValueDictionary()
{
m_resultNameValueDictionary = m_baseNameValueDictionary;
m_resultNameValueDictionary.modify(m_items);
// Add removed variables again and mark them as "<UNSET>" so
// that the user can actually see those removals:
foreach (const NameValueItem &item, m_items) {
if (item.operation == NameValueItem::Unset)
m_resultNameValueDictionary.set(item.name, NameValueModel::tr("<UNSET>"));
}
}
int findInChanges(const QString &name) const
{
for (int i = 0; i < m_items.size(); ++i)
if (m_items.at(i).name == name)
return i;
return -1;
}
int findInResultInsertPosition(const QString &name) const
{
NameValueDictionary::const_iterator it;
int i = 0;
for (it = m_resultNameValueDictionary.constBegin();
it != m_resultNameValueDictionary.constEnd();
++it, ++i)
if (m_resultNameValueDictionary.key(it) > name)
return i;
return m_resultNameValueDictionary.size();
}
int findInResult(const QString &name) const
{
NameValueDictionary::const_iterator it;
int i = 0;
for (it = m_resultNameValueDictionary.constBegin();
it != m_resultNameValueDictionary.constEnd();
++it, ++i)
if (m_resultNameValueDictionary.key(it) == name)
return i;
return -1;
}
NameValueDictionary m_baseNameValueDictionary;
NameValueDictionary m_resultNameValueDictionary;
NameValueItems m_items;
};
} // namespace Internal
NameValueModel::NameValueModel(QObject *parent)
: QAbstractTableModel(parent)
, d(std::make_unique<Internal::NameValueModelPrivate>())
{}
NameValueModel::~NameValueModel() = default;
QString NameValueModel::indexToVariable(const QModelIndex &index) const
{
return d->m_resultNameValueDictionary.key(d->m_resultNameValueDictionary.constBegin()
+ index.row());
}
void NameValueModel::setBaseNameValueDictionary(const NameValueDictionary &dictionary)
{
if (d->m_baseNameValueDictionary == dictionary)
return;
beginResetModel();
d->m_baseNameValueDictionary = dictionary;
d->updateResultNameValueDictionary();
endResetModel();
}
int NameValueModel::rowCount(const QModelIndex &parent) const
{
if (parent.isValid())
return 0;
return d->m_resultNameValueDictionary.size();
}
int NameValueModel::columnCount(const QModelIndex &parent) const
{
if (parent.isValid())
return 0;
return 2;
}
bool NameValueModel::changes(const QString &name) const
{
return d->findInChanges(name) >= 0;
}
const NameValueDictionary &NameValueModel::baseNameValueDictionary() const
{
return d->m_baseNameValueDictionary;
}
QVariant NameValueModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::ToolTipRole) {
if (index.column() == 0) {
return d->m_resultNameValueDictionary.key(d->m_resultNameValueDictionary.constBegin()
+ index.row());
} else if (index.column() == 1) {
// Do not return "<UNSET>" when editing a previously unset variable:
if (role == Qt::EditRole) {
int pos = d->findInChanges(indexToVariable(index));
if (pos >= 0)
return d->m_items.at(pos).value;
}
QString value = d->m_resultNameValueDictionary.value(
d->m_resultNameValueDictionary.constBegin() + index.row());
if (role == Qt::ToolTipRole && value.length() > 80) {
// Use html to enable text wrapping
value = value.toHtmlEscaped();
value.prepend(QLatin1String("<html><body>"));
value.append(QLatin1String("</body></html>"));
}
return value;
}
}
if (role == Qt::FontRole) {
// check whether this name value item variable exists in d->m_items
if (changes(d->m_resultNameValueDictionary.key(d->m_resultNameValueDictionary.constBegin()
+ index.row()))) {
QFont f;
f.setBold(true);
return QVariant(f);
}
return QFont();
}
return QVariant();
}
Qt::ItemFlags NameValueModel::flags(const QModelIndex &index) const
{
Q_UNUSED(index)
return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled;
}
QVariant NameValueModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Vertical || role != Qt::DisplayRole)
return QVariant();
return section == 0 ? tr("Variable") : tr("Value");
}
/// *****************
/// Utility functions
/// *****************
QModelIndex NameValueModel::variableToIndex(const QString &name) const
{
int row = d->findInResult(name);
if (row == -1)
return QModelIndex();
return index(row, 0);
}
bool NameValueModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (!index.isValid() || role != Qt::EditRole)
return false;
// ignore changes to already set values:
if (data(index, role) == value)
return true;
const QString oldName = data(this->index(index.row(), 0, QModelIndex())).toString();
const QString oldValue = data(this->index(index.row(), 1, QModelIndex()), Qt::EditRole).toString();
int changesPos = d->findInChanges(oldName);
if (index.column() == 0) {
//fail if a variable with the same name already exists
const QString &newName = HostOsInfo::isWindowsHost() ? value.toString().toUpper()
: value.toString();
if (newName.isEmpty() || newName.contains('='))
return false;
// Does the new name exist already?
if (d->m_resultNameValueDictionary.hasKey(newName) || newName.isEmpty())
return false;
NameValueItem newVariable(newName, oldValue);
if (changesPos != -1)
resetVariable(oldName); // restore the original base variable again
QModelIndex newIndex = addVariable(newVariable); // add the new variable
emit focusIndex(newIndex.sibling(newIndex.row(), 1)); // hint to focus on the value
return true;
} else if (index.column() == 1) {
// We are changing an existing value:
const QString stringValue = value.toString();
if (changesPos != -1) {
// We have already changed this value
if (d->m_baseNameValueDictionary.hasKey(oldName)
&& stringValue == d->m_baseNameValueDictionary.value(oldName)) {
// ... and now went back to the base value
d->m_items.removeAt(changesPos);
} else {
// ... and changed it again
d->m_items[changesPos].value = stringValue;
d->m_items[changesPos].operation = NameValueItem::Set;
}
} else {
// Add a new change item:
d->m_items.append(NameValueItem(oldName, stringValue));
}
d->updateResultNameValueDictionary();
emit dataChanged(index, index);
emit userChangesChanged();
return true;
}
return false;
}
QModelIndex NameValueModel::addVariable()
{
//: Name when inserting a new variable
return addVariable(NameValueItem(tr("<VARIABLE>"),
//: Value when inserting a new variable
tr("<VALUE>")));
}
QModelIndex NameValueModel::addVariable(const NameValueItem &item)
{
// Return existing index if the name is already in the result set:
int pos = d->findInResult(item.name);
if (pos >= 0 && pos < d->m_resultNameValueDictionary.size())
return index(pos, 0, QModelIndex());
int insertPos = d->findInResultInsertPosition(item.name);
int changePos = d->findInChanges(item.name);
if (d->m_baseNameValueDictionary.hasKey(item.name)) {
// We previously unset this!
Q_ASSERT(changePos >= 0);
// Do not insert a line here as we listed the variable as <UNSET> before!
Q_ASSERT(d->m_items.at(changePos).name == item.name);
Q_ASSERT(d->m_items.at(changePos).operation == NameValueItem::Unset);
Q_ASSERT(d->m_items.at(changePos).value.isEmpty());
d->m_items[changePos] = item;
emit dataChanged(index(insertPos, 0, QModelIndex()), index(insertPos, 1, QModelIndex()));
} else {
// We add something that is not in the base dictionary
// Insert a new line!
beginInsertRows(QModelIndex(), insertPos, insertPos);
Q_ASSERT(changePos < 0);
d->m_items.append(item);
d->updateResultNameValueDictionary();
endInsertRows();
}
emit userChangesChanged();
return index(insertPos, 0, QModelIndex());
}
void NameValueModel::resetVariable(const QString &name)
{
int rowInChanges = d->findInChanges(name);
if (rowInChanges < 0)
return;
int rowInResult = d->findInResult(name);
if (rowInResult < 0)
return;
if (d->m_baseNameValueDictionary.hasKey(name)) {
d->m_items.removeAt(rowInChanges);
d->updateResultNameValueDictionary();
emit dataChanged(index(rowInResult, 0, QModelIndex()), index(rowInResult, 1, QModelIndex()));
emit userChangesChanged();
} else {
// Remove the line completely!
beginRemoveRows(QModelIndex(), rowInResult, rowInResult);
d->m_items.removeAt(rowInChanges);
d->updateResultNameValueDictionary();
endRemoveRows();
emit userChangesChanged();
}
}
void NameValueModel::unsetVariable(const QString &name)
{
// This does not change the number of rows as we will display a <UNSET>
// in place of the original variable!
int row = d->findInResult(name);
if (row < 0)
return;
// look in d->m_items for the variable
int pos = d->findInChanges(name);
if (pos != -1) {
d->m_items[pos].operation = NameValueItem::Unset;
d->m_items[pos].value.clear();
d->updateResultNameValueDictionary();
emit dataChanged(index(row, 0, QModelIndex()), index(row, 1, QModelIndex()));
emit userChangesChanged();
return;
}
d->m_items.append(NameValueItem(name, QString(), NameValueItem::Unset));
d->updateResultNameValueDictionary();
emit dataChanged(index(row, 0, QModelIndex()), index(row, 1, QModelIndex()));
emit userChangesChanged();
}
bool NameValueModel::canUnset(const QString &name)
{
int pos = d->findInChanges(name);
if (pos != -1)
return d->m_items.at(pos).operation == NameValueItem::Unset;
else
return false;
}
bool NameValueModel::canReset(const QString &name)
{
return d->m_baseNameValueDictionary.hasKey(name);
}
NameValueItems NameValueModel::userChanges() const
{
return d->m_items;
}
void NameValueModel::setUserChanges(const NameValueItems &items)
{
NameValueItems filtered = Utils::filtered(items, [](const NameValueItem &i) {
return i.name != "export " && !i.name.contains('=');
});
// We assume nobody is reordering the items here.
if (filtered == d->m_items)
return;
beginResetModel();
d->m_items = filtered;
for (NameValueItem &item : d->m_items) {
QString &name = item.name;
name = name.trimmed();
if (name.startsWith("export "))
name = name.mid(7).trimmed();
if (d->m_baseNameValueDictionary.osType() == OsTypeWindows) {
// NameValueDictionary variable names are case-insensitive under windows, but we still
// want to preserve the case of pre-existing variables.
auto it = d->m_baseNameValueDictionary.constFind(name);
if (it != d->m_baseNameValueDictionary.constEnd())
name = d->m_baseNameValueDictionary.key(it);
}
}
d->updateResultNameValueDictionary();
endResetModel();
emit userChangesChanged();
}
} // namespace Utils

View File

@@ -0,0 +1,83 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "environmentfwd.h"
#include "utils_global.h"
#include <QAbstractTableModel>
#include <memory>
namespace Utils {
namespace Internal {
class NameValueModelPrivate;
}
class QTCREATOR_UTILS_EXPORT NameValueModel : public QAbstractTableModel
{
Q_OBJECT
public:
explicit NameValueModel(QObject *parent = nullptr);
~NameValueModel() override;
int rowCount(const QModelIndex &parent) const override;
int columnCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
QVariant headerData(int section,
Qt::Orientation orientation,
int role = Qt::DisplayRole) const override;
QModelIndex addVariable();
QModelIndex addVariable(const NameValueItem &item);
void resetVariable(const QString &name);
void unsetVariable(const QString &name);
bool canUnset(const QString &name);
bool canReset(const QString &name);
QString indexToVariable(const QModelIndex &index) const;
QModelIndex variableToIndex(const QString &name) const;
bool changes(const QString &key) const;
const NameValueDictionary &baseNameValueDictionary() const;
void setBaseNameValueDictionary(const NameValueDictionary &dictionary);
NameValueItems userChanges() const;
void setUserChanges(const NameValueItems &items);
signals:
void userChangesChanged();
/// Hint to the view where it should make sense to focus on next
// This is a hack since there is no way for a model to suggest
// the next interesting place to focus on to the view.
void focusIndex(const QModelIndex &index);
private:
std::unique_ptr<Internal::NameValueModelPrivate> d;
};
} // namespace Utils

View File

@@ -0,0 +1,147 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "namevaluesdialog.h"
#include <utils/environment.h>
#include <utils/hostosinfo.h>
#include <QDialogButtonBox>
#include <QLabel>
#include <QPlainTextEdit>
#include <QVBoxLayout>
namespace Utils {
namespace Internal {
static EnvironmentItems cleanUp(const EnvironmentItems &items)
{
EnvironmentItems uniqueItems;
QSet<QString> uniqueSet;
for (int i = items.count() - 1; i >= 0; i--) {
EnvironmentItem item = items.at(i);
if (HostOsInfo::isWindowsHost())
item.name = item.name.toUpper();
const QString &itemName = item.name;
QString emptyName = itemName;
emptyName.remove(QLatin1Char(' '));
if (!emptyName.isEmpty() && !uniqueSet.contains(itemName)) {
uniqueItems.prepend(item);
uniqueSet.insert(itemName);
}
}
return uniqueItems;
}
NameValueItemsWidget::NameValueItemsWidget(QWidget *parent)
: QWidget(parent)
{
m_editor = new QPlainTextEdit(this);
auto layout = new QVBoxLayout(this);
layout->setMargin(0);
layout->addWidget(m_editor);
}
void NameValueItemsWidget::setEnvironmentItems(const EnvironmentItems &items)
{
EnvironmentItems sortedItems = items;
EnvironmentItem::sort(&sortedItems);
const QStringList list = EnvironmentItem::toStringList(sortedItems);
m_editor->document()->setPlainText(list.join(QLatin1Char('\n')));
}
EnvironmentItems NameValueItemsWidget::environmentItems() const
{
const QStringList list = m_editor->document()->toPlainText().split(QLatin1String("\n"));
EnvironmentItems items = EnvironmentItem::fromStringList(list);
return cleanUp(items);
}
void NameValueItemsWidget::setPlaceholderText(const QString &text)
{
m_editor->setPlaceholderText(text);
}
} // namespace Internal
NameValuesDialog::NameValuesDialog(const QString &windowTitle, const QString &helpText, QWidget *parent)
: QDialog(parent)
{
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
resize(640, 480);
m_editor = new Internal::NameValueItemsWidget(this);
auto box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel,
Qt::Horizontal,
this);
connect(box, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(box, &QDialogButtonBox::rejected, this, &QDialog::reject);
auto helpLabel = new QLabel(this);
helpLabel->setText(helpText);
auto layout = new QVBoxLayout(this);
layout->addWidget(m_editor);
layout->addWidget(helpLabel);
layout->addWidget(box);
setWindowTitle(windowTitle);
}
void NameValuesDialog::setNameValueItems(const EnvironmentItems &items)
{
m_editor->setEnvironmentItems(items);
}
EnvironmentItems NameValuesDialog::nameValueItems() const
{
return m_editor->environmentItems();
}
void NameValuesDialog::setPlaceholderText(const QString &text)
{
m_editor->setPlaceholderText(text);
}
Utils::optional<NameValueItems> NameValuesDialog::getNameValueItems(QWidget *parent,
const NameValueItems &initial,
const QString &placeholderText,
Polisher polisher,
const QString &windowTitle,
const QString &helpText)
{
NameValuesDialog dialog(windowTitle, helpText, parent);
if (polisher)
polisher(&dialog);
dialog.setNameValueItems(initial);
dialog.setPlaceholderText(placeholderText);
bool result = dialog.exec() == QDialog::Accepted;
if (result)
return dialog.nameValueItems();
return {};
}
} // namespace Utils

View File

@@ -0,0 +1,86 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "environmentfwd.h"
#include "optional.h"
#include "utils_global.h"
#include <QDialog>
#include <functional>
#include <memory>
QT_BEGIN_NAMESPACE
class QPlainTextEdit;
QT_END_NAMESPACE
namespace Utils {
namespace Internal {
class NameValueItemsWidget : public QWidget
{
Q_OBJECT
public:
explicit NameValueItemsWidget(QWidget *parent = nullptr);
void setEnvironmentItems(const EnvironmentItems &items);
EnvironmentItems environmentItems() const;
void setPlaceholderText(const QString &text);
private:
QPlainTextEdit *m_editor;
};
} // namespace Internal
class QTCREATOR_UTILS_EXPORT NameValuesDialog : public QDialog
{
Q_OBJECT
public:
void setNameValueItems(const NameValueItems &items);
NameValueItems nameValueItems() const;
void setPlaceholderText(const QString &text);
using Polisher = std::function<void(QWidget *)>;
static Utils::optional<NameValueItems> getNameValueItems(QWidget *parent = nullptr,
const NameValueItems &initial = {},
const QString &placeholderText = {},
Polisher polish = {},
const QString &windowTitle = {},
const QString &helpText = {});
protected:
explicit NameValuesDialog(const QString &windowTitle,
const QString &helpText,
QWidget *parent = {});
private:
Internal::NameValueItemsWidget *m_editor;
};
} // namespace Utils

View File

@@ -0,0 +1,72 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "namevaluevalidator.h"
#include "namevaluemodel.h"
#include "tooltip/tooltip.h"
#include <QTreeView>
namespace Utils {
NameValueValidator::NameValueValidator(QWidget *parent,
Utils::NameValueModel *model,
QTreeView *view,
const QModelIndex &index,
const QString &toolTipText)
: QValidator(parent)
, m_toolTipText(toolTipText)
, m_model(model)
, m_view(view)
, m_index(index)
{
m_hideTipTimer.setInterval(2000);
m_hideTipTimer.setSingleShot(true);
connect(&m_hideTipTimer, &QTimer::timeout, this, []() { Utils::ToolTip::hide(); });
}
QValidator::State NameValueValidator::validate(QString &in, int &pos) const
{
Q_UNUSED(pos)
QModelIndex idx = m_model->variableToIndex(in);
if (idx.isValid() && idx != m_index)
return QValidator::Intermediate;
Utils::ToolTip::hide();
m_hideTipTimer.stop();
return QValidator::Acceptable;
}
void NameValueValidator::fixup(QString &input) const
{
Q_UNUSED(input)
QPoint pos = m_view->mapToGlobal(m_view->visualRect(m_index).topLeft());
pos -= Utils::ToolTip::offsetFromPosition();
Utils::ToolTip::show(pos, m_toolTipText);
m_hideTipTimer.start();
// do nothing
}
} // namespace Utils

View File

@@ -0,0 +1,58 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "environmentfwd.h"
#include "utils_global.h"
#include <QModelIndex>
#include <QTimer>
#include <QValidator>
namespace Utils {
class QTCREATOR_UTILS_EXPORT NameValueValidator : public QValidator
{
Q_OBJECT
public:
NameValueValidator(QWidget *parent,
Utils::NameValueModel *model,
QTreeView *view,
const QModelIndex &index,
const QString &toolTipText);
QValidator::State validate(QString &in, int &pos) const override;
void fixup(QString &input) const override;
private:
const QString &m_toolTipText;
Utils::NameValueModel *m_model;
QTreeView *m_view;
QModelIndex m_index;
mutable QTimer m_hideTipTimer;
};
} // namespace Utils

View File

@@ -27,6 +27,10 @@ SOURCES += \
$$PWD/environment.cpp \
$$PWD/environmentmodel.cpp \
$$PWD/environmentdialog.cpp \
$$PWD/namevaluedictionary.cpp \
$$PWD/namevalueitem.cpp \
$$PWD/namevaluemodel.cpp \
$$PWD/namevaluesdialog.cpp \
$$PWD/qrcparser.cpp \
$$PWD/qtcprocess.cpp \
$$PWD/reloadpromptutils.cpp \
@@ -125,19 +129,24 @@ SOURCES += \
$$PWD/fixedsizeclicklabel.cpp \
$$PWD/removefiledialog.cpp \
$$PWD/differ.cpp \
$$PWD/jsontreeitem.cpp
$$PWD/jsontreeitem.cpp \
$$PWD/namevaluevalidator.cpp
win32:SOURCES += $$PWD/consoleprocess_win.cpp
else:SOURCES += $$PWD/consoleprocess_unix.cpp
HEADERS += \
$$PWD/environmentfwd.h \
$$PWD/genericconstants.h \
$$PWD/globalfilechangeblocker.h \
$$PWD/benchmarker.h \
$$PWD/environment.h \
$$PWD/environmentmodel.h \
$$PWD/environmentdialog.h \
$$PWD/namevaluedictionary.h \
$$PWD/namevalueitem.h \
$$PWD/namevaluemodel.h \
$$PWD/namevaluesdialog.h \
$$PWD/pointeralgorithm.h \
$$PWD/qrcparser.h \
$$PWD/qtcprocess.h \
@@ -270,7 +279,8 @@ HEADERS += \
$$PWD/differ.h \
$$PWD/cpplanguage_details.h \
$$PWD/jsontreeitem.h \
$$PWD/listmodel.h
$$PWD/listmodel.h \
$$PWD/namevaluevalidator.h
FORMS += $$PWD/filewizardpage.ui \
$$PWD/newclasswidget.ui \

View File

@@ -150,7 +150,7 @@ static QString constructOmittedDetailsString(const QStringList &omitted)
"configuration page for \"%1\":") + '\n' + omitted.join('\n');
}
static QString constructOmittedVariablesDetailsString(const QList<Utils::EnvironmentItem> &diff)
static QString constructOmittedVariablesDetailsString(const Utils::EnvironmentItems &diff)
{
auto removedVars = Utils::transform<QStringList>(diff, [](const Utils::EnvironmentItem &it) {
return it.name;
@@ -204,10 +204,10 @@ void TestRunner::scheduleNext()
m_currentProcess->setWorkingDirectory(m_currentConfig->workingDirectory());
const Utils::Environment &original = m_currentConfig->environment();
Utils::Environment environment = m_currentConfig->filteredEnvironment(original);
const QList<Utils::EnvironmentItem> removedVariables
= Utils::filtered(original.diff(environment), [](const Utils::EnvironmentItem &it) {
return it.operation == Utils::EnvironmentItem::Unset;
});
const Utils::EnvironmentItems removedVariables = Utils::filtered(
original.diff(environment), [](const Utils::EnvironmentItem &it) {
return it.operation == Utils::EnvironmentItem::Unset;
});
if (!removedVariables.isEmpty()) {
const QString &details = constructOmittedVariablesDetailsString(removedVariables)
.arg(m_currentConfig->displayName());
@@ -560,10 +560,10 @@ void TestRunner::debugTests()
}
Utils::Environment original(inferior.environment);
inferior.environment = config->filteredEnvironment(original);
const QList<Utils::EnvironmentItem> removedVariables
= Utils::filtered(original.diff(inferior.environment), [](const Utils::EnvironmentItem &it) {
return it.operation == Utils::EnvironmentItem::Unset;
});
const Utils::EnvironmentItems removedVariables = Utils::filtered(
original.diff(inferior.environment), [](const Utils::EnvironmentItem &it) {
return it.operation == Utils::EnvironmentItem::Unset;
});
if (!removedVariables.isEmpty()) {
const QString &details = constructOmittedVariablesDetailsString(removedVariables)
.arg(config->displayName());

View File

@@ -60,9 +60,8 @@ static void addProjectPanelWidget()
auto panelFactory = new ProjectExplorer::ProjectPanelFactory();
panelFactory->setPriority(60);
panelFactory->setDisplayName(ClangProjectSettingsWidget::tr("Clang Code Model"));
panelFactory->setCreateWidgetFunction([](ProjectExplorer::Project *project) {
return new ClangProjectSettingsWidget(project);
});
panelFactory->setCreateWidgetFunction(
[&](ProjectExplorer::Project *project) { return new ClangProjectSettingsWidget(project); });
ProjectExplorer::ProjectPanelFactory::registerFactory(panelFactory);
}

View File

@@ -106,8 +106,8 @@ static void replaceCppCodeStyle()
bool ClangFormatPlugin::initialize(const QStringList &arguments, QString *errorString)
{
Q_UNUSED(arguments);
Q_UNUSED(errorString);
Q_UNUSED(arguments)
Q_UNUSED(errorString)
#ifdef KEEP_LINE_BREAKS_FOR_NON_EMPTY_LINES_BACKPORTED
replaceCppCodeStyle();

View File

@@ -4,12 +4,17 @@ add_qtc_plugin(ClangPchManager
DEFINES CLANGPCHMANAGER_LIB
PLUGIN_DEPENDS Core CppTools
SOURCES
clangindexingprojectsettings.cpp clangindexingprojectsettings.h
clangindexingprojectsettingswidget.cpp clangindexingprojectsettingswidget.h clangindexingprojectsettingswidget.ui
clangindexingsettingsmanager.cpp clangindexingsettingsmanager.h
clangpchmanager_global.h
clangpchmanagerplugin.cpp clangpchmanagerplugin.h
pchmanagerclient.cpp pchmanagerclient.h
pchmanagerconnectionclient.cpp pchmanagerconnectionclient.h
pchmanagernotifierinterface.cpp pchmanagernotifierinterface.h
pchmanagerprojectupdater.cpp pchmanagerprojectupdater.h
preprocessormacrocollector.cpp preprocessormacrocollector.h
preprocessormacrowidget.cpp preprocessormacrowidget.h
progressmanager.h
progressmanagerinterface.h
projectupdater.cpp projectupdater.h

View File

@@ -0,0 +1,96 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "clangindexingprojectsettings.h"
#include <projectexplorer/project.h>
namespace ClangPchManager {
namespace {
Utils::NameValueItems fromQVariantMap(const QVariantMap &variantMap,
Utils::NameValueItem::Operation operation)
{
Utils::NameValueItems nameValueItems;
nameValueItems.reserve(variantMap.size());
auto end = variantMap.end();
for (auto iterator = variantMap.cbegin(); iterator != end; ++iterator) // QMap iterators are broken
nameValueItems.push_back({iterator.key(), iterator.value().toString(), operation});
return nameValueItems;
}
} // namespace
ClangIndexingProjectSettings::ClangIndexingProjectSettings(ProjectExplorer::Project *project)
: m_project(project)
{}
void ClangIndexingProjectSettings::saveMacros(const Utils::NameValueItems &items)
{
QVariantMap unsets;
QVariantMap sets;
for (const Utils::NameValueItem &item : items) {
using Operation = Utils::NameValueItem::Operation;
switch (item.operation) {
case Operation::Set:
sets[item.name] = item.value;
break;
case Operation::Unset:
unsets[item.name] = item.value;
break;
default:
break;
}
}
if (sets.size())
m_project->setNamedSettings("set_indexing_macro", sets);
else
m_project->setNamedSettings("set_indexing_macro", {});
if (unsets.size())
m_project->setNamedSettings("unset_indexing_macro", unsets);
else
m_project->setNamedSettings("unset_indexing_macro", {});
}
Utils::NameValueItems ClangIndexingProjectSettings::readMacros() const
{
QVariant unsets = m_project->namedSettings("unset_indexing_macro");
Utils::NameValueItems items = fromQVariantMap(unsets.toMap(), Utils::NameValueItem::Unset);
QVariant sets = m_project->namedSettings("set_indexing_macro");
items += fromQVariantMap(sets.toMap(), Utils::NameValueItem::Set);
return items;
}
} // namespace ClangPchManager

View File

@@ -0,0 +1,50 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "clangpchmanager_global.h"
#include <utils/namevalueitem.h>
namespace ProjectExplorer {
class Project;
}
namespace ClangPchManager {
class CLANGPCHMANAGER_EXPORT ClangIndexingProjectSettings
{
public:
ClangIndexingProjectSettings(ProjectExplorer::Project *project);
Utils::NameValueItems readMacros() const;
void saveMacros(const Utils::NameValueItems &items);
private:
ProjectExplorer::Project *m_project;
};
} // namespace ClangPchManager

View File

@@ -0,0 +1,61 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "clangindexingprojectsettingswidget.h"
#include "ui_clangindexingprojectsettingswidget.h"
#include <cpptools/cppmodelmanager.h>
#include <projectexplorer/project.h>
#include "preprocessormacrocollector.h"
#include "preprocessormacrowidget.h"
namespace ClangPchManager {
ClangIndexingProjectSettingsWidget::ClangIndexingProjectSettingsWidget(ClangIndexingProjectSettings *settings)
: ui(new Ui::ClangIndexingProjectSettingsWidget)
{
ui->setupUi(this);
ui->preprocessorMacrosWidget->setSettings(settings);
}
ClangIndexingProjectSettingsWidget::~ClangIndexingProjectSettingsWidget()
{
delete ui;
}
void ClangIndexingProjectSettingsWidget::onProjectPartsUpdated(ProjectExplorer::Project *project)
{
const CppTools::ProjectInfo projectInfo = CppTools::CppModelManager::instance()->projectInfo(
project);
PreprocessorMacroCollector collector;
for (auto projectPart : projectInfo.projectParts())
collector.add(projectPart->projectMacros);
ui->preprocessorMacrosWidget->setBasePreprocessorMacros(collector.macros());
}
} // namespace ClangPchManager

View File

@@ -0,0 +1,56 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <QWidget>
namespace ProjectExplorer {
class Project;
}
namespace Ui {
class ClangIndexingProjectSettingsWidget;
}
namespace ClangPchManager {
class ClangIndexingProjectSettings;
class ClangIndexingProjectSettingsWidget : public QWidget
{
Q_OBJECT
public:
explicit ClangIndexingProjectSettingsWidget(ClangIndexingProjectSettings *settings);
~ClangIndexingProjectSettingsWidget();
void onProjectPartsUpdated(ProjectExplorer::Project *project);
private:
Ui::ClangIndexingProjectSettingsWidget *ui;
};
} // namespace ClangPchManager

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ClangIndexingProjectSettingsWidget</class>
<widget class="QWidget" name="ClangIndexingProjectSettingsWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="ClangPchManager::PreprocessorMacroWidget" name="preprocessorMacrosWidget" native="true"/>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>ClangPchManager::PreprocessorMacroWidget</class>
<extends>QWidget</extends>
<header>preprocessormacrowidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,55 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "clangindexingsettingsmanager.h"
#include "clangindexingprojectsettings.h"
namespace ClangPchManager {
ClangIndexingSettingsManager::ClangIndexingSettingsManager() = default;
ClangIndexingSettingsManager::~ClangIndexingSettingsManager() = default;
ClangIndexingProjectSettings *ClangIndexingSettingsManager::settings(ProjectExplorer::Project *project)
{
auto &setting = m_settings[project];
if (!setting)
setting = std::make_unique<ClangIndexingProjectSettings>(project);
return setting.get();
}
void ClangIndexingSettingsManager::remove(ProjectExplorer::Project *project)
{
m_settings.erase(project);
}
bool ClangIndexingSettingsManager::hasSettings(ProjectExplorer::Project *project) const
{
return m_settings.find(project) != m_settings.end();
}
} // namespace ClangPchManager

View File

@@ -0,0 +1,56 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "clangpchmanager_global.h"
#include <map>
#include <memory>
namespace ProjectExplorer {
class Project;
}
namespace ClangPchManager {
class ClangIndexingProjectSettings;
class CLANGPCHMANAGER_EXPORT ClangIndexingSettingsManager
{
public:
ClangIndexingSettingsManager();
~ClangIndexingSettingsManager();
ClangIndexingProjectSettings *settings(ProjectExplorer::Project *project);
void remove(ProjectExplorer::Project *project);
bool hasSettings(ProjectExplorer::Project *project) const;
private:
std::map<ProjectExplorer::Project *, std::unique_ptr<ClangIndexingProjectSettings>> m_settings;
};
} // namespace ClangPchManager

View File

@@ -7,18 +7,24 @@ shared|dll {
INCLUDEPATH += $$PWD
HEADERS += \
$$PWD/clangindexingprojectsettings.h \
$$PWD/clangindexingsettingsmanager.h \
$$PWD/pchmanagerclient.h \
$$PWD/pchmanagernotifierinterface.h \
$$PWD/pchmanagerconnectionclient.h \
$$PWD/clangpchmanager_global.h \
$$PWD/preprocessormacrocollector.h \
$$PWD/projectupdater.h \
$$PWD/pchmanagerprojectupdater.h \
$$PWD/progressmanager.h \
$$PWD/progressmanagerinterface.h
SOURCES += \
$$PWD/clangindexingprojectsettings.cpp \
$$PWD/clangindexingsettingsmanager.cpp \
$$PWD/pchmanagerclient.cpp \
$$PWD/pchmanagernotifierinterface.cpp \
$$PWD/pchmanagerconnectionclient.cpp \
$$PWD/preprocessormacrocollector.cpp \
$$PWD/projectupdater.cpp \
$$PWD/pchmanagerprojectupdater.cpp

View File

@@ -12,8 +12,15 @@ win32 {
HEADERS += \
$$PWD/clangpchmanagerplugin.h \
clangindexingprojectsettingswidget.h \
preprocessormacrowidget.h \
qtcreatorprojectupdater.h
SOURCES += \
$$PWD/clangpchmanagerplugin.cpp \
clangindexingprojectsettingswidget.cpp \
preprocessormacrowidget.cpp \
qtcreatorprojectupdater.cpp
FORMS += \
clangindexingprojectsettingswidget.ui

View File

@@ -25,8 +25,10 @@
#include "clangpchmanagerplugin.h"
#include "pchmanagerconnectionclient.h"
#include "clangindexingprojectsettingswidget.h"
#include "clangindexingsettingsmanager.h"
#include "pchmanagerclient.h"
#include "pchmanagerconnectionclient.h"
#include "progressmanager.h"
#include "qtcreatorprojectupdater.h"
@@ -38,12 +40,14 @@
#include <coreplugin/icore.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <extensionsystem/pluginmanager.h>
#include <projectexplorer/projectpanelfactory.h>
#include <utils/hostosinfo.h>
#include <QFutureInterface>
#include <chrono>
#include <map>
using namespace std::chrono_literals;
@@ -58,6 +62,26 @@ QString backendProcessPath()
+ QStringLiteral(QTC_HOST_EXE_SUFFIX);
}
void addIndexingProjectPaneWidget(ClangIndexingSettingsManager &settingsManager)
{
auto factory = new ProjectExplorer::ProjectPanelFactory;
factory->setPriority(120);
factory->setDisplayName(ClangIndexingProjectSettingsWidget::tr("Clang Indexing"));
factory->setCreateWidgetFunction([&](ProjectExplorer::Project *project) {
auto widget = new ClangIndexingProjectSettingsWidget(settingsManager.settings(project));
widget->onProjectPartsUpdated(project);
QObject::connect(CppTools::CppModelManager::instance(),
&CppTools::CppModelManager::projectPartsUpdated,
widget,
&ClangIndexingProjectSettingsWidget::onProjectPartsUpdated);
return widget;
});
ProjectExplorer::ProjectPanelFactory::registerFactory(factory);
}
} // anonymous namespace
class ClangPchManagerPluginData
@@ -83,10 +107,12 @@ public:
ClangBackEnd::ProjectPartsStorage<Sqlite::Database> projectPartsStorage{database};
PchManagerClient pchManagerClient{pchCreationProgressManager, dependencyCreationProgressManager};
PchManagerConnectionClient connectionClient{&pchManagerClient};
ClangIndexingSettingsManager settingsManager;
QtCreatorProjectUpdater<PchManagerProjectUpdater> projectUpdate{connectionClient.serverProxy(),
pchManagerClient,
filePathCache,
projectPartsStorage};
projectPartsStorage,
settingsManager};
};
std::unique_ptr<ClangPchManagerPluginData> ClangPchManagerPlugin::d;
@@ -102,6 +128,8 @@ bool ClangPchManagerPlugin::initialize(const QStringList & /*arguments*/, QStrin
startBackend();
addIndexingProjectPaneWidget(d->settingsManager);
return true;
}
@@ -133,4 +161,9 @@ PchManagerClient &ClangPchManagerPlugin::pchManagerClient()
return d->pchManagerClient;
}
ClangIndexingSettingsManager &ClangPchManagerPlugin::settingsManager()
{
return d->settingsManager;
}
} // namespace ClangRefactoring

View File

@@ -33,6 +33,7 @@
namespace ClangPchManager {
class ClangIndexingSettingsManager;
class ClangPchManagerPluginData;
class PchManagerClient;
@@ -50,6 +51,7 @@ public:
ShutdownFlag aboutToShutdown();
static PchManagerClient &pchManagerClient();
static ClangIndexingSettingsManager &settingsManager();
private:
void startBackend();

View File

@@ -35,8 +35,9 @@ public:
PchManagerProjectUpdater(ClangBackEnd::ProjectManagementServerInterface &server,
PchManagerClient &client,
ClangBackEnd::FilePathCachingInterface &filePathCache,
ClangBackEnd::ProjectPartsStorageInterface &projectPartsStorage)
: ProjectUpdater(server, filePathCache, projectPartsStorage)
ClangBackEnd::ProjectPartsStorageInterface &projectPartsStorage,
ClangIndexingSettingsManager &settingsManager)
: ProjectUpdater(server, filePathCache, projectPartsStorage, settingsManager)
, m_client(client)
{}

View File

@@ -0,0 +1,68 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "preprocessormacrocollector.h"
namespace ClangPchManager {
namespace {
PreprocessorMacros toSortedMacros(const ProjectExplorer::Macros &macros)
{
PreprocessorMacros sortedMacros;
sortedMacros.reserve(macros.size());
for (const ProjectExplorer::Macro &macro : macros)
if (macro.type == ProjectExplorer::MacroType::Define)
sortedMacros.push_back({QString::fromUtf8(macro.key), QString::fromUtf8(macro.value)});
std::sort(sortedMacros.begin(), sortedMacros.end());
return sortedMacros;
}
} // namespace
void PreprocessorMacroCollector::add(const ProjectExplorer::Macros &macros)
{
PreprocessorMacros sortedMacros = toSortedMacros(macros);
std::sort(sortedMacros.begin(), sortedMacros.end());
PreprocessorMacros mergedMacros;
mergedMacros.reserve(sortedMacros.size() + m_macros.size());
std::set_union(m_macros.begin(),
m_macros.end(),
sortedMacros.begin(),
sortedMacros.end(),
std::back_inserter(mergedMacros));
m_macros = mergedMacros;
}
const PreprocessorMacros &PreprocessorMacroCollector::macros() const
{
return m_macros;
}
} // namespace ClangPchManager

View File

@@ -0,0 +1,51 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <QString>
#include <QVector>
#include <projectexplorer/projectmacro.h>
#include <utility>
namespace ClangPchManager {
using PreprocessorMacro = std::pair<QString, QString>;
using PreprocessorMacros = QVector<PreprocessorMacro>;
class PreprocessorMacroCollector
{
public:
void add(const ProjectExplorer::Macros &macros);
const PreprocessorMacros &macros() const;
private:
PreprocessorMacros m_macros;
};
} // namespace ClangPchManager

View File

@@ -0,0 +1,282 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "preprocessormacrowidget.h"
#include "clangindexingprojectsettings.h"
#include <utils/detailswidget.h>
#include <utils/headerviewstretcher.h>
#include <utils/itemviews.h>
#include <utils/namevaluedictionary.h>
#include <utils/namevalueitem.h>
#include <utils/namevaluemodel.h>
#include <utils/namevaluesdialog.h>
#include <utils/namevaluevalidator.h>
#include <coreplugin/find/itemviewfind.h>
#include <QLineEdit>
#include <QStyledItemDelegate>
#include <QVBoxLayout>
namespace ClangPchManager {
class ProcessorMacroDelegate : public QStyledItemDelegate
{
public:
ProcessorMacroDelegate(Utils::NameValueModel *model, QTreeView *view)
: QStyledItemDelegate(view)
, m_model(model)
, m_view(view)
{}
QWidget *createEditor(QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index) const override
{
QWidget *w = QStyledItemDelegate::createEditor(parent, option, index);
if (index.column() != 0)
return w;
if (auto edit = qobject_cast<QLineEdit *>(w))
edit->setValidator(new Utils::NameValueValidator(
edit, m_model, m_view, index, PreprocessorMacroWidget::tr("Macro already exists.")));
return w;
}
private:
Utils::NameValueModel *m_model;
QTreeView *m_view;
};
PreprocessorMacroWidget::PreprocessorMacroWidget(QWidget *parent) : QWidget(parent)
{
m_model = std::make_unique<Utils::NameValueModel>();
connect(m_model.get(),
&Utils::NameValueModel::userChangesChanged,
this,
&PreprocessorMacroWidget::userChangesChanged);
connect(m_model.get(),
&QAbstractItemModel::modelReset,
this,
&PreprocessorMacroWidget::invalidateCurrentIndex);
connect(m_model.get(), &Utils::NameValueModel::focusIndex, this, &PreprocessorMacroWidget::focusIndex);
auto vbox = new QVBoxLayout(this);
vbox->setContentsMargins(0, 0, 0, 0);
m_detailsContainer = new Utils::DetailsWidget(this);
auto details = new QWidget(m_detailsContainer);
m_detailsContainer->setWidget(details);
details->setVisible(false);
auto vbox2 = new QVBoxLayout(details);
vbox2->setMargin(0);
auto horizontalLayout = new QHBoxLayout;
horizontalLayout->setMargin(0);
auto tree = new Utils::TreeView(this);
connect(tree, &QAbstractItemView::activated, tree, [tree](const QModelIndex &idx) {
tree->edit(idx);
});
m_preprocessorMacrosView = tree;
m_preprocessorMacrosView->setModel(m_model.get());
m_preprocessorMacrosView->setItemDelegate(
new ProcessorMacroDelegate(m_model.get(), m_preprocessorMacrosView));
m_preprocessorMacrosView->setMinimumHeight(400);
m_preprocessorMacrosView->setRootIsDecorated(false);
m_preprocessorMacrosView->setUniformRowHeights(true);
new Utils::HeaderViewStretcher(m_preprocessorMacrosView->header(), 1);
m_preprocessorMacrosView->setSelectionMode(QAbstractItemView::SingleSelection);
m_preprocessorMacrosView->setSelectionBehavior(QAbstractItemView::SelectItems);
m_preprocessorMacrosView->setFrameShape(QFrame::NoFrame);
QFrame *findWrapper = Core::ItemViewFind::createSearchableWrapper(m_preprocessorMacrosView,
Core::ItemViewFind::LightColored);
findWrapper->setFrameStyle(QFrame::StyledPanel);
horizontalLayout->addWidget(findWrapper);
auto buttonLayout = new QVBoxLayout();
m_editButton = new QPushButton(this);
m_editButton->setText(tr("Ed&it"));
buttonLayout->addWidget(m_editButton);
m_addButton = new QPushButton(this);
m_addButton->setText(tr("&Add"));
buttonLayout->addWidget(m_addButton);
m_resetButton = new QPushButton(this);
m_resetButton->setEnabled(false);
m_resetButton->setText(tr("&Reset"));
buttonLayout->addWidget(m_resetButton);
m_unsetButton = new QPushButton(this);
m_unsetButton->setEnabled(false);
m_unsetButton->setText(tr("&Unset"));
buttonLayout->addWidget(m_unsetButton);
buttonLayout->addStretch();
horizontalLayout->addLayout(buttonLayout);
vbox2->addLayout(horizontalLayout);
vbox->addWidget(m_detailsContainer);
connect(m_model.get(),
&QAbstractItemModel::dataChanged,
this,
&PreprocessorMacroWidget::updateButtons);
connect(m_editButton, &QAbstractButton::clicked, this, &PreprocessorMacroWidget::editButtonClicked);
connect(m_addButton, &QAbstractButton::clicked, this, &PreprocessorMacroWidget::addButtonClicked);
connect(m_resetButton, &QAbstractButton::clicked, this, &PreprocessorMacroWidget::removeButtonClicked);
connect(m_unsetButton, &QAbstractButton::clicked, this, &PreprocessorMacroWidget::unsetButtonClicked);
connect(m_preprocessorMacrosView->selectionModel(),
&QItemSelectionModel::currentChanged,
this,
&PreprocessorMacroWidget::currentIndexChanged);
connect(m_detailsContainer,
&Utils::DetailsWidget::linkActivated,
this,
&PreprocessorMacroWidget::linkActivated);
connect(m_model.get(),
&Utils::NameValueModel::userChangesChanged,
this,
&PreprocessorMacroWidget::updateSummaryText);
connect(m_model.get(),
&Utils::NameValueModel::userChangesChanged,
this,
&PreprocessorMacroWidget::saveSettings);
}
void PreprocessorMacroWidget::setBasePreprocessorMacros(const PreprocessorMacros &macros)
{
m_model->setUserChanges(m_settings->readMacros());
m_model->setBaseNameValueDictionary(Utils::NameValueDictionary{macros});
}
void PreprocessorMacroWidget::setSettings(ClangIndexingProjectSettings *settings)
{
m_settings = settings;
}
PreprocessorMacroWidget::~PreprocessorMacroWidget() = default;
void PreprocessorMacroWidget::updateButtons()
{
currentIndexChanged(m_preprocessorMacrosView->currentIndex());
}
void PreprocessorMacroWidget::focusIndex(const QModelIndex &index)
{
m_preprocessorMacrosView->setCurrentIndex(index);
m_preprocessorMacrosView->setFocus();
m_preprocessorMacrosView->scrollTo(index, QAbstractItemView::PositionAtTop);
}
void PreprocessorMacroWidget::invalidateCurrentIndex()
{
currentIndexChanged(QModelIndex());
}
void PreprocessorMacroWidget::editButtonClicked()
{
m_preprocessorMacrosView->edit(m_preprocessorMacrosView->currentIndex());
}
void PreprocessorMacroWidget::addButtonClicked()
{
QModelIndex index = m_model->addVariable();
m_preprocessorMacrosView->setCurrentIndex(index);
m_preprocessorMacrosView->edit(index);
}
void PreprocessorMacroWidget::removeButtonClicked()
{
const QString &name = m_model->indexToVariable(m_preprocessorMacrosView->currentIndex());
m_model->resetVariable(name);
}
void PreprocessorMacroWidget::unsetButtonClicked()
{
const QString &name = m_model->indexToVariable(m_preprocessorMacrosView->currentIndex());
if (!m_model->canReset(name))
m_model->resetVariable(name);
else
m_model->unsetVariable(name);
}
void PreprocessorMacroWidget::currentIndexChanged(const QModelIndex &current)
{
if (current.isValid()) {
m_editButton->setEnabled(true);
const QString &name = m_model->indexToVariable(current);
bool modified = m_model->canReset(name) && m_model->changes(name);
bool unset = m_model->canUnset(name);
m_resetButton->setEnabled(modified || unset);
m_unsetButton->setEnabled(!unset);
} else {
m_editButton->setEnabled(false);
m_resetButton->setEnabled(false);
m_unsetButton->setEnabled(false);
}
}
void PreprocessorMacroWidget::linkActivated(const QString &link)
{
m_detailsContainer->setState(Utils::DetailsWidget::Expanded);
QModelIndex idx = m_model->variableToIndex(link);
focusIndex(idx);
}
void PreprocessorMacroWidget::updateSummaryText()
{
Utils::NameValueItems items = m_model->userChanges();
Utils::NameValueItem::sort(&items);
QString text;
for (const Utils::EnvironmentItem &item : items) {
if (item.name != Utils::NameValueModel::tr("<VARIABLE>")) {
text.append(QLatin1String("<br>"));
if (item.operation == Utils::NameValueItem::Unset)
text.append(tr("Unset <a href=\"%1\"><b>%1</b></a>").arg(item.name.toHtmlEscaped()));
else
text.append(tr("Set <a href=\"%1\"><b>%1</b></a> to <b>%2</b>")
.arg(item.name.toHtmlEscaped(), item.value.toHtmlEscaped()));
}
}
m_detailsContainer->setSummaryText(text);
}
void PreprocessorMacroWidget::saveSettings()
{
m_settings->saveMacros(m_model->userChanges());
}
} // namespace ClangPchManager

View File

@@ -0,0 +1,79 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <QPushButton>
#include <QTreeView>
#include <memory>
namespace Utils {
class DetailsWidget;
class NameValueModel;
} // namespace Utils
namespace ClangPchManager {
class ClangIndexingProjectSettings;
using PreprocessorMacro = std::pair<QString, QString>;
using PreprocessorMacros = QVector<PreprocessorMacro>;
class PreprocessorMacroWidget : public QWidget
{
Q_OBJECT
public:
explicit PreprocessorMacroWidget(QWidget *parent = nullptr);
~PreprocessorMacroWidget() override;
void setBasePreprocessorMacros(const PreprocessorMacros &macros);
void setSettings(ClangIndexingProjectSettings *settings);
signals:
void userChangesChanged();
private:
void updateButtons();
void focusIndex(const QModelIndex &index);
void invalidateCurrentIndex();
void editButtonClicked();
void addButtonClicked();
void removeButtonClicked();
void unsetButtonClicked();
void currentIndexChanged(const QModelIndex &current);
void linkActivated(const QString &link);
void updateSummaryText();
void saveSettings();
private:
std::unique_ptr<Utils::NameValueModel> m_model;
Utils::DetailsWidget *m_detailsContainer;
QTreeView *m_preprocessorMacrosView;
QPushButton *m_editButton;
QPushButton *m_addButton;
QPushButton *m_resetButton;
QPushButton *m_unsetButton;
ClangIndexingProjectSettings *m_settings = {};
};
} // namespace ClangPchManager

View File

@@ -27,6 +27,8 @@
#include "pchmanagerclient.h"
#include <clangindexingprojectsettings.h>
#include <clangindexingsettingsmanager.h>
#include <filepathid.h>
#include <pchmanagerserverinterface.h>
#include <removegeneratedfilesmessage.h>
@@ -42,6 +44,7 @@
#include <projectexplorer/buildconfiguration.h>
#include <utils/algorithm.h>
#include <utils/namevalueitem.h>
#include <algorithm>
#include <functional>
@@ -175,9 +178,63 @@ void cleanupMacros(ClangBackEnd::CompilerMacros &macros)
macros.erase(newEnd, macros.end());
}
void updateWithSettings(ClangBackEnd::CompilerMacros &macros,
Utils::NameValueItems &&settingsItems,
int &index)
{
std::sort(settingsItems.begin(), settingsItems.end(), [](const auto &first, const auto &second) {
return std::tie(first.operation, first.name, first.value)
< std::tie(first.operation, second.name, second.value);
});
auto point = std::partition_point(settingsItems.begin(), settingsItems.end(), [](const auto &entry) {
return entry.operation == Utils::NameValueItem::Set;
});
std::transform(
settingsItems.begin(),
point,
std::back_inserter(macros),
[&](const Utils::NameValueItem &settingsMacro) {
return ClangBackEnd::CompilerMacro{settingsMacro.name, settingsMacro.value, ++index};
});
std::sort(macros.begin(), macros.end(), [](const auto &first, const auto &second) {
return std::tie(first.key, first.value) < std::tie(second.key, second.value);
});
ClangBackEnd::CompilerMacros result;
result.reserve(macros.size());
ClangBackEnd::CompilerMacros convertedSettingsMacros;
convertedSettingsMacros.resize(std::distance(point, settingsItems.end()));
std::transform(
point,
settingsItems.end(),
std::back_inserter(convertedSettingsMacros),
[&](const Utils::NameValueItem &settingsMacro) {
return ClangBackEnd::CompilerMacro{settingsMacro.name, settingsMacro.value, ++index};
});
std::set_difference(macros.begin(),
macros.end(),
convertedSettingsMacros.begin(),
convertedSettingsMacros.end(),
std::back_inserter(result),
[](const ClangBackEnd::CompilerMacro &first,
const ClangBackEnd::CompilerMacro &second) {
return std::tie(first.key, first.value)
< std::tie(second.key, second.value);
});
macros = std::move(result);
}
} // namespace
ClangBackEnd::CompilerMacros ProjectUpdater::createCompilerMacros(const ProjectExplorer::Macros &projectMacros)
ClangBackEnd::CompilerMacros ProjectUpdater::createCompilerMacros(
const ProjectExplorer::Macros &projectMacros, Utils::NameValueItems &&settingsMacros) const
{
int index = 0;
auto macros = Utils::transform<ClangBackEnd::CompilerMacros>(
@@ -186,6 +243,7 @@ ClangBackEnd::CompilerMacros ProjectUpdater::createCompilerMacros(const ProjectE
});
cleanupMacros(macros);
updateWithSettings(macros, std::move(settingsMacros), index);
std::sort(macros.begin(), macros.end());
@@ -291,9 +349,12 @@ ClangBackEnd::ProjectPartContainer ProjectUpdater::toProjectPartContainer(
ClangBackEnd::ProjectPartId projectPartId = m_projectPartsStorage.fetchProjectPartId(
projectPartName);
ClangIndexingProjectSettings *settings = m_settingsManager.settings(projectPart->project);
return ClangBackEnd::ProjectPartContainer(projectPartId,
Utils::SmallStringVector(arguments),
createCompilerMacros(projectPart->projectMacros),
createCompilerMacros(projectPart->projectMacros,
settings->readMacros()),
std::move(includeSearchPaths.system),
std::move(includeSearchPaths.project),
std::move(headerAndSources.headers),

View File

@@ -37,6 +37,8 @@
#include <projectexplorer/headerpath.h>
#include <utils/environmentfwd.h>
namespace ProjectExplorer {
class Macro;
using Macros = QVector<Macro>;
@@ -59,6 +61,8 @@ namespace ClangPchManager {
class HeaderAndSources;
class PchManagerClient;
class ClangIndexingSettingsManager;
class ClangIndexingProjectSettings;
class CLANGPCHMANAGER_EXPORT ProjectUpdater
{
@@ -71,10 +75,12 @@ public:
ProjectUpdater(ClangBackEnd::ProjectManagementServerInterface &server,
ClangBackEnd::FilePathCachingInterface &filePathCache,
ClangBackEnd::ProjectPartsStorageInterface &projectPartsStorage)
ClangBackEnd::ProjectPartsStorageInterface &projectPartsStorage,
ClangIndexingSettingsManager &settingsManager)
: m_server(server)
, m_filePathCache(filePathCache)
, m_projectPartsStorage(projectPartsStorage)
, m_settingsManager(settingsManager)
{}
void updateProjectParts(const std::vector<CppTools::ProjectPart *> &projectParts,
@@ -98,8 +104,8 @@ public:
void addToHeaderAndSources(HeaderAndSources &headerAndSources,
const CppTools::ProjectFile &projectFile) const;
static QStringList toolChainArguments(CppTools::ProjectPart *projectPart);
static ClangBackEnd::CompilerMacros createCompilerMacros(
const ProjectExplorer::Macros &projectMacros);
ClangBackEnd::CompilerMacros createCompilerMacros(const ProjectExplorer::Macros &projectMacros,
Utils::NameValueItems &&settingsMacros) const;
static SystemAndProjectIncludeSearchPaths createIncludeSearchPaths(
const CppTools::ProjectPart &projectPart);
static ClangBackEnd::FilePaths createExcludedPaths(
@@ -115,6 +121,7 @@ private:
ClangBackEnd::ProjectManagementServerInterface &m_server;
ClangBackEnd::FilePathCachingInterface &m_filePathCache;
ClangBackEnd::ProjectPartsStorageInterface &m_projectPartsStorage;
ClangIndexingSettingsManager &m_settingsManager;
};
} // namespace ClangPchManager

View File

@@ -59,8 +59,9 @@ public:
QtCreatorProjectUpdater(ClangBackEnd::ProjectManagementServerInterface &server,
ClientType &client,
ClangBackEnd::FilePathCachingInterface &filePathCache,
ClangBackEnd::ProjectPartsStorageInterface &projectPartsStorage)
: ProjectUpdaterType(server, client, filePathCache, projectPartsStorage)
ClangBackEnd::ProjectPartsStorageInterface &projectPartsStorage,
ClangIndexingSettingsManager &settingsManager)
: ProjectUpdaterType(server, client, filePathCache, projectPartsStorage, settingsManager)
{
connectToCppModelManager();
}
@@ -74,7 +75,6 @@ public:
void projectPartsUpdated(ProjectExplorer::Project *project)
{
ProjectUpdaterType::updateProjectParts(Internal::createProjectParts(project), {}); // TODO add support for toolchainarguments
}

View File

@@ -42,8 +42,8 @@
#include <coreplugin/icore.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <extensionsystem/pluginmanager.h>
#include <cpptools/cpptoolsconstants.h>
#include <extensionsystem/pluginmanager.h>
#include <refactoringdatabaseinitializer.h>
#include <filepathcaching.h>
@@ -103,16 +103,13 @@ public:
QtCreatorRefactoringProjectUpdater projectUpdate{connectionClient.serverProxy(),
ClangPchManagerPlugin::pchManagerClient(),
filePathCache,
projectPartsStorage};
projectPartsStorage,
ClangPchManagerPlugin::settingsManager()};
};
ClangRefactoringPlugin::ClangRefactoringPlugin()
{
}
ClangRefactoringPlugin::ClangRefactoringPlugin() = default;
ClangRefactoringPlugin::~ClangRefactoringPlugin()
{
}
ClangRefactoringPlugin::~ClangRefactoringPlugin() = default;
static bool useClangFilters()
{
@@ -131,8 +128,8 @@ bool ClangRefactoringPlugin::initialize(const QStringList & /*arguments*/, QStri
connectBackend();
startBackend();
CppTools::CppModelManager::addRefactoringEngine(
CppTools::RefactoringEngineType::ClangRefactoring, &refactoringEngine());
CppTools::CppModelManager::addRefactoringEngine(CppTools::RefactoringEngineType::ClangRefactoring,
&refactoringEngine());
initializeFilters();

View File

@@ -64,12 +64,14 @@ QtCreatorRefactoringProjectUpdater::QtCreatorRefactoringProjectUpdater(
ClangBackEnd::ProjectManagementServerInterface &server,
ClangPchManager::PchManagerClient &pchManagerClient,
ClangBackEnd::FilePathCachingInterface &filePathCache,
ClangBackEnd::ProjectPartsStorageInterface &projectPartsStorage)
ClangBackEnd::ProjectPartsStorageInterface &projectPartsStorage,
ClangPchManager::ClangIndexingSettingsManager &settingsManager)
: RefactoringProjectUpdater(server,
pchManagerClient,
*cppModelManager(),
filePathCache,
projectPartsStorage)
projectPartsStorage,
settingsManager)
{
connectToCppModelManager();
}

View File

@@ -32,11 +32,11 @@ namespace ClangRefactoring {
class QtCreatorRefactoringProjectUpdater final : public RefactoringProjectUpdater
{
public:
QtCreatorRefactoringProjectUpdater(
ClangBackEnd::ProjectManagementServerInterface &server,
ClangPchManager::PchManagerClient &pchManagerClient,
ClangBackEnd::FilePathCachingInterface &filePathCache,
ClangBackEnd::ProjectPartsStorageInterface &projectPartsStorage);
QtCreatorRefactoringProjectUpdater(ClangBackEnd::ProjectManagementServerInterface &server,
ClangPchManager::PchManagerClient &pchManagerClient,
ClangBackEnd::FilePathCachingInterface &filePathCache,
ClangBackEnd::ProjectPartsStorageInterface &projectPartsStorage,
ClangPchManager::ClangIndexingSettingsManager &settingsManager);
private:
void abstractEditorUpdated(const QString &filePath, const QByteArray &contents);

View File

@@ -40,8 +40,9 @@ public:
ClangPchManager::PchManagerClient &pchManagerClient,
CppTools::CppModelManagerInterface &cppModelManager,
ClangBackEnd::FilePathCachingInterface &filePathCache,
ClangBackEnd::ProjectPartsStorageInterface &projectPartsStorage)
: ClangPchManager::ProjectUpdater(server, filePathCache, projectPartsStorage)
ClangBackEnd::ProjectPartsStorageInterface &projectPartsStorage,
ClangPchManager::ClangIndexingSettingsManager &settingsManager)
: ClangPchManager::ProjectUpdater(server, filePathCache, projectPartsStorage, settingsManager)
, ClangPchManager::PchManagerNotifierInterface(pchManagerClient)
, m_cppModelManager(cppModelManager)
{

View File

@@ -630,19 +630,16 @@ void ExternalToolConfig::updateEffectiveArguments()
void ExternalToolConfig::editEnvironmentChanges()
{
bool ok;
const QString placeholderText = Utils::HostOsInfo::isWindowsHost()
? tr("PATH=C:\\dev\\bin;${PATH}")
: tr("PATH=/opt/bin:${PATH}");
const QList<Utils::EnvironmentItem> newItems =
Utils::EnvironmentDialog::getEnvironmentItems(&ok, ui->environmentLabel,
m_environment,
placeholderText);
if (!ok)
return;
m_environment = newItems;
updateEnvironmentLabel();
const auto newItems = Utils::EnvironmentDialog::getEnvironmentItems(ui->environmentLabel,
m_environment,
placeholderText);
if (newItems) {
m_environment = *newItems;
updateEnvironmentLabel();
}
}
void ExternalToolConfig::updateEnvironmentLabel()

View File

@@ -27,14 +27,14 @@
#include "../externaltool.h"
#include <utils/environmentfwd.h>
#include <QAbstractItemModel>
#include <QDialog>
#include <QWidget>
QT_FORWARD_DECLARE_CLASS(QPlainTextEdit)
namespace Utils { class EnvironmentItem; }
namespace Core {
namespace Internal {
@@ -108,9 +108,9 @@ private:
void updateEnvironmentLabel();
Ui::ExternalToolConfig *ui;
QList<Utils::EnvironmentItem> m_environment;
Utils::EnvironmentItems m_environment;
ExternalToolModel *m_model;
};
} // Internal
} // Core
} // namespace Core

View File

@@ -190,7 +190,7 @@ Environment ExternalTool::baseEnvironment() const
return Environment::systemEnvironment();
}
QList<EnvironmentItem> ExternalTool::environmentUserChanges() const
EnvironmentItems ExternalTool::environmentUserChanges() const
{
return m_environment;
}
@@ -297,7 +297,7 @@ void ExternalTool::setBaseEnvironmentProviderId(Id id)
m_baseEnvironmentProviderId = id;
}
void ExternalTool::setEnvironmentUserChanges(const QList<EnvironmentItem> &items)
void ExternalTool::setEnvironmentUserChanges(const EnvironmentItems &items)
{
m_environment = items;
}
@@ -600,10 +600,10 @@ bool ExternalToolRunner::resolve()
m_resolvedEnvironment = m_tool->baseEnvironment();
MacroExpander *expander = globalMacroExpander();
QList<EnvironmentItem> expandedEnvironment
= Utils::transform(m_tool->environmentUserChanges(), [expander](const EnvironmentItem &item) {
return EnvironmentItem(item.name, expander->expand(item.value), item.operation);
});
EnvironmentItems expandedEnvironment = Utils::transform(
m_tool->environmentUserChanges(), [expander](const EnvironmentItem &item) {
return EnvironmentItem(item.name, expander->expand(item.value), item.operation);
});
m_resolvedEnvironment.modify(expandedEnvironment);
{

View File

@@ -71,7 +71,7 @@ public:
QString workingDirectory() const;
Id baseEnvironmentProviderId() const;
Utils::Environment baseEnvironment() const;
QList<Utils::EnvironmentItem> environmentUserChanges() const;
Utils::EnvironmentItems environmentUserChanges() const;
void setFileName(const QString &fileName);
void setPreset(QSharedPointer<ExternalTool> preset);
@@ -101,7 +101,7 @@ public:
void setInput(const QString &input);
void setWorkingDirectory(const QString &workingDirectory);
void setBaseEnvironmentProviderId(Id id);
void setEnvironmentUserChanges(const QList<Utils::EnvironmentItem> &items);
void setEnvironmentUserChanges(const Utils::EnvironmentItems &items);
private:
QString m_id;
@@ -114,7 +114,7 @@ private:
QString m_input;
QString m_workingDirectory;
Id m_baseEnvironmentProviderId;
QList<Utils::EnvironmentItem> m_environment;
Utils::EnvironmentItems m_environment;
OutputHandling m_outputHandling = ShowInPane;
OutputHandling m_errorHandling = ShowInPane;
bool m_modifiesCurrentDocument = false;

View File

@@ -319,12 +319,12 @@ bool BuildConfiguration::useSystemEnvironment() const
return !m_clearSystemEnvironment;
}
QList<Utils::EnvironmentItem> BuildConfiguration::userEnvironmentChanges() const
Utils::EnvironmentItems BuildConfiguration::userEnvironmentChanges() const
{
return m_userEnvironmentChanges;
}
void BuildConfiguration::setUserEnvironmentChanges(const QList<Utils::EnvironmentItem> &diff)
void BuildConfiguration::setUserEnvironmentChanges(const Utils::EnvironmentItems &diff)
{
if (m_userEnvironmentChanges == diff)
return;

View File

@@ -62,8 +62,8 @@ public:
Utils::Environment baseEnvironment() const;
QString baseEnvironmentText() const;
Utils::Environment environment() const;
void setUserEnvironmentChanges(const QList<Utils::EnvironmentItem> &diff);
QList<Utils::EnvironmentItem> userEnvironmentChanges() const;
void setUserEnvironmentChanges(const Utils::EnvironmentItems &diff);
Utils::EnvironmentItems userEnvironmentChanges() const;
bool useSystemEnvironment() const;
void setUseSystemEnvironment(bool b);
@@ -117,7 +117,7 @@ private:
void emitBuildDirectoryChanged();
bool m_clearSystemEnvironment = false;
QList<Utils::EnvironmentItem> m_userEnvironmentChanges;
Utils::EnvironmentItems m_userEnvironmentChanges;
QList<BuildStepList *> m_stepLists;
ProjectExplorer::BaseStringAspect *m_buildDirectoryAspect = nullptr;
Utils::FilePath m_lastEmmitedBuildDirectory;

View File

@@ -62,7 +62,7 @@ void EnvironmentAspect::setBaseEnvironmentBase(int base)
}
}
void EnvironmentAspect::setUserEnvironmentChanges(const QList<Utils::EnvironmentItem> &diff)
void EnvironmentAspect::setUserEnvironmentChanges(const Utils::EnvironmentItems &diff)
{
if (m_userChanges != diff) {
m_userChanges = diff;

View File

@@ -49,8 +49,8 @@ public:
int baseEnvironmentBase() const;
void setBaseEnvironmentBase(int base);
QList<Utils::EnvironmentItem> userEnvironmentChanges() const { return m_userChanges; }
void setUserEnvironmentChanges(const QList<Utils::EnvironmentItem> &diff);
Utils::EnvironmentItems userEnvironmentChanges() const { return m_userChanges; }
void setUserEnvironmentChanges(const Utils::EnvironmentItems &diff);
void addSupportedBaseEnvironment(const QString &displayName,
const std::function<Utils::Environment()> &getter);
@@ -70,7 +70,7 @@ public:
signals:
void baseEnvironmentChanged();
void userEnvironmentChangesChanged(const QList<Utils::EnvironmentItem> &diff);
void userEnvironmentChangesChanged(const Utils::EnvironmentItems &diff);
void environmentChanged();
protected:
@@ -88,7 +88,7 @@ private:
QString displayName;
};
QList<Utils::EnvironmentItem> m_userChanges;
Utils::EnvironmentItems m_userChanges;
QList<EnvironmentModifier> m_modifiers;
QList<BaseEnvironment> m_baseEnvironments;
int m_base = -1;

View File

@@ -132,7 +132,7 @@ void EnvironmentAspectWidget::userChangesEdited()
m_ignoreChange = false;
}
void EnvironmentAspectWidget::changeUserChanges(QList<Utils::EnvironmentItem> changes)
void EnvironmentAspectWidget::changeUserChanges(Utils::EnvironmentItems changes)
{
if (m_ignoreChange)
return;

View File

@@ -61,7 +61,7 @@ private:
void baseEnvironmentSelected(int idx);
void changeBaseEnvironment();
void userChangesEdited();
void changeUserChanges(QList<Utils::EnvironmentItem> changes);
void changeUserChanges(Utils::EnvironmentItems changes);
void environmentChanged();
EnvironmentAspect *m_aspect;

View File

@@ -30,11 +30,12 @@
#include <utils/detailswidget.h>
#include <utils/environment.h>
#include <utils/environmentmodel.h>
#include <utils/environmentdialog.h>
#include <utils/environmentmodel.h>
#include <utils/headerviewstretcher.h>
#include <utils/hostosinfo.h>
#include <utils/itemviews.h>
#include <utils/namevaluevalidator.h>
#include <utils/tooltip/tooltip.h>
#include <QDir>
@@ -51,48 +52,6 @@
namespace ProjectExplorer {
class EnvironmentValidator : public QValidator
{
Q_OBJECT
public:
EnvironmentValidator(QWidget *parent, Utils::EnvironmentModel *model, QTreeView *view,
const QModelIndex &index) :
QValidator(parent), m_model(model), m_view(view), m_index(index)
{
m_hideTipTimer.setInterval(2000);
m_hideTipTimer.setSingleShot(true);
connect(&m_hideTipTimer, &QTimer::timeout,
this, [](){Utils::ToolTip::hide();});
}
QValidator::State validate(QString &in, int &pos) const override
{
Q_UNUSED(pos)
QModelIndex idx = m_model->variableToIndex(in);
if (idx.isValid() && idx != m_index)
return QValidator::Intermediate;
Utils::ToolTip::hide();
m_hideTipTimer.stop();
return QValidator::Acceptable;
}
void fixup(QString &input) const override
{
Q_UNUSED(input)
QPoint pos = m_view->mapToGlobal(m_view->visualRect(m_index).topLeft());
pos -= Utils::ToolTip::offsetFromPosition();
Utils::ToolTip::show(pos, tr("Variable already exists."));
m_hideTipTimer.start();
// do nothing
}
private:
Utils::EnvironmentModel *m_model;
QTreeView *m_view;
QModelIndex m_index;
mutable QTimer m_hideTipTimer;
};
class EnvironmentDelegate : public QStyledItemDelegate
{
public:
@@ -108,7 +67,8 @@ public:
return w;
if (auto edit = qobject_cast<QLineEdit *>(w))
edit->setValidator(new EnvironmentValidator(edit, m_model, m_view, index));
edit->setValidator(new Utils::NameValueValidator(
edit, m_model, m_view, index, EnvironmentWidget::tr("Variable already exists.")));
return w;
}
private:
@@ -301,12 +261,12 @@ void EnvironmentWidget::setBaseEnvironmentText(const QString &text)
updateSummaryText();
}
QList<Utils::EnvironmentItem> EnvironmentWidget::userChanges() const
Utils::EnvironmentItems EnvironmentWidget::userChanges() const
{
return d->m_model->userChanges();
}
void EnvironmentWidget::setUserChanges(const QList<Utils::EnvironmentItem> &list)
void EnvironmentWidget::setUserChanges(const Utils::EnvironmentItems &list)
{
d->m_model->setUserChanges(list);
updateSummaryText();
@@ -320,7 +280,7 @@ void EnvironmentWidget::setOpenTerminalFunc(const EnvironmentWidget::OpenTermina
void EnvironmentWidget::updateSummaryText()
{
QList<Utils::EnvironmentItem> list = d->m_model->userChanges();
Utils::EnvironmentItems list = d->m_model->userChanges();
Utils::EnvironmentItem::sort(&list);
QString text;
@@ -455,14 +415,12 @@ void EnvironmentWidget::prependPathButtonClicked()
void EnvironmentWidget::batchEditEnvironmentButtonClicked()
{
const QList<Utils::EnvironmentItem> changes = d->m_model->userChanges();
const Utils::EnvironmentItems changes = d->m_model->userChanges();
bool ok;
const QList<Utils::EnvironmentItem> newChanges = Utils::EnvironmentDialog::getEnvironmentItems(&ok, this, changes);
if (!ok)
return;
const auto newChanges = Utils::EnvironmentDialog::getEnvironmentItems(this, changes);
d->m_model->setUserChanges(newChanges);
if (newChanges)
d->m_model->setUserChanges(*newChanges);
}
void EnvironmentWidget::environmentCurrentIndexChanged(const QModelIndex &current)
@@ -491,5 +449,3 @@ void EnvironmentWidget::invalidateCurrentIndex()
}
} // namespace ProjectExplorer
#include "environmentwidget.moc"

View File

@@ -27,6 +27,8 @@
#include "projectexplorer_export.h"
#include <utils/environmentfwd.h>
#include <QWidget>
#include <functional>
@@ -34,11 +36,6 @@
QT_FORWARD_DECLARE_CLASS(QModelIndex)
namespace Utils {
class Environment;
class EnvironmentItem;
} // namespace Utils
namespace ProjectExplorer {
class EnvironmentWidgetPrivate;
@@ -56,8 +53,8 @@ public:
void setBaseEnvironmentText(const QString &text);
void setBaseEnvironment(const Utils::Environment &env);
QList<Utils::EnvironmentItem> userChanges() const;
void setUserChanges(const QList<Utils::EnvironmentItem> &list);
Utils::EnvironmentItems userChanges() const;
void setUserChanges(const Utils::EnvironmentItems &list);
using OpenTerminalFunc = std::function<void(const Utils::Environment &env)>;
void setOpenTerminalFunc(const OpenTerminalFunc &func);

View File

@@ -254,7 +254,7 @@ Utils::Environment ExtraCompiler::buildEnvironment() const
if (BuildConfiguration *bc = target->activeBuildConfiguration()) {
return bc->environment();
} else {
QList<Utils::EnvironmentItem> changes =
Utils::EnvironmentItems changes =
EnvironmentKitAspect::environmentChanges(target->kit());
Utils::Environment env = Utils::Environment::systemEnvironment();
env.modify(changes);

View File

@@ -1150,7 +1150,7 @@ private:
void refresh() override
{
const QList<Utils::EnvironmentItem> changes = currentEnvironment();
const Utils::EnvironmentItems changes = currentEnvironment();
QString shortSummary = Utils::EnvironmentItem::toStringList(changes).join(QLatin1String("; "));
QFontMetrics fm(m_summaryLabel->font());
shortSummary = fm.elidedText(shortSummary, Qt::ElideRight, m_summaryLabel->width());
@@ -1159,32 +1159,29 @@ private:
void editEnvironmentChanges()
{
bool ok;
Utils::MacroExpander *expander = m_kit->macroExpander();
Utils::EnvironmentDialog::Polisher polisher = [expander](QWidget *w) {
Core::VariableChooser::addSupportForChildWidgets(w, expander);
};
QList<Utils::EnvironmentItem>
changes = Utils::EnvironmentDialog::getEnvironmentItems(&ok,
m_summaryLabel,
currentEnvironment(),
QString(),
polisher);
if (!ok)
auto changes = Utils::EnvironmentDialog::getEnvironmentItems(m_summaryLabel,
currentEnvironment(),
QString(),
polisher);
if (!changes)
return;
if (Utils::HostOsInfo::isWindowsHost()) {
const Utils::EnvironmentItem forceMSVCEnglishItem("VSLANG", "1033");
if (m_vslangCheckbox->isChecked() && changes.indexOf(forceMSVCEnglishItem) < 0)
changes.append(forceMSVCEnglishItem);
if (m_vslangCheckbox->isChecked() && changes->indexOf(forceMSVCEnglishItem) < 0)
changes->append(forceMSVCEnglishItem);
}
EnvironmentKitAspect::setEnvironmentChanges(m_kit, changes);
EnvironmentKitAspect::setEnvironmentChanges(m_kit, *changes);
}
QList<Utils::EnvironmentItem> currentEnvironment() const
Utils::EnvironmentItems currentEnvironment() const
{
QList<Utils::EnvironmentItem> changes = EnvironmentKitAspect::environmentChanges(m_kit);
Utils::EnvironmentItems changes = EnvironmentKitAspect::environmentChanges(m_kit);
if (Utils::HostOsInfo::isWindowsHost()) {
const Utils::EnvironmentItem forceMSVCEnglishItem("VSLANG", "1033");
@@ -1207,8 +1204,7 @@ private:
"just forces UTF-8 output (may vary depending on the used MSVC "
"compiler)."));
connect(m_vslangCheckbox, &QCheckBox::toggled, this, [this](bool checked) {
QList<Utils::EnvironmentItem> changes
= EnvironmentKitAspect::environmentChanges(m_kit);
Utils::EnvironmentItems changes = EnvironmentKitAspect::environmentChanges(m_kit);
const Utils::EnvironmentItem forceMSVCEnglishItem("VSLANG", "1033");
if (!checked && changes.indexOf(forceMSVCEnglishItem) >= 0)
changes.removeAll(forceMSVCEnglishItem);
@@ -1254,7 +1250,7 @@ void EnvironmentKitAspect::fix(Kit *k)
const QVariant variant = k->value(EnvironmentKitAspect::id());
if (!variant.isNull() && !variant.canConvert(QVariant::List)) {
qWarning("Kit \"%s\" has a wrong environment value set.", qPrintable(k->displayName()));
setEnvironmentChanges(k, QList<Utils::EnvironmentItem>());
setEnvironmentChanges(k, Utils::EnvironmentItems());
}
}
@@ -1283,14 +1279,14 @@ Core::Id EnvironmentKitAspect::id()
return "PE.Profile.Environment";
}
QList<Utils::EnvironmentItem> EnvironmentKitAspect::environmentChanges(const Kit *k)
Utils::EnvironmentItems EnvironmentKitAspect::environmentChanges(const Kit *k)
{
if (k)
return Utils::EnvironmentItem::fromStringList(k->value(EnvironmentKitAspect::id()).toStringList());
return QList<Utils::EnvironmentItem>();
return Utils::EnvironmentItems();
}
void EnvironmentKitAspect::setEnvironmentChanges(Kit *k, const QList<Utils::EnvironmentItem> &changes)
void EnvironmentKitAspect::setEnvironmentChanges(Kit *k, const Utils::EnvironmentItems &changes)
{
if (k)
k->setValue(EnvironmentKitAspect::id(), Utils::EnvironmentItem::toStringList(changes));

View File

@@ -186,8 +186,8 @@ public:
ItemList toUserOutput(const Kit *k) const override;
static Core::Id id();
static QList<Utils::EnvironmentItem> environmentChanges(const Kit *k);
static void setEnvironmentChanges(Kit *k, const QList<Utils::EnvironmentItem> &changes);
static Utils::EnvironmentItems environmentChanges(const Kit *k);
static void setEnvironmentChanges(Kit *k, const Utils::EnvironmentItems &changes);
};
} // namespace ProjectExplorer

View File

@@ -715,7 +715,7 @@ void MsvcToolChain::environmentModifications(
const Utils::Environment inEnv = Utils::Environment::systemEnvironment();
Utils::Environment outEnv;
QMap<QString, QString> envPairs;
QList<Utils::EnvironmentItem> diff;
Utils::EnvironmentItems diff;
Utils::optional<QString> error = generateEnvironmentSettings(inEnv,
vcvarsBat,
varsBatArg,
@@ -764,7 +764,7 @@ void MsvcToolChain::initEnvModWatcher(const QFuture<GenerateEnvResult> &future)
m_envModWatcher.setFuture(future);
}
void MsvcToolChain::updateEnvironmentModifications(QList<Utils::EnvironmentItem> modifications)
void MsvcToolChain::updateEnvironmentModifications(Utils::EnvironmentItems modifications)
{
Utils::EnvironmentItem::sort(&modifications);
if (modifications != m_environmentModifications) {

View File

@@ -28,6 +28,7 @@
#include "abi.h"
#include "abiwidget.h"
#include "toolchain.h"
#include "toolchaincache.h"
#include "toolchainconfigwidget.h"
#include <QFutureWatcher>
@@ -142,7 +143,7 @@ protected:
struct GenerateEnvResult
{
Utils::optional<QString> error;
QList<Utils::EnvironmentItem> environmentItems;
Utils::EnvironmentItems environmentItems;
};
static void environmentModifications(QFutureInterface<GenerateEnvResult> &future,
QString vcvarsBat,
@@ -154,11 +155,11 @@ protected:
mutable HeaderPaths m_headerPaths;
private:
void updateEnvironmentModifications(QList<Utils::EnvironmentItem> modifications);
void updateEnvironmentModifications(Utils::EnvironmentItems modifications);
void rescanForCompiler();
void detectInstalledAbis();
mutable QList<Utils::EnvironmentItem> m_environmentModifications;
mutable Utils::EnvironmentItems m_environmentModifications;
mutable QFutureWatcher<GenerateEnvResult> m_envModWatcher;
mutable Utils::Environment m_lastEnvironment; // Last checked 'incoming' environment.

View File

@@ -98,7 +98,7 @@ bool QmlProjectItem::matchesFile(const QString &filePath) const
return false;
}
QList<Utils::EnvironmentItem> QmlProjectItem::environment() const
Utils::EnvironmentItems QmlProjectItem::environment() const
{
return m_environment;
}

View File

@@ -65,7 +65,7 @@ public:
void appendContent(QmlProjectContentItem *item) { m_content.append(item); }
QList<Utils::EnvironmentItem> environment() const;
Utils::EnvironmentItems environment() const;
void addToEnviroment(const QString &key, const QString &value);
signals:
@@ -77,7 +77,7 @@ protected:
QStringList m_importPaths;
QStringList m_fileSelectors;
QString m_mainFile;
QList<Utils::EnvironmentItem> m_environment;
Utils::EnvironmentItems m_environment;
QList<QmlProjectContentItem *> m_content; // content property
};

View File

@@ -206,7 +206,7 @@ Utils::FilePath QmlProject::targetFile(const Utils::FilePath &sourceFile,
return Utils::FilePath::fromString(QDir::cleanPath(targetDir.absoluteFilePath(relative)));
}
QList<Utils::EnvironmentItem> QmlProject::environment() const
Utils::EnvironmentItems QmlProject::environment() const
{
if (m_projectItem)
return m_projectItem.data()->environment();

View File

@@ -69,7 +69,7 @@ public:
Utils::FilePath targetFile(const Utils::FilePath &sourceFile,
const ProjectExplorer::Target *target) const;
QList<Utils::EnvironmentItem> environment() const;
Utils::EnvironmentItems environment() const;
QStringList customImportPaths() const;
QStringList customFileSelectors() const;

View File

@@ -107,7 +107,7 @@ FilePath QnxConfiguration::qccCompilerPath() const
return m_qccCompiler;
}
QList<EnvironmentItem> QnxConfiguration::qnxEnv() const
EnvironmentItems QnxConfiguration::qnxEnv() const
{
return m_qnxEnv;
}

View File

@@ -61,7 +61,7 @@ public:
Utils::FilePath qnxTarget() const;
Utils::FilePath qnxHost() const;
Utils::FilePath qccCompilerPath() const;
QList<Utils::EnvironmentItem> qnxEnv() const;
Utils::EnvironmentItems qnxEnv() const;
QnxVersionNumber version() const;
QVariantMap toMap() const;
@@ -97,7 +97,7 @@ private:
Utils::FilePath m_qnxTarget;
Utils::FilePath m_qnxHost;
Utils::FilePath m_qccCompiler;
QList<Utils::EnvironmentItem> m_qnxEnv;
Utils::EnvironmentItems m_qnxEnv;
QnxVersionNumber m_version;
class Target

View File

@@ -184,7 +184,7 @@ void QnxQtVersion::updateEnvironment() const
}
}
QList<Utils::EnvironmentItem> QnxQtVersion::environment() const
Utils::EnvironmentItems QnxQtVersion::environment() const
{
return QnxUtils::qnxEnvironment(sdpPath());
}

View File

@@ -73,13 +73,13 @@ protected:
private:
void updateEnvironment() const;
QList<Utils::EnvironmentItem> environment() const;
Utils::EnvironmentItems environment() const;
QString m_sdpPath;
mutable QString m_cpuDir;
mutable bool m_environmentUpToDate = false;
mutable QList<Utils::EnvironmentItem> m_qnxEnv;
mutable Utils::EnvironmentItems m_qnxEnv;
};
class QnxQtVersionFactory : public QtSupport::QtVersionFactory

View File

@@ -50,7 +50,7 @@ static Abis detectTargetAbis(const FilePath &sdpPath)
FilePath qnxTarget;
if (!sdpPath.fileName().isEmpty()) {
QList<Utils::EnvironmentItem> environment = QnxUtils::qnxEnvironment(sdpPath.toString());
Utils::EnvironmentItems environment = QnxUtils::qnxEnvironment(sdpPath.toString());
foreach (const Utils::EnvironmentItem &item, environment) {
if (item.name == QLatin1Literal("QNX_TARGET"))
qnxTarget = FilePath::fromString(item.value);
@@ -72,12 +72,11 @@ static Abis detectTargetAbis(const FilePath &sdpPath)
return result;
}
static void setQnxEnvironment(Environment &env, const QList<EnvironmentItem> &qnxEnv)
static void setQnxEnvironment(Environment &env, const EnvironmentItems &qnxEnv)
{
// We only need to set QNX_HOST and QNX_TARGET needed when running qcc
foreach (const EnvironmentItem &item, qnxEnv) {
if (item.name == QLatin1String("QNX_HOST") ||
item.name == QLatin1String("QNX_TARGET") )
if (item.name == QLatin1String("QNX_HOST") || item.name == QLatin1String("QNX_TARGET"))
env.set(item.name, item.value);
}
}

View File

@@ -73,9 +73,9 @@ QString QnxUtils::cpuDirShortDescription(const QString &cpuDir)
return cpuDir;
}
QList<Utils::EnvironmentItem> QnxUtils::qnxEnvironmentFromEnvFile(const QString &fileName)
Utils::EnvironmentItems QnxUtils::qnxEnvironmentFromEnvFile(const QString &fileName)
{
QList <Utils::EnvironmentItem> items;
Utils::EnvironmentItems items;
if (!QFileInfo::exists(fileName))
return items;
@@ -206,7 +206,7 @@ QList<ConfigInstallInformation> QnxUtils::installedConfigs(const QString &config
return sdpList;
}
QList<Utils::EnvironmentItem> QnxUtils::qnxEnvironment(const QString &sdpPath)
Utils::EnvironmentItems QnxUtils::qnxEnvironment(const QString &sdpPath)
{
return qnxEnvironmentFromEnvFile(envFilePath(sdpPath));
}

View File

@@ -70,11 +70,11 @@ class QnxUtils
public:
static QString addQuotes(const QString &string);
static QString cpuDirShortDescription(const QString &cpuDir);
static QList<Utils::EnvironmentItem> qnxEnvironmentFromEnvFile(const QString &fileName);
static Utils::EnvironmentItems qnxEnvironmentFromEnvFile(const QString &fileName);
static QString envFilePath(const QString &sdpPath);
static QString defaultTargetVersion(const QString &sdpPath);
static QList<ConfigInstallInformation> installedConfigs(const QString &configPath = QString());
static QList<Utils::EnvironmentItem> qnxEnvironment(const QString &sdpPath);
static Utils::EnvironmentItems qnxEnvironment(const QString &sdpPath);
static QList<QnxTarget> findTargets(const Utils::FilePath &basePath);
static ProjectExplorer::Abi convertAbi(const ProjectExplorer::Abi &abi);
static ProjectExplorer::Abis convertAbis(const ProjectExplorer::Abis &abis);

View File

@@ -34,7 +34,7 @@ const char DISPLAY_KEY[] = "DISPLAY";
const char VERSION_KEY[] = "RemoteLinux.EnvironmentAspect.Version";
const int ENVIRONMENTASPECT_VERSION = 1; // Version was introduced in 4.3 with the value 1
static bool displayAlreadySet(const QList<Utils::EnvironmentItem> &changes)
static bool displayAlreadySet(const Utils::EnvironmentItems &changes)
{
return Utils::contains(changes, [](const Utils::EnvironmentItem &item) {
return item.name == DISPLAY_KEY;

View File

@@ -102,6 +102,7 @@ newFrontendActionFactory(Factory *consumerFactory,
bool BeginInvocation(clang::CompilerInstance &compilerInstance) override
{
compilerInstance.getPreprocessorOpts().AllowPCHWithCompilerErrors = true;
compilerInstance.getDiagnosticOpts().ErrorLimit = 1;
return clang::ASTFrontendAction::BeginInvocation(compilerInstance);
}

View File

@@ -33,6 +33,8 @@ SOURCES += \
$$UTILS/environment.cpp \
$$UTILS/fileutils.cpp \
$$UTILS/hostosinfo.cpp \
$$UTILS/namevaluedictionary.cpp \
$$UTILS/namevalueitem.cpp \
$$UTILS/persistentsettings.cpp \
$$UTILS/qtcassert.cpp \
$$UTILS/qtcprocess.cpp \
@@ -63,6 +65,8 @@ HEADERS += \
$$UTILS/environment.h \
$$UTILS/fileutils.h \
$$UTILS/hostosinfo.h \
$$UTILS/namevaluedictionary.h \
$$UTILS/namevalueitem.h \
$$UTILS/persistentsettings.h \
$$UTILS/qtcassert.h \
$$UTILS/qtcprocess.h \

View File

@@ -43,6 +43,10 @@ public:
Target *activeTarget() const { return {}; }
QVariant namedSettings(const QString &name) const { return settings.at(name); }
void setNamedSettings(const QString &name, const QVariant &value) { settings[name] = value; }
Utils::FileName rootProjectDirectoryPath;
std::map<QString, QVariant> settings;
};
} // namespace ProjectExplorer

View File

@@ -0,0 +1,83 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "googletest.h"
#include <clangindexingsettingsmanager.h>
#include <projectexplorer/project.h>
namespace {
class ClangIndexingSettingsManager : public testing::Test
{
protected:
ClangPchManager::ClangIndexingSettingsManager manager;
ProjectExplorer::Project project;
ProjectExplorer::Project project2;
};
TEST_F(ClangIndexingSettingsManager, FetchSettings)
{
auto setting = manager.settings(&project);
ASSERT_THAT(setting, Not(IsNull()));
}
TEST_F(ClangIndexingSettingsManager, SettingsAreTheSameForTheSameProject)
{
auto setting1 = manager.settings(&project);
auto setting2 = manager.settings(&project);
ASSERT_THAT(setting1, Eq(setting2));
}
TEST_F(ClangIndexingSettingsManager, SettingsAreTheDifferentForDifferentProjects)
{
manager.settings(&project);
manager.settings(&project2);
auto setting1 = manager.settings(&project);
auto setting2 = manager.settings(&project2);
ASSERT_THAT(setting1, Not(Eq(setting2)));
}
TEST_F(ClangIndexingSettingsManager, RemoveSettings)
{
manager.settings(&project);
manager.remove(&project);
ASSERT_FALSE(manager.hasSettings(&project));
}
TEST_F(ClangIndexingSettingsManager, RemoveNonExistingSettings)
{
manager.remove(&project);
ASSERT_FALSE(manager.hasSettings(&project));
}
} // namespace

View File

@@ -34,10 +34,11 @@
#include <pchmanagerclient.h>
#include <pchmanagerprojectupdater.h>
#include <clangindexingsettingsmanager.h>
#include <filepathcaching.h>
#include <refactoringdatabaseinitializer.h>
#include <precompiledheadersupdatedmessage.h>
#include <progressmessage.h>
#include <refactoringdatabaseinitializer.h>
#include <removegeneratedfilesmessage.h>
#include <removeprojectpartsmessage.h>
#include <updategeneratedfilesmessage.h>
@@ -60,10 +61,12 @@ protected:
Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory};
ClangBackEnd::RefactoringDatabaseInitializer<Sqlite::Database> initializer{database};
ClangBackEnd::FilePathCaching filePathCache{database};
ClangPchManager::ClangIndexingSettingsManager settingsManager;
ClangPchManager::PchManagerProjectUpdater projectUpdater{mockPchManagerServer,
client,
filePathCache,
mockProjectPartsStorage};
mockProjectPartsStorage,
settingsManager};
ClangBackEnd::ProjectPartId projectPartId{1};
ClangBackEnd::FilePath pchFilePath{"/path/to/pch"};
PrecompiledHeadersUpdatedMessage message{{{projectPartId, pchFilePath.clone(), 1}}};

View File

@@ -0,0 +1,71 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "googletest.h"
#include <preprocessormacrocollector.h>
namespace {
using ProjectExplorer::Macros;
using ProjectExplorer::MacroType;
using PM = ClangPchManager::PreprocessorMacro;
class PreprocessorMacrosManager : public testing::Test
{
protected:
Macros macros1{{"yi", "1"}, {"er", "2"}};
Macros macros2{{"san", "3"}, {"se", "4"}};
Macros macrosWithDuplicates{{"yi", "1"}, {"san", "3"}, {"se", "4"}, {"er", "0"}};
ClangPchManager::PreprocessorMacroCollector manager;
};
TEST_F(PreprocessorMacrosManager, Add)
{
manager.add(macros1);
ASSERT_THAT(manager.macros(), ElementsAre(PM{"er", "2"}, PM{"yi", "1"}));
}
TEST_F(PreprocessorMacrosManager, AddMore)
{
manager.add(macros1);
manager.add(macros2);
ASSERT_THAT(manager.macros(),
ElementsAre(PM{"er", "2"}, PM{"san", "3"}, PM{"se", "4"}, PM{"yi", "1"}));
}
TEST_F(PreprocessorMacrosManager, FilterDuplicates)
{
manager.add(macros1);
manager.add(macrosWithDuplicates);
ASSERT_THAT(manager.macros(),
ElementsAre(PM{"er", "0"}, PM{"er", "2"}, PM{"san", "3"}, PM{"se", "4"}, PM{"yi", "1"}));
}
} // namespace

View File

@@ -35,6 +35,8 @@
#include <pchmanagerprojectupdater.h>
#include <clangindexingprojectsettings.h>
#include <clangindexingsettingsmanager.h>
#include <filepathcaching.h>
#include <pchmanagerclient.h>
#include <precompiledheaderstorage.h>
@@ -52,6 +54,7 @@
#include <projectexplorer/projectexplorerconstants.h>
#include <utils/algorithm.h>
#include <utils/namevalueitem.h>
namespace {
@@ -94,7 +97,7 @@ protected:
projectPart.files.push_back(source2ProjectFile);
projectPart.files.push_back(nonActiveProjectFile);
projectPart.displayName = "projectb";
projectPart.projectMacros = {{"FOO", "2"}, {"BAR", "1"}};
projectPart.projectMacros = {{"FOO", "2"}, {"BAR", "1"}, {"POO", "3"}};
projectPartId = projectPartsStorage.fetchProjectPartId(Utils::SmallString{projectPart.id()});
projectPart2.project = &project;
@@ -138,6 +141,9 @@ protected:
Utils::Language::Cxx,
Utils::LanguageVersion::LatestCxx,
Utils::LanguageExtension::None};
auto settings = settingsManager.settings(&project);
settings->saveMacros({{"POO", "3", Utils::NameValueItem::Unset}});
}
protected:
@@ -151,7 +157,11 @@ protected:
mockDependencyCreationProgressManager};
MockPchManagerNotifier mockPchManagerNotifier{pchManagerClient};
NiceMock<MockPchManagerServer> mockPchManagerServer;
ClangPchManager::ProjectUpdater updater{mockPchManagerServer, filePathCache, projectPartsStorage};
ClangPchManager::ClangIndexingSettingsManager settingsManager;
ClangPchManager::ProjectUpdater updater{mockPchManagerServer,
filePathCache,
projectPartsStorage,
settingsManager};
ClangBackEnd::ProjectPartId projectPartId;
ClangBackEnd::ProjectPartId projectPartId2;
Utils::PathStringVector headerPaths = {"/path/to/header1.h", "/path/to/header2.h"};
@@ -243,10 +253,12 @@ TEST_F(ProjectUpdater, CallRemoveProjectParts)
TEST_F(ProjectUpdater, CallPrecompiledHeaderRemovedInPchManagerProjectUpdater)
{
ClangPchManager::ClangIndexingSettingsManager settingManager;
ClangPchManager::PchManagerProjectUpdater pchUpdater{mockPchManagerServer,
pchManagerClient,
filePathCache,
projectPartsStorage};
projectPartsStorage,
settingManager};
ClangBackEnd::RemoveProjectPartsMessage message{{projectPartId, projectPartId2}};
EXPECT_CALL(mockPchManagerNotifier, precompiledHeaderRemoved(projectPartId));
@@ -283,9 +295,11 @@ TEST_F(ProjectUpdater, CallStorageInsideTransaction)
MockProjectPartsStorage mockProjectPartsStorage;
ON_CALL(mockProjectPartsStorage, transactionBackend())
.WillByDefault(ReturnRef(mockSqliteTransactionBackend));
ClangPchManager::ClangIndexingSettingsManager settingsManager;
ClangPchManager::ProjectUpdater updater{mockPchManagerServer,
filePathCache,
mockProjectPartsStorage};
mockProjectPartsStorage,
settingsManager};
EXPECT_CALL(mockProjectPartsStorage, fetchProjectPartId(Eq(projectPartName)));
@@ -303,7 +317,7 @@ TEST_F(ProjectUpdater, CreateSortedExcludedPaths)
TEST_F(ProjectUpdater, CreateSortedCompilerMacros)
{
auto paths = updater.createCompilerMacros({{"DEFINE", "1"}, {"FOO", "2"}, {"BAR", "1"}});
auto paths = updater.createCompilerMacros({{"DEFINE", "1"}, {"FOO", "2"}, {"BAR", "1"}}, {});
ASSERT_THAT(paths, ElementsAre(CompilerMacro{"BAR", "1", 1},
CompilerMacro{"FOO", "2", 2},
@@ -312,12 +326,28 @@ TEST_F(ProjectUpdater, CreateSortedCompilerMacros)
TEST_F(ProjectUpdater, FilterCompilerMacros)
{
auto paths = updater.createCompilerMacros(
{{"DEFINE", "1"}, {"QT_TESTCASE_BUILDDIR", "2"}, {"BAR", "1"}});
auto paths = updater.createCompilerMacros({{"DEFINE", "1"},
{"QT_TESTCASE_BUILDDIR", "2"},
{"BAR", "1"}},
{});
ASSERT_THAT(paths, ElementsAre(CompilerMacro{"BAR", "1", 1}, CompilerMacro{"DEFINE", "1", 3}));
}
TEST_F(ProjectUpdater, FilterSettingsMacros)
{
auto paths = updater.createCompilerMacros({{"YI", "1"}, {"SAN", "3"}, {"SE", "4"}, {"WU", "5"}},
{{"SE", "44", Utils::NameValueItem::Unset},
{"ER", "2", Utils::NameValueItem::Set},
{"WU", "5", Utils::NameValueItem::Unset}});
ASSERT_THAT(paths,
ElementsAre(CompilerMacro{"ER", "2", 3},
CompilerMacro{"SE", "4", 3},
CompilerMacro{"YI", "1", 1},
CompilerMacro{"SAN", "3", 2}));
}
TEST_F(ProjectUpdater, CreateSortedIncludeSearchPaths)
{
CppTools::ProjectPart projectPart;

View File

@@ -32,10 +32,11 @@
#include <sqlitedatabase.h>
#include <clangindexingsettingsmanager.h>
#include <clangrefactoringservermessages.h>
#include <filepathcaching.h>
#include <precompiledheadersupdatedmessage.h>
#include <refactoringdatabaseinitializer.h>
#include <clangrefactoringservermessages.h>
#include <pchmanagerclient.h>
@@ -84,11 +85,13 @@ protected:
mockDependencyCreationProgressManager};
MockCppModelManager mockCppModelManager;
ProjectExplorer::Project project;
ClangPchManager::ClangIndexingSettingsManager settingsManager;
ClangRefactoring::RefactoringProjectUpdater updater{mockRefactoringServer,
pchManagerClient,
mockCppModelManager,
filePathCache,
mockProjectPartsStorage};
mockProjectPartsStorage,
settingsManager};
Utils::SmallString projectPartId;
};

View File

@@ -47,6 +47,7 @@ DEFINES += CPPTOOLS_JSON=\"R\\\"xxx($${cpptoolsjson.output})xxx\\\"\"
SOURCES += \
changedfilepathcompressor-test.cpp \
clangindexingsettingsmanager-test.cpp \
clangpathwatcher-test.cpp \
clangqueryexamplehighlightmarker-test.cpp \
clangqueryhighlightmarker-test.cpp \
@@ -67,6 +68,7 @@ SOURCES += \
pchmanagerclientserverinprocess-test.cpp \
pchmanagerclient-test.cpp \
pchmanagerserver-test.cpp \
preprocessormacrocollector-test.cpp \
processevents-utilities.cpp \
projectpartsmanager-test.cpp \
projectpartsstorage-test.cpp \