diff options
author | Marco Bubke <marco.bubke@qt.io> | 2019-05-07 16:51:22 +0200 |
---|---|---|
committer | Marco Bubke <marco.bubke@qt.io> | 2019-06-13 16:51:48 +0000 |
commit | 4bae5de36b530bbef06ec7cb1fbfb7453fd59dc7 (patch) | |
tree | e8631b82915aef669fd63e6b0a72a1088185fa7e | |
parent | f636f06b458782289340a61e42e6bbc1e523f937 (diff) |
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>
84 files changed, 2971 insertions, 1178 deletions
diff --git a/src/libs/utils/CMakeLists.txt b/src/libs/utils/CMakeLists.txt index 0cbbeadfc1..127a41960f 100644 --- a/src/libs/utils/CMakeLists.txt +++ b/src/libs/utils/CMakeLists.txt @@ -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 diff --git a/src/libs/utils/environment.cpp b/src/libs/utils/environment.cpp index d61a75ebf7..476df9c0f9 100644 --- a/src/libs/utils/environment.cpp +++ b/src/libs/utils/environment.cpp @@ -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,27 +73,21 @@ QProcessEnvironment Environment::toProcessEnvironment() const return result; } -void Environment::set(const QString &key, const QString &value) +void Environment::appendOrSetPath(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; + appendOrSet("PATH", QDir::toNativeSeparators(value), + QString(OsSpecificAspects::pathListSeparator(m_osType))); } -void Environment::unset(const QString &key) +void Environment::prependOrSetPath(const QString &value) { - QTC_ASSERT(!key.contains('='), return); - auto it = findKey(m_values, m_osType, key); - if (it != m_values.end()) - m_values.erase(it); + prependOrSet("PATH", QDir::toNativeSeparators(value), + QString(OsSpecificAspects::pathListSeparator(m_osType))); } void Environment::appendOrSet(const QString &key, const QString &value, const QString &sep) { - QTC_ASSERT(!key.contains('='), return); + QTC_ASSERT(!key.contains('='), return ); auto it = findKey(m_values, m_osType, key); if (it == m_values.end()) { m_values.insert(key, value); @@ -296,9 +99,9 @@ void Environment::appendOrSet(const QString &key, const QString &value, const QS } } -void Environment::prependOrSet(const QString&key, const QString &value, const QString &sep) +void Environment::prependOrSet(const QString &key, const QString &value, const QString &sep) { - QTC_ASSERT(!key.contains('='), return); + QTC_ASSERT(!key.contains('='), return ); auto it = findKey(m_values, m_osType, key); if (it == m_values.end()) { m_values.insert(key, value); @@ -310,18 +113,6 @@ void Environment::prependOrSet(const QString&key, const QString &value, const QS } } -void Environment::appendOrSetPath(const QString &value) -{ - appendOrSet("PATH", QDir::toNativeSeparators(value), - QString(OsSpecificAspects::pathListSeparator(m_osType))); -} - -void Environment::prependOrSetPath(const QString &value) -{ - prependOrSet("PATH", QDir::toNativeSeparators(value), - QString(OsSpecificAspects::pathListSeparator(m_osType))); -} - 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); } diff --git a/src/libs/utils/environment.h b/src/libs/utils/environment.h index f1e957ab6d..10ad75165e 100644 --- a/src/libs/utils/environment.h +++ b/src/libs/utils/environment.h @@ -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 diff --git a/src/libs/utils/environmentdialog.cpp b/src/libs/utils/environmentdialog.cpp index 07e6155c7d..e9c9fd620f 100644 --- a/src/libs/utils/environmentdialog.cpp +++ b/src/libs/utils/environmentdialog.cpp @@ -35,144 +35,19 @@ namespace Utils { -namespace Internal { - -static QList<EnvironmentItem> cleanUp( - const QList<EnvironmentItem> &items) -{ - 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 +Utils::optional<EnvironmentItems> EnvironmentDialog::getEnvironmentItems( + QWidget *parent, const EnvironmentItems &initial, const QString &placeholderText, Polisher polisher) { - 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" diff --git a/src/libs/utils/environmentdialog.h b/src/libs/utils/environmentdialog.h index be8218508b..6dbb38e191 100644 --- a/src/libs/utils/environmentdialog.h +++ b/src/libs/utils/environmentdialog.h @@ -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 diff --git a/src/libs/utils/environmentfwd.h b/src/libs/utils/environmentfwd.h new file mode 100644 index 0000000000..04a57418b5 --- /dev/null +++ b/src/libs/utils/environmentfwd.h @@ -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 diff --git a/src/libs/utils/environmentmodel.cpp b/src/libs/utils/environmentmodel.cpp index 045a6d2b65..c7efcce930 100644 --- a/src/libs/utils/environmentmodel.cpp +++ b/src/libs/utils/environmentmodel.cpp @@ -33,360 +33,12 @@ #include <QFont> namespace Utils { -namespace Internal { - -class EnvironmentModelPrivate -{ -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; -} - -QString EnvironmentModel::indexToVariable(const QModelIndex &index) const +const Environment &EnvironmentModel::baseEnvironment() const { - return d->m_resultEnvironment.key(d->m_resultEnvironment.constBegin() + index.row()); + return static_cast<const Environment &>(baseNameValueDictionary()); } - void EnvironmentModel::setBaseEnvironment(const Environment &env) { - if (d->m_baseEnvironment == env) - return; - beginResetModel(); - d->m_baseEnvironment = env; - d->updateResultEnvironment(); - endResetModel(); -} - -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"); + setBaseNameValueDictionary(env); } - -/// ***************** -/// 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 diff --git a/src/libs/utils/environmentmodel.h b/src/libs/utils/environmentmodel.h index db20224651..ac5f017448 100644 --- a/src/libs/utils/environmentmodel.h +++ b/src/libs/utils/environmentmodel.h @@ -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 diff --git a/src/libs/utils/namevaluedictionary.cpp b/src/libs/utils/namevaluedictionary.cpp new file mode 100644 index 0000000000..30e3c18448 --- /dev/null +++ b/src/libs/utils/namevaluedictionary.cpp @@ -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 diff --git a/src/libs/utils/namevaluedictionary.h b/src/libs/utils/namevaluedictionary.h new file mode 100644 index 0000000000..112d6b094c --- /dev/null +++ b/src/libs/utils/namevaluedictionary.h @@ -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 diff --git a/src/libs/utils/namevalueitem.cpp b/src/libs/utils/namevalueitem.cpp new file mode 100644 index 0000000000..6be972201c --- /dev/null +++ b/src/libs/utils/namevalueitem.cpp @@ -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 diff --git a/src/libs/utils/namevalueitem.h b/src/libs/utils/namevalueitem.h new file mode 100644 index 0000000000..3ed4cc3cb3 --- /dev/null +++ b/src/libs/utils/namevalueitem.h @@ -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 diff --git a/src/libs/utils/namevaluemodel.cpp b/src/libs/utils/namevaluemodel.cpp new file mode 100644 index 0000000000..2a310d6b61 --- /dev/null +++ b/src/libs/utils/namevaluemodel.cpp @@ -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 diff --git a/src/libs/utils/namevaluemodel.h b/src/libs/utils/namevaluemodel.h new file mode 100644 index 0000000000..96811fec68 --- /dev/null +++ b/src/libs/utils/namevaluemodel.h @@ -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 diff --git a/src/libs/utils/namevaluesdialog.cpp b/src/libs/utils/namevaluesdialog.cpp new file mode 100644 index 0000000000..ed71d8db7f --- /dev/null +++ b/src/libs/utils/namevaluesdialog.cpp @@ -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 diff --git a/src/libs/utils/namevaluesdialog.h b/src/libs/utils/namevaluesdialog.h new file mode 100644 index 0000000000..8170a99a1d --- /dev/null +++ b/src/libs/utils/namevaluesdialog.h @@ -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 diff --git a/src/libs/utils/namevaluevalidator.cpp b/src/libs/utils/namevaluevalidator.cpp new file mode 100644 index 0000000000..580a476e01 --- /dev/null +++ b/src/libs/utils/namevaluevalidator.cpp @@ -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 diff --git a/src/libs/utils/namevaluevalidator.h b/src/libs/utils/namevaluevalidator.h new file mode 100644 index 0000000000..e1a8ae13a0 --- /dev/null +++ b/src/libs/utils/namevaluevalidator.h @@ -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 diff --git a/src/libs/utils/utils-lib.pri b/src/libs/utils/utils-lib.pri index c7fef47c97..5789bc4995 100644 --- a/src/libs/utils/utils-lib.pri +++ b/src/libs/utils/utils-lib.pri @@ -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 \ diff --git a/src/plugins/autotest/testrunner.cpp b/src/plugins/autotest/testrunner.cpp index cf432870e3..cee23ef1ae 100644 --- a/src/plugins/autotest/testrunner.cpp +++ b/src/plugins/autotest/testrunner.cpp @@ -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()); diff --git a/src/plugins/clangcodemodel/clangcodemodelplugin.cpp b/src/plugins/clangcodemodel/clangcodemodelplugin.cpp index 12efff6c6e..c8f7d9f7d1 100644 --- a/src/plugins/clangcodemodel/clangcodemodelplugin.cpp +++ b/src/plugins/clangcodemodel/clangcodemodelplugin.cpp @@ -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); } diff --git a/src/plugins/clangformat/clangformatplugin.cpp b/src/plugins/clangformat/clangformatplugin.cpp index 49f4fe049f..493f19c3e2 100644 --- a/src/plugins/clangformat/clangformatplugin.cpp +++ b/src/plugins/clangformat/clangformatplugin.cpp @@ -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(); diff --git a/src/plugins/clangpchmanager/CMakeLists.txt b/src/plugins/clangpchmanager/CMakeLists.txt index 6b36a88060..a012d090c9 100644 --- a/src/plugins/clangpchmanager/CMakeLists.txt +++ b/src/plugins/clangpchmanager/CMakeLists.txt @@ -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 diff --git a/src/plugins/clangpchmanager/clangindexingprojectsettings.cpp b/src/plugins/clangpchmanager/clangindexingprojectsettings.cpp new file mode 100644 index 0000000000..d8be4be390 --- /dev/null +++ b/src/plugins/clangpchmanager/clangindexingprojectsettings.cpp @@ -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 diff --git a/src/plugins/clangpchmanager/clangindexingprojectsettings.h b/src/plugins/clangpchmanager/clangindexingprojectsettings.h new file mode 100644 index 0000000000..7571034a28 --- /dev/null +++ b/src/plugins/clangpchmanager/clangindexingprojectsettings.h @@ -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 diff --git a/src/plugins/clangpchmanager/clangindexingprojectsettingswidget.cpp b/src/plugins/clangpchmanager/clangindexingprojectsettingswidget.cpp new file mode 100644 index 0000000000..25e77b58a0 --- /dev/null +++ b/src/plugins/clangpchmanager/clangindexingprojectsettingswidget.cpp @@ -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 diff --git a/src/plugins/clangpchmanager/clangindexingprojectsettingswidget.h b/src/plugins/clangpchmanager/clangindexingprojectsettingswidget.h new file mode 100644 index 0000000000..d46d811938 --- /dev/null +++ b/src/plugins/clangpchmanager/clangindexingprojectsettingswidget.h @@ -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 diff --git a/src/plugins/clangpchmanager/clangindexingprojectsettingswidget.ui b/src/plugins/clangpchmanager/clangindexingprojectsettingswidget.ui new file mode 100644 index 0000000000..e54a2a7a91 --- /dev/null +++ b/src/plugins/clangpchmanager/clangindexingprojectsettingswidget.ui @@ -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> diff --git a/src/plugins/clangpchmanager/clangindexingsettingsmanager.cpp b/src/plugins/clangpchmanager/clangindexingsettingsmanager.cpp new file mode 100644 index 0000000000..786581d73d --- /dev/null +++ b/src/plugins/clangpchmanager/clangindexingsettingsmanager.cpp @@ -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 diff --git a/src/plugins/clangpchmanager/clangindexingsettingsmanager.h b/src/plugins/clangpchmanager/clangindexingsettingsmanager.h new file mode 100644 index 0000000000..a8399f62cf --- /dev/null +++ b/src/plugins/clangpchmanager/clangindexingsettingsmanager.h @@ -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 diff --git a/src/plugins/clangpchmanager/clangpchmanager-source.pri b/src/plugins/clangpchmanager/clangpchmanager-source.pri index c0eb7ba90a..a74d61313c 100644 --- a/src/plugins/clangpchmanager/clangpchmanager-source.pri +++ b/src/plugins/clangpchmanager/clangpchmanager-source.pri @@ -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 diff --git a/src/plugins/clangpchmanager/clangpchmanager.pro b/src/plugins/clangpchmanager/clangpchmanager.pro index e222ffd3d4..b0bc548c20 100644 --- a/src/plugins/clangpchmanager/clangpchmanager.pro +++ b/src/plugins/clangpchmanager/clangpchmanager.pro @@ -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 diff --git a/src/plugins/clangpchmanager/clangpchmanagerplugin.cpp b/src/plugins/clangpchmanager/clangpchmanagerplugin.cpp index c50a4db7b9..98e455b0ea 100644 --- a/src/plugins/clangpchmanager/clangpchmanagerplugin.cpp +++ b/src/plugins/clangpchmanager/clangpchmanagerplugin.cpp @@ -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 diff --git a/src/plugins/clangpchmanager/clangpchmanagerplugin.h b/src/plugins/clangpchmanager/clangpchmanagerplugin.h index 60c7d2b266..f472b769b4 100644 --- a/src/plugins/clangpchmanager/clangpchmanagerplugin.h +++ b/src/plugins/clangpchmanager/clangpchmanagerplugin.h @@ -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(); diff --git a/src/plugins/clangpchmanager/pchmanagerprojectupdater.h b/src/plugins/clangpchmanager/pchmanagerprojectupdater.h index fd1b98eab4..8d41e56476 100644 --- a/src/plugins/clangpchmanager/pchmanagerprojectupdater.h +++ b/src/plugins/clangpchmanager/pchmanagerprojectupdater.h @@ -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) {} diff --git a/src/plugins/clangpchmanager/preprocessormacrocollector.cpp b/src/plugins/clangpchmanager/preprocessormacrocollector.cpp new file mode 100644 index 0000000000..03b38daeff --- /dev/null +++ b/src/plugins/clangpchmanager/preprocessormacrocollector.cpp @@ -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 ¯os) +{ + PreprocessorMacros sortedMacros; + sortedMacros.reserve(macros.size()); + + for (const ProjectExplorer::Macro ¯o : 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 ¯os) +{ + 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 diff --git a/src/plugins/clangpchmanager/preprocessormacrocollector.h b/src/plugins/clangpchmanager/preprocessormacrocollector.h new file mode 100644 index 0000000000..2de2553df3 --- /dev/null +++ b/src/plugins/clangpchmanager/preprocessormacrocollector.h @@ -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 ¯os); + + const PreprocessorMacros ¯os() const; + +private: + PreprocessorMacros m_macros; +}; + +} // namespace ClangPchManager diff --git a/src/plugins/clangpchmanager/preprocessormacrowidget.cpp b/src/plugins/clangpchmanager/preprocessormacrowidget.cpp new file mode 100644 index 0000000000..19c421935b --- /dev/null +++ b/src/plugins/clangpchmanager/preprocessormacrowidget.cpp @@ -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 ¯os) +{ + 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 ¤t) +{ + 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 diff --git a/src/plugins/clangpchmanager/preprocessormacrowidget.h b/src/plugins/clangpchmanager/preprocessormacrowidget.h new file mode 100644 index 0000000000..0c4c01545c --- /dev/null +++ b/src/plugins/clangpchmanager/preprocessormacrowidget.h @@ -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 ¯os); + 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 ¤t); + 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 diff --git a/src/plugins/clangpchmanager/projectupdater.cpp b/src/plugins/clangpchmanager/projectupdater.cpp index 3d340aea88..a26ae9e203 100644 --- a/src/plugins/clangpchmanager/projectupdater.cpp +++ b/src/plugins/clangpchmanager/projectupdater.cpp @@ -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 ¯os) macros.erase(newEnd, macros.end()); } + +void updateWithSettings(ClangBackEnd::CompilerMacros ¯os, + 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), diff --git a/src/plugins/clangpchmanager/projectupdater.h b/src/plugins/clangpchmanager/projectupdater.h index 044b86f637..4c4300d194 100644 --- a/src/plugins/clangpchmanager/projectupdater.h +++ b/src/plugins/clangpchmanager/projectupdater.h @@ -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 diff --git a/src/plugins/clangpchmanager/qtcreatorprojectupdater.h b/src/plugins/clangpchmanager/qtcreatorprojectupdater.h index 1cdef70b79..143ffea655 100644 --- a/src/plugins/clangpchmanager/qtcreatorprojectupdater.h +++ b/src/plugins/clangpchmanager/qtcreatorprojectupdater.h @@ -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 } diff --git a/src/plugins/clangrefactoring/clangrefactoringplugin.cpp b/src/plugins/clangrefactoring/clangrefactoringplugin.cpp index 059eff4c78..03917ebd13 100644 --- a/src/plugins/clangrefactoring/clangrefactoringplugin.cpp +++ b/src/plugins/clangrefactoring/clangrefactoringplugin.cpp @@ -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(); diff --git a/src/plugins/clangrefactoring/qtcreatorrefactoringprojectupdater.cpp b/src/plugins/clangrefactoring/qtcreatorrefactoringprojectupdater.cpp index dea3ccfeec..2d71b7b9fb 100644 --- a/src/plugins/clangrefactoring/qtcreatorrefactoringprojectupdater.cpp +++ b/src/plugins/clangrefactoring/qtcreatorrefactoringprojectupdater.cpp @@ -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(); } diff --git a/src/plugins/clangrefactoring/qtcreatorrefactoringprojectupdater.h b/src/plugins/clangrefactoring/qtcreatorrefactoringprojectupdater.h index 85d635a3af..7fd16afa16 100644 --- a/src/plugins/clangrefactoring/qtcreatorrefactoringprojectupdater.h +++ b/src/plugins/clangrefactoring/qtcreatorrefactoringprojectupdater.h @@ -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); diff --git a/src/plugins/clangrefactoring/refactoringprojectupdater.h b/src/plugins/clangrefactoring/refactoringprojectupdater.h index a9559ef94e..643bf74667 100644 --- a/src/plugins/clangrefactoring/refactoringprojectupdater.h +++ b/src/plugins/clangrefactoring/refactoringprojectupdater.h @@ -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) { diff --git a/src/plugins/coreplugin/dialogs/externaltoolconfig.cpp b/src/plugins/coreplugin/dialogs/externaltoolconfig.cpp index 62f7030c6d..59e73c8e35 100644 --- a/src/plugins/coreplugin/dialogs/externaltoolconfig.cpp +++ b/src/plugins/coreplugin/dialogs/externaltoolconfig.cpp @@ -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() diff --git a/src/plugins/coreplugin/dialogs/externaltoolconfig.h b/src/plugins/coreplugin/dialogs/externaltoolconfig.h index 298b70c54f..386eeee38a 100644 --- a/src/plugins/coreplugin/dialogs/externaltoolconfig.h +++ b/src/plugins/coreplugin/dialogs/externaltoolconfig.h @@ -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 diff --git a/src/plugins/coreplugin/externaltool.cpp b/src/plugins/coreplugin/externaltool.cpp index 0ef88fb3fa..93a688324d 100644 --- a/src/plugins/coreplugin/externaltool.cpp +++ b/src/plugins/coreplugin/externaltool.cpp @@ -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); { diff --git a/src/plugins/coreplugin/externaltool.h b/src/plugins/coreplugin/externaltool.h index 394ee091a5..4294615cb9 100644 --- a/src/plugins/coreplugin/externaltool.h +++ b/src/plugins/coreplugin/externaltool.h @@ -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; diff --git a/src/plugins/projectexplorer/buildconfiguration.cpp b/src/plugins/projectexplorer/buildconfiguration.cpp index e267822b97..31226e5e85 100644 --- a/src/plugins/projectexplorer/buildconfiguration.cpp +++ b/src/plugins/projectexplorer/buildconfiguration.cpp @@ -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; diff --git a/src/plugins/projectexplorer/buildconfiguration.h b/src/plugins/projectexplorer/buildconfiguration.h index c9ba394946..2d260ad4a3 100644 --- a/src/plugins/projectexplorer/buildconfiguration.h +++ b/src/plugins/projectexplorer/buildconfiguration.h @@ -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; diff --git a/src/plugins/projectexplorer/environmentaspect.cpp b/src/plugins/projectexplorer/environmentaspect.cpp index 0b0eff1e37..68eff43620 100644 --- a/src/plugins/projectexplorer/environmentaspect.cpp +++ b/src/plugins/projectexplorer/environmentaspect.cpp @@ -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; diff --git a/src/plugins/projectexplorer/environmentaspect.h b/src/plugins/projectexplorer/environmentaspect.h index 6657d1dfe8..ef5ed2907e 100644 --- a/src/plugins/projectexplorer/environmentaspect.h +++ b/src/plugins/projectexplorer/environmentaspect.h @@ -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; diff --git a/src/plugins/projectexplorer/environmentaspectwidget.cpp b/src/plugins/projectexplorer/environmentaspectwidget.cpp index 0898a3f1b6..db341ab609 100644 --- a/src/plugins/projectexplorer/environmentaspectwidget.cpp +++ b/src/plugins/projectexplorer/environmentaspectwidget.cpp @@ -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; diff --git a/src/plugins/projectexplorer/environmentaspectwidget.h b/src/plugins/projectexplorer/environmentaspectwidget.h index 6ca8164f05..44acee2f61 100644 --- a/src/plugins/projectexplorer/environmentaspectwidget.h +++ b/src/plugins/projectexplorer/environmentaspectwidget.h @@ -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; diff --git a/src/plugins/projectexplorer/environmentwidget.cpp b/src/plugins/projectexplorer/environmentwidget.cpp index d1dcd488fe..673aa61cfc 100644 --- a/src/plugins/projectexplorer/environmentwidget.cpp +++ b/src/plugins/projectexplorer/environmentwidget.cpp @@ -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 ¤t) @@ -491,5 +449,3 @@ void EnvironmentWidget::invalidateCurrentIndex() } } // namespace ProjectExplorer - -#include "environmentwidget.moc" diff --git a/src/plugins/projectexplorer/environmentwidget.h b/src/plugins/projectexplorer/environmentwidget.h index 927dc9b0c1..2a7e84c024 100644 --- a/src/plugins/projectexplorer/environmentwidget.h +++ b/src/plugins/projectexplorer/environmentwidget.h @@ -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); diff --git a/src/plugins/projectexplorer/extracompiler.cpp b/src/plugins/projectexplorer/extracompiler.cpp index adcc8f180e..5c65198486 100644 --- a/src/plugins/projectexplorer/extracompiler.cpp +++ b/src/plugins/projectexplorer/extracompiler.cpp @@ -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); diff --git a/src/plugins/projectexplorer/kitinformation.cpp b/src/plugins/projectexplorer/kitinformation.cpp index 4ca258df1a..562e090806 100644 --- a/src/plugins/projectexplorer/kitinformation.cpp +++ b/src/plugins/projectexplorer/kitinformation.cpp @@ -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)); diff --git a/src/plugins/projectexplorer/kitinformation.h b/src/plugins/projectexplorer/kitinformation.h index 037b4b9394..57660db588 100644 --- a/src/plugins/projectexplorer/kitinformation.h +++ b/src/plugins/projectexplorer/kitinformation.h @@ -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 diff --git a/src/plugins/projectexplorer/msvctoolchain.cpp b/src/plugins/projectexplorer/msvctoolchain.cpp index 557a03a5d6..0343edb401 100644 --- a/src/plugins/projectexplorer/msvctoolchain.cpp +++ b/src/plugins/projectexplorer/msvctoolchain.cpp @@ -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) { diff --git a/src/plugins/projectexplorer/msvctoolchain.h b/src/plugins/projectexplorer/msvctoolchain.h index bc0adb00ba..4a2b5057a3 100644 --- a/src/plugins/projectexplorer/msvctoolchain.h +++ b/src/plugins/projectexplorer/msvctoolchain.h @@ -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. diff --git a/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.cpp b/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.cpp index e82e90fedc..a659a5dd66 100644 --- a/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.cpp +++ b/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.cpp @@ -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; } diff --git a/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.h b/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.h index 4ba9afcd5f..a6982443d9 100644 --- a/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.h +++ b/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.h @@ -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 }; diff --git a/src/plugins/qmlprojectmanager/qmlproject.cpp b/src/plugins/qmlprojectmanager/qmlproject.cpp index 5b518ccc87..ecfe48378b 100644 --- a/src/plugins/qmlprojectmanager/qmlproject.cpp +++ b/src/plugins/qmlprojectmanager/qmlproject.cpp @@ -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(); diff --git a/src/plugins/qmlprojectmanager/qmlproject.h b/src/plugins/qmlprojectmanager/qmlproject.h index b3ce817f19..c75fa83614 100644 --- a/src/plugins/qmlprojectmanager/qmlproject.h +++ b/src/plugins/qmlprojectmanager/qmlproject.h @@ -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; diff --git a/src/plugins/qnx/qnxconfiguration.cpp b/src/plugins/qnx/qnxconfiguration.cpp index d05ae172ff..11a846e612 100644 --- a/src/plugins/qnx/qnxconfiguration.cpp +++ b/src/plugins/qnx/qnxconfiguration.cpp @@ -107,7 +107,7 @@ FilePath QnxConfiguration::qccCompilerPath() const return m_qccCompiler; } -QList<EnvironmentItem> QnxConfiguration::qnxEnv() const +EnvironmentItems QnxConfiguration::qnxEnv() const { return m_qnxEnv; } diff --git a/src/plugins/qnx/qnxconfiguration.h b/src/plugins/qnx/qnxconfiguration.h index ebb77c825f..41649ce6bf 100644 --- a/src/plugins/qnx/qnxconfiguration.h +++ b/src/plugins/qnx/qnxconfiguration.h @@ -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 diff --git a/src/plugins/qnx/qnxqtversion.cpp b/src/plugins/qnx/qnxqtversion.cpp index ffddcf7811..b096f16a80 100644 --- a/src/plugins/qnx/qnxqtversion.cpp +++ b/src/plugins/qnx/qnxqtversion.cpp @@ -184,7 +184,7 @@ void QnxQtVersion::updateEnvironment() const } } -QList<Utils::EnvironmentItem> QnxQtVersion::environment() const +Utils::EnvironmentItems QnxQtVersion::environment() const { return QnxUtils::qnxEnvironment(sdpPath()); } diff --git a/src/plugins/qnx/qnxqtversion.h b/src/plugins/qnx/qnxqtversion.h index 6479954bf8..d41e19852c 100644 --- a/src/plugins/qnx/qnxqtversion.h +++ b/src/plugins/qnx/qnxqtversion.h @@ -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 diff --git a/src/plugins/qnx/qnxtoolchain.cpp b/src/plugins/qnx/qnxtoolchain.cpp index 376ae63711..30c1f9b475 100644 --- a/src/plugins/qnx/qnxtoolchain.cpp +++ b/src/plugins/qnx/qnxtoolchain.cpp @@ -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); } } diff --git a/src/plugins/qnx/qnxutils.cpp b/src/plugins/qnx/qnxutils.cpp index 9d61864558..3d34dd74bb 100644 --- a/src/plugins/qnx/qnxutils.cpp +++ b/src/plugins/qnx/qnxutils.cpp @@ -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)); } diff --git a/src/plugins/qnx/qnxutils.h b/src/plugins/qnx/qnxutils.h index ae8367e43b..63ef5dd86e 100644 --- a/src/plugins/qnx/qnxutils.h +++ b/src/plugins/qnx/qnxutils.h @@ -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); diff --git a/src/plugins/remotelinux/remotelinuxenvironmentaspect.cpp b/src/plugins/remotelinux/remotelinuxenvironmentaspect.cpp index 36d2d03e2d..4b8c4a5ad8 100644 --- a/src/plugins/remotelinux/remotelinuxenvironmentaspect.cpp +++ b/src/plugins/remotelinux/remotelinuxenvironmentaspect.cpp @@ -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; diff --git a/src/tools/clangrefactoringbackend/source/symbolscollector.cpp b/src/tools/clangrefactoringbackend/source/symbolscollector.cpp index b4bafcc8ad..1634512b50 100644 --- a/src/tools/clangrefactoringbackend/source/symbolscollector.cpp +++ b/src/tools/clangrefactoringbackend/source/symbolscollector.cpp @@ -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); } diff --git a/src/tools/sdktool/sdktool.pro b/src/tools/sdktool/sdktool.pro index 9869d50dc4..379a1b10ca 100644 --- a/src/tools/sdktool/sdktool.pro +++ b/src/tools/sdktool/sdktool.pro @@ -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 \ diff --git a/tests/unit/mockup/projectexplorer/project.h b/tests/unit/mockup/projectexplorer/project.h index e6e321ee43..3e0fef904b 100644 --- a/tests/unit/mockup/projectexplorer/project.h +++ b/tests/unit/mockup/projectexplorer/project.h @@ -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 diff --git a/tests/unit/unittest/clangindexingsettingsmanager-test.cpp b/tests/unit/unittest/clangindexingsettingsmanager-test.cpp new file mode 100644 index 0000000000..e3c4eb7c67 --- /dev/null +++ b/tests/unit/unittest/clangindexingsettingsmanager-test.cpp @@ -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 diff --git a/tests/unit/unittest/pchmanagerclient-test.cpp b/tests/unit/unittest/pchmanagerclient-test.cpp index 6c74143e4d..5f9fffbf01 100644 --- a/tests/unit/unittest/pchmanagerclient-test.cpp +++ b/tests/unit/unittest/pchmanagerclient-test.cpp @@ -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}}}; diff --git a/tests/unit/unittest/preprocessormacrocollector-test.cpp b/tests/unit/unittest/preprocessormacrocollector-test.cpp new file mode 100644 index 0000000000..94eaa01a8b --- /dev/null +++ b/tests/unit/unittest/preprocessormacrocollector-test.cpp @@ -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 diff --git a/tests/unit/unittest/projectupdater-test.cpp b/tests/unit/unittest/projectupdater-test.cpp index 960a256469..c4744148ab 100644 --- a/tests/unit/unittest/projectupdater-test.cpp +++ b/tests/unit/unittest/projectupdater-test.cpp @@ -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; diff --git a/tests/unit/unittest/refactoringprojectupdater-test.cpp b/tests/unit/unittest/refactoringprojectupdater-test.cpp index 8d5e60df91..6331fc3016 100644 --- a/tests/unit/unittest/refactoringprojectupdater-test.cpp +++ b/tests/unit/unittest/refactoringprojectupdater-test.cpp @@ -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; }; diff --git a/tests/unit/unittest/unittest.pro b/tests/unit/unittest/unittest.pro index 8e0a4d6577..5ea9ba2fef 100644 --- a/tests/unit/unittest/unittest.pro +++ b/tests/unit/unittest/unittest.pro @@ -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 \ |