forked from qt-creator/qt-creator
Enable macro editing for the Clang indexer
Refactor much of the code from Environment* classes to NameValue* classes to share it with the preprocessor macro settings. Change-Id: Ica4ee817aa338230c422b30d91240d266248d226 Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
This commit is contained in:
@@ -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
|
||||
|
@@ -39,11 +39,12 @@ Q_GLOBAL_STATIC_WITH_ARGS(Utils::Environment, staticSystemEnvironment,
|
||||
|
||||
Q_GLOBAL_STATIC(QVector<Utils::EnvironmentProvider>, environmentProviders)
|
||||
|
||||
static QMap<QString, QString>::iterator findKey(QMap<QString, QString> &input, Utils::OsType osType,
|
||||
const QString &key)
|
||||
namespace Utils {
|
||||
|
||||
static NameValueMap::iterator findKey(NameValueMap &input, Utils::OsType osType, const QString &key)
|
||||
{
|
||||
const Qt::CaseSensitivity casing
|
||||
= (osType == Utils::OsTypeWindows) ? Qt::CaseInsensitive : Qt::CaseSensitive;
|
||||
const Qt::CaseSensitivity casing = (osType == Utils::OsTypeWindows) ? Qt::CaseInsensitive
|
||||
: Qt::CaseSensitive;
|
||||
for (auto it = input.begin(); it != input.end(); ++it) {
|
||||
if (key.compare(it.key(), casing) == 0)
|
||||
return it;
|
||||
@@ -51,12 +52,12 @@ static QMap<QString, QString>::iterator findKey(QMap<QString, QString> &input, U
|
||||
return input.end();
|
||||
}
|
||||
|
||||
static QMap<QString, QString>::const_iterator findKey(const QMap<QString, QString> &input,
|
||||
Utils::OsType osType,
|
||||
const QString &key)
|
||||
static NameValueMap::const_iterator findKey(const NameValueMap &input,
|
||||
Utils::OsType osType,
|
||||
const QString &key)
|
||||
{
|
||||
const Qt::CaseSensitivity casing
|
||||
= (osType == Utils::OsTypeWindows) ? Qt::CaseInsensitive : Qt::CaseSensitive;
|
||||
const Qt::CaseSensitivity casing = (osType == Utils::OsTypeWindows) ? Qt::CaseInsensitive
|
||||
: Qt::CaseSensitive;
|
||||
for (auto it = input.constBegin(); it != input.constEnd(); ++it) {
|
||||
if (key.compare(it.key(), casing) == 0)
|
||||
return it;
|
||||
@@ -64,198 +65,6 @@ static QMap<QString, QString>::const_iterator findKey(const QMap<QString, QStrin
|
||||
return input.constEnd();
|
||||
}
|
||||
|
||||
namespace Utils {
|
||||
|
||||
enum : char
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
pathSepC = ';'
|
||||
#else
|
||||
pathSepC = ':'
|
||||
#endif
|
||||
};
|
||||
|
||||
void EnvironmentItem::sort(QList<EnvironmentItem> *list)
|
||||
{
|
||||
Utils::sort(*list, &EnvironmentItem::name);
|
||||
}
|
||||
|
||||
QList<EnvironmentItem> EnvironmentItem::fromStringList(const QStringList &list)
|
||||
{
|
||||
QList<EnvironmentItem> result;
|
||||
for (const QString &string : list) {
|
||||
int pos = string.indexOf('=', 1);
|
||||
if (pos == -1)
|
||||
result.append(EnvironmentItem(string, QString(), EnvironmentItem::Unset));
|
||||
else
|
||||
result.append(EnvironmentItem(string.left(pos), string.mid(pos + 1)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QStringList EnvironmentItem::toStringList(const QList<EnvironmentItem> &list)
|
||||
{
|
||||
return Utils::transform(list, [](const EnvironmentItem &item) {
|
||||
if (item.operation == EnvironmentItem::Unset)
|
||||
return QString(item.name);
|
||||
return QString(item.name + '=' + item.value);
|
||||
});
|
||||
}
|
||||
|
||||
QList<EnvironmentItem> EnvironmentItem::itemsFromVariantList(const QVariantList &list)
|
||||
{
|
||||
return Utils::transform(list, [](const QVariant &item) {
|
||||
return itemFromVariantList(item.toList());
|
||||
});
|
||||
}
|
||||
|
||||
QVariantList EnvironmentItem::toVariantList(const QList<EnvironmentItem> &list)
|
||||
{
|
||||
return Utils::transform(list, [](const EnvironmentItem &item) {
|
||||
return QVariant(toVariantList(item));
|
||||
});
|
||||
}
|
||||
|
||||
EnvironmentItem EnvironmentItem::itemFromVariantList(const QVariantList &list)
|
||||
{
|
||||
QTC_ASSERT(list.size() == 3, return EnvironmentItem("", ""));
|
||||
QString name = list.value(0).toString();
|
||||
Operation operation = Operation(list.value(1).toInt());
|
||||
QString value = list.value(2).toString();
|
||||
return EnvironmentItem(name, value, operation);
|
||||
}
|
||||
|
||||
QVariantList EnvironmentItem::toVariantList(const EnvironmentItem &item)
|
||||
{
|
||||
return QVariantList() << item.name << item.operation << item.value;
|
||||
}
|
||||
|
||||
static QString expand(const Environment *e, QString value)
|
||||
{
|
||||
int replaceCount = 0;
|
||||
for (int i = 0; i < value.size(); ++i) {
|
||||
if (value.at(i) == '$') {
|
||||
if ((i + 1) < value.size()) {
|
||||
const QChar &c = value.at(i+1);
|
||||
int end = -1;
|
||||
if (c == '(')
|
||||
end = value.indexOf(')', i);
|
||||
else if (c == '{')
|
||||
end = value.indexOf('}', i);
|
||||
if (end != -1) {
|
||||
const QString &name = value.mid(i + 2, end - i - 2);
|
||||
Environment::const_iterator it = e->constFind(name);
|
||||
if (it != e->constEnd())
|
||||
value.replace(i, end - i + 1, it.value());
|
||||
++replaceCount;
|
||||
QTC_ASSERT(replaceCount < 100, break);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug debug, const EnvironmentItem &i)
|
||||
{
|
||||
QDebugStateSaver saver(debug);
|
||||
debug.noquote();
|
||||
debug.nospace();
|
||||
debug << "EnvironmentItem(";
|
||||
switch (i.operation) {
|
||||
case EnvironmentItem::Set:
|
||||
debug << "set \"" << i.name << "\" to \"" << i.value << '"';
|
||||
break;
|
||||
case EnvironmentItem::Unset:
|
||||
debug << "unset \"" << i.name << '"';
|
||||
break;
|
||||
case EnvironmentItem::Prepend:
|
||||
debug << "prepend to \"" << i.name << "\":\"" << i.value << '"';
|
||||
break;
|
||||
case EnvironmentItem::Append:
|
||||
debug << "append to \"" << i.name << "\":\"" << i.value << '"';
|
||||
break;
|
||||
}
|
||||
debug << ')';
|
||||
return debug;
|
||||
}
|
||||
|
||||
void EnvironmentItem::apply(Environment *e, Operation op) const
|
||||
{
|
||||
switch (op) {
|
||||
case Set:
|
||||
e->set(name, expand(e, value));
|
||||
break;
|
||||
case Unset:
|
||||
e->unset(name);
|
||||
break;
|
||||
case Prepend: {
|
||||
const Environment::const_iterator it = e->constFind(name);
|
||||
if (it != e->constEnd()) {
|
||||
QString v = it.value();
|
||||
const QChar pathSep{QLatin1Char(pathSepC)};
|
||||
int sepCount = 0;
|
||||
if (v.startsWith(pathSep))
|
||||
++sepCount;
|
||||
if (value.endsWith(pathSep))
|
||||
++sepCount;
|
||||
if (sepCount == 2)
|
||||
v.remove(0, 1);
|
||||
else if (sepCount == 0)
|
||||
v.prepend(pathSep);
|
||||
v.prepend(expand(e, value));
|
||||
e->set(name, v);
|
||||
} else {
|
||||
apply(e, Set);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Append: {
|
||||
const Environment::const_iterator it = e->constFind(name);
|
||||
if (it != e->constEnd()) {
|
||||
QString v = it.value();
|
||||
const QChar pathSep{QLatin1Char(pathSepC)};
|
||||
int sepCount = 0;
|
||||
if (v.endsWith(pathSep))
|
||||
++sepCount;
|
||||
if (value.startsWith(pathSep))
|
||||
++sepCount;
|
||||
if (sepCount == 2)
|
||||
v.chop(1);
|
||||
else if (sepCount == 0)
|
||||
v.append(pathSep);
|
||||
v.append(expand(e, value));
|
||||
e->set(name, v);
|
||||
} else {
|
||||
apply(e, Set);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Environment::Environment(const QStringList &env, OsType osType) : m_osType(osType)
|
||||
{
|
||||
for (const QString &s : env) {
|
||||
int i = s.indexOf('=', 1);
|
||||
if (i >= 0) {
|
||||
const QString key = s.left(i);
|
||||
if (!key.contains('=')) {
|
||||
const QString value = s.mid(i + 1);
|
||||
set(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QStringList Environment::toStringList() const
|
||||
{
|
||||
QStringList result;
|
||||
for (auto it = m_values.constBegin(); it != m_values.constEnd(); ++it)
|
||||
result.append(it.key() + '=' + it.value());
|
||||
return result;
|
||||
}
|
||||
|
||||
QProcessEnvironment Environment::toProcessEnvironment() const
|
||||
{
|
||||
QProcessEnvironment result;
|
||||
@@ -264,52 +73,6 @@ QProcessEnvironment Environment::toProcessEnvironment() const
|
||||
return result;
|
||||
}
|
||||
|
||||
void Environment::set(const QString &key, const QString &value)
|
||||
{
|
||||
QTC_ASSERT(!key.contains('='), return);
|
||||
auto it = findKey(m_values, m_osType, key);
|
||||
if (it == m_values.end())
|
||||
m_values.insert(key, value);
|
||||
else
|
||||
it.value() = value;
|
||||
}
|
||||
|
||||
void Environment::unset(const QString &key)
|
||||
{
|
||||
QTC_ASSERT(!key.contains('='), return);
|
||||
auto it = findKey(m_values, m_osType, key);
|
||||
if (it != m_values.end())
|
||||
m_values.erase(it);
|
||||
}
|
||||
|
||||
void Environment::appendOrSet(const QString &key, const QString &value, const QString &sep)
|
||||
{
|
||||
QTC_ASSERT(!key.contains('='), return);
|
||||
auto it = findKey(m_values, m_osType, key);
|
||||
if (it == m_values.end()) {
|
||||
m_values.insert(key, value);
|
||||
} else {
|
||||
// Append unless it is already there
|
||||
const QString toAppend = sep + value;
|
||||
if (!it.value().endsWith(toAppend))
|
||||
it.value().append(toAppend);
|
||||
}
|
||||
}
|
||||
|
||||
void Environment::prependOrSet(const QString&key, const QString &value, const QString &sep)
|
||||
{
|
||||
QTC_ASSERT(!key.contains('='), return);
|
||||
auto it = findKey(m_values, m_osType, key);
|
||||
if (it == m_values.end()) {
|
||||
m_values.insert(key, value);
|
||||
} else {
|
||||
// Prepend unless it is already there
|
||||
const QString toPrepend = value + sep;
|
||||
if (!it.value().startsWith(toPrepend))
|
||||
it.value().prepend(toPrepend);
|
||||
}
|
||||
}
|
||||
|
||||
void Environment::appendOrSetPath(const QString &value)
|
||||
{
|
||||
appendOrSet("PATH", QDir::toNativeSeparators(value),
|
||||
@@ -322,6 +85,34 @@ void Environment::prependOrSetPath(const QString &value)
|
||||
QString(OsSpecificAspects::pathListSeparator(m_osType)));
|
||||
}
|
||||
|
||||
void Environment::appendOrSet(const QString &key, const QString &value, const QString &sep)
|
||||
{
|
||||
QTC_ASSERT(!key.contains('='), return );
|
||||
auto it = findKey(m_values, m_osType, key);
|
||||
if (it == m_values.end()) {
|
||||
m_values.insert(key, value);
|
||||
} else {
|
||||
// Append unless it is already there
|
||||
const QString toAppend = sep + value;
|
||||
if (!it.value().endsWith(toAppend))
|
||||
it.value().append(toAppend);
|
||||
}
|
||||
}
|
||||
|
||||
void Environment::prependOrSet(const QString &key, const QString &value, const QString &sep)
|
||||
{
|
||||
QTC_ASSERT(!key.contains('='), return );
|
||||
auto it = findKey(m_values, m_osType, key);
|
||||
if (it == m_values.end()) {
|
||||
m_values.insert(key, value);
|
||||
} else {
|
||||
// Prepend unless it is already there
|
||||
const QString toPrepend = value + sep;
|
||||
if (!it.value().startsWith(toPrepend))
|
||||
it.value().prepend(toPrepend);
|
||||
}
|
||||
}
|
||||
|
||||
void Environment::prependOrSetLibrarySearchPath(const QString &value)
|
||||
{
|
||||
switch (m_osType) {
|
||||
@@ -387,11 +178,6 @@ void Environment::setupEnglishOutput(QStringList *environment)
|
||||
*environment = env.toStringList();
|
||||
}
|
||||
|
||||
void Environment::clear()
|
||||
{
|
||||
m_values.clear();
|
||||
}
|
||||
|
||||
FilePath Environment::searchInDirectory(const QStringList &execs, const FilePath &directory,
|
||||
QSet<FilePath> &alreadyChecked) const
|
||||
{
|
||||
@@ -494,122 +280,7 @@ FilePathList Environment::path() const
|
||||
return Utils::transform(pathComponents, &FilePath::fromUserInput);
|
||||
}
|
||||
|
||||
QString Environment::value(const QString &key) const
|
||||
{
|
||||
const auto it = findKey(m_values, m_osType, key);
|
||||
return it != m_values.end() ? it.value() : QString();
|
||||
}
|
||||
|
||||
QString Environment::key(Environment::const_iterator it) const
|
||||
{
|
||||
return it.key();
|
||||
}
|
||||
|
||||
QString Environment::value(Environment::const_iterator it) const
|
||||
{
|
||||
return it.value();
|
||||
}
|
||||
|
||||
Environment::const_iterator Environment::constBegin() const
|
||||
{
|
||||
return m_values.constBegin();
|
||||
}
|
||||
|
||||
Environment::const_iterator Environment::constEnd() const
|
||||
{
|
||||
return m_values.constEnd();
|
||||
}
|
||||
|
||||
Environment::const_iterator Environment::constFind(const QString &name) const
|
||||
{
|
||||
return findKey(m_values, m_osType, name);
|
||||
}
|
||||
|
||||
int Environment::size() const
|
||||
{
|
||||
return m_values.size();
|
||||
}
|
||||
|
||||
void Environment::modify(const QList<EnvironmentItem> & list)
|
||||
{
|
||||
Environment resultEnvironment = *this;
|
||||
for (const EnvironmentItem &item : list)
|
||||
item.apply(&resultEnvironment);
|
||||
*this = resultEnvironment;
|
||||
}
|
||||
|
||||
QList<EnvironmentItem> Environment::diff(const Environment &other, bool checkAppendPrepend) const
|
||||
{
|
||||
QMap<QString, QString>::const_iterator thisIt = constBegin();
|
||||
QMap<QString, QString>::const_iterator otherIt = other.constBegin();
|
||||
|
||||
QList<EnvironmentItem> result;
|
||||
while (thisIt != constEnd() || otherIt != other.constEnd()) {
|
||||
if (thisIt == constEnd()) {
|
||||
result.append(EnvironmentItem(otherIt.key(), otherIt.value()));
|
||||
++otherIt;
|
||||
} else if (otherIt == other.constEnd()) {
|
||||
result.append(EnvironmentItem(thisIt.key(), QString(), EnvironmentItem::Unset));
|
||||
++thisIt;
|
||||
} else if (thisIt.key() < otherIt.key()) {
|
||||
result.append(EnvironmentItem(thisIt.key(), QString(), EnvironmentItem::Unset));
|
||||
++thisIt;
|
||||
} else if (thisIt.key() > otherIt.key()) {
|
||||
result.append(EnvironmentItem(otherIt.key(), otherIt.value()));
|
||||
++otherIt;
|
||||
} else {
|
||||
const QString &oldValue = thisIt.value();
|
||||
const QString &newValue = otherIt.value();
|
||||
if (oldValue != newValue) {
|
||||
if (checkAppendPrepend && newValue.startsWith(oldValue)) {
|
||||
QString appended = newValue.right(newValue.size() - oldValue.size());
|
||||
if (appended.startsWith(QLatin1Char(pathSepC)))
|
||||
appended.remove(0, 1);
|
||||
result.append(EnvironmentItem(otherIt.key(), appended,
|
||||
EnvironmentItem::Append));
|
||||
} else if (checkAppendPrepend && newValue.endsWith(oldValue)) {
|
||||
QString prepended = newValue.left(newValue.size() - oldValue.size());
|
||||
if (prepended.endsWith(QLatin1Char(pathSepC)))
|
||||
prepended.chop(1);
|
||||
result.append(EnvironmentItem(otherIt.key(), prepended,
|
||||
EnvironmentItem::Prepend));
|
||||
} else {
|
||||
result.append(EnvironmentItem(otherIt.key(), newValue));
|
||||
}
|
||||
}
|
||||
++otherIt;
|
||||
++thisIt;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Environment::hasKey(const QString &key) const
|
||||
{
|
||||
return m_values.contains(key);
|
||||
}
|
||||
|
||||
OsType Environment::osType() const
|
||||
{
|
||||
return m_osType;
|
||||
}
|
||||
|
||||
QString Environment::userName() const
|
||||
{
|
||||
return value(QString::fromLatin1(m_osType == OsTypeWindows ? "USERNAME" : "USER"));
|
||||
}
|
||||
|
||||
bool Environment::operator!=(const Environment &other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
bool Environment::operator==(const Environment &other) const
|
||||
{
|
||||
return m_osType == other.m_osType && m_values == other.m_values;
|
||||
}
|
||||
|
||||
void Environment::modifySystemEnvironment(const QList<EnvironmentItem> &list)
|
||||
void Environment::modifySystemEnvironment(const EnvironmentItems &list)
|
||||
{
|
||||
staticSystemEnvironment->modify(list);
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -35,144 +35,19 @@
|
||||
|
||||
namespace Utils {
|
||||
|
||||
namespace Internal {
|
||||
|
||||
static QList<EnvironmentItem> cleanUp(
|
||||
const QList<EnvironmentItem> &items)
|
||||
Utils::optional<EnvironmentItems> EnvironmentDialog::getEnvironmentItems(
|
||||
QWidget *parent, const EnvironmentItems &initial, const QString &placeholderText, Polisher polisher)
|
||||
{
|
||||
QList<EnvironmentItem> uniqueItems;
|
||||
QSet<QString> uniqueSet;
|
||||
for (int i = items.count() - 1; i >= 0; i--) {
|
||||
EnvironmentItem item = items.at(i);
|
||||
if (HostOsInfo::isWindowsHost())
|
||||
item.name = item.name.toUpper();
|
||||
const QString &itemName = item.name;
|
||||
QString emptyName = itemName;
|
||||
emptyName.remove(QLatin1Char(' '));
|
||||
if (!emptyName.isEmpty() && !uniqueSet.contains(itemName)) {
|
||||
uniqueItems.prepend(item);
|
||||
uniqueSet.insert(itemName);
|
||||
}
|
||||
}
|
||||
return uniqueItems;
|
||||
}
|
||||
|
||||
class EnvironmentItemsWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit EnvironmentItemsWidget(QWidget *parent = nullptr);
|
||||
|
||||
void setEnvironmentItems(const QList<EnvironmentItem> &items);
|
||||
QList<EnvironmentItem> environmentItems() const;
|
||||
|
||||
void setPlaceholderText(const QString &text);
|
||||
|
||||
private:
|
||||
QPlainTextEdit *m_editor;
|
||||
};
|
||||
|
||||
EnvironmentItemsWidget::EnvironmentItemsWidget(QWidget *parent) :
|
||||
QWidget(parent)
|
||||
{
|
||||
m_editor = new QPlainTextEdit(this);
|
||||
auto layout = new QVBoxLayout(this);
|
||||
layout->setMargin(0);
|
||||
layout->addWidget(m_editor);
|
||||
}
|
||||
|
||||
void EnvironmentItemsWidget::setEnvironmentItems(const QList<EnvironmentItem> &items)
|
||||
{
|
||||
QList<EnvironmentItem> sortedItems = items;
|
||||
EnvironmentItem::sort(&sortedItems);
|
||||
const QStringList list = EnvironmentItem::toStringList(sortedItems);
|
||||
m_editor->document()->setPlainText(list.join(QLatin1Char('\n')));
|
||||
}
|
||||
|
||||
QList<EnvironmentItem> EnvironmentItemsWidget::environmentItems() const
|
||||
{
|
||||
const QStringList list = m_editor->document()->toPlainText().split(QLatin1String("\n"));
|
||||
QList<EnvironmentItem> items = EnvironmentItem::fromStringList(list);
|
||||
return cleanUp(items);
|
||||
}
|
||||
|
||||
void EnvironmentItemsWidget::setPlaceholderText(const QString &text)
|
||||
{
|
||||
m_editor->setPlaceholderText(text);
|
||||
}
|
||||
|
||||
class EnvironmentDialogPrivate
|
||||
{
|
||||
public:
|
||||
EnvironmentItemsWidget *m_editor;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
EnvironmentDialog::EnvironmentDialog(QWidget *parent) :
|
||||
QDialog(parent), d(new Internal::EnvironmentDialogPrivate)
|
||||
{
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
resize(640, 480);
|
||||
d->m_editor = new Internal::EnvironmentItemsWidget(this);
|
||||
auto box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this);
|
||||
connect(box, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(box, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
|
||||
auto helpLabel = new QLabel(this);
|
||||
helpLabel->setText(tr("Enter one environment variable per line.\n"
|
||||
"To set or change a variable, use VARIABLE=VALUE.\n"
|
||||
"Existing variables can be referenced in a VALUE with ${OTHER}.\n"
|
||||
"To clear a variable, put its name on a line with nothing else on it."));
|
||||
|
||||
auto layout = new QVBoxLayout(this);
|
||||
layout->addWidget(d->m_editor);
|
||||
layout->addWidget(helpLabel);
|
||||
|
||||
layout->addWidget(box);
|
||||
|
||||
setWindowTitle(tr("Edit Environment"));
|
||||
}
|
||||
|
||||
EnvironmentDialog::~EnvironmentDialog()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
void EnvironmentDialog::setEnvironmentItems(const QList<EnvironmentItem> &items)
|
||||
{
|
||||
d->m_editor->setEnvironmentItems(items);
|
||||
}
|
||||
|
||||
QList<EnvironmentItem> EnvironmentDialog::environmentItems() const
|
||||
{
|
||||
return d->m_editor->environmentItems();
|
||||
}
|
||||
|
||||
void EnvironmentDialog::setPlaceholderText(const QString &text)
|
||||
{
|
||||
d->m_editor->setPlaceholderText(text);
|
||||
}
|
||||
|
||||
QList<EnvironmentItem> EnvironmentDialog::getEnvironmentItems(bool *ok,
|
||||
QWidget *parent,
|
||||
const QList<EnvironmentItem> &initial,
|
||||
const QString &placeholderText,
|
||||
Polisher polisher)
|
||||
{
|
||||
EnvironmentDialog dlg(parent);
|
||||
if (polisher)
|
||||
polisher(&dlg);
|
||||
dlg.setEnvironmentItems(initial);
|
||||
dlg.setPlaceholderText(placeholderText);
|
||||
bool result = dlg.exec() == QDialog::Accepted;
|
||||
if (ok)
|
||||
*ok = result;
|
||||
if (result)
|
||||
return dlg.environmentItems();
|
||||
return QList<EnvironmentItem>();
|
||||
return getNameValueItems(
|
||||
parent,
|
||||
initial,
|
||||
placeholderText,
|
||||
polisher,
|
||||
tr("Edit Environment"),
|
||||
tr("Enter one environment variable per line.\n"
|
||||
"To set or change a variable, use VARIABLE=VALUE.\n"
|
||||
"Existing variables can be referenced in a VALUE with ${OTHER}.\n"
|
||||
"To clear a variable, put its name on a line with nothing else on it."));
|
||||
}
|
||||
|
||||
} // namespace Utils
|
||||
|
||||
#include "environmentdialog.moc"
|
||||
|
@@ -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
|
||||
|
51
src/libs/utils/environmentfwd.h
Normal file
51
src/libs/utils/environmentfwd.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
template<typename Type>
|
||||
class QList;
|
||||
class QTreeView;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Utils {
|
||||
class NameValueDictionary;
|
||||
class NameValueItem;
|
||||
using NameValueItems = QVector<NameValueItem>;
|
||||
|
||||
class Environment;
|
||||
using EnvironmentItem = NameValueItem;
|
||||
using EnvironmentItems = NameValueItems;
|
||||
|
||||
class PreprocessorMacroDictionary;
|
||||
using PreprocessorMacroItem = NameValueItem;
|
||||
using PreprocessorMacroItems = NameValueItems;
|
||||
|
||||
class NameValueModel;
|
||||
class EnvironmentModel;
|
||||
} // namespace Utils
|
@@ -33,360 +33,12 @@
|
||||
#include <QFont>
|
||||
|
||||
namespace Utils {
|
||||
namespace Internal {
|
||||
|
||||
class EnvironmentModelPrivate
|
||||
const Environment &EnvironmentModel::baseEnvironment() const
|
||||
{
|
||||
public:
|
||||
void updateResultEnvironment()
|
||||
{
|
||||
m_resultEnvironment = m_baseEnvironment;
|
||||
m_resultEnvironment.modify(m_items);
|
||||
// Add removed variables again and mark them as "<UNSET>" so
|
||||
// that the user can actually see those removals:
|
||||
foreach (const EnvironmentItem &item, m_items) {
|
||||
if (item.operation == EnvironmentItem::Unset)
|
||||
m_resultEnvironment.set(item.name, EnvironmentModel::tr("<UNSET>"));
|
||||
}
|
||||
}
|
||||
|
||||
int findInChanges(const QString &name) const
|
||||
{
|
||||
for (int i=0; i<m_items.size(); ++i)
|
||||
if (m_items.at(i).name == name)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int findInResultInsertPosition(const QString &name) const
|
||||
{
|
||||
Environment::const_iterator it;
|
||||
int i = 0;
|
||||
for (it = m_resultEnvironment.constBegin(); it != m_resultEnvironment.constEnd(); ++it, ++i)
|
||||
if (m_resultEnvironment.key(it) > name)
|
||||
return i;
|
||||
return m_resultEnvironment.size();
|
||||
}
|
||||
|
||||
int findInResult(const QString &name) const
|
||||
{
|
||||
Environment::const_iterator it;
|
||||
int i = 0;
|
||||
for (it = m_resultEnvironment.constBegin(); it != m_resultEnvironment.constEnd(); ++it, ++i)
|
||||
if (m_resultEnvironment.key(it) == name)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
Environment m_baseEnvironment;
|
||||
Environment m_resultEnvironment;
|
||||
QList<EnvironmentItem> m_items;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
EnvironmentModel::EnvironmentModel(QObject *parent) :
|
||||
QAbstractTableModel(parent),
|
||||
d(new Internal::EnvironmentModelPrivate)
|
||||
{ }
|
||||
|
||||
EnvironmentModel::~EnvironmentModel()
|
||||
{
|
||||
delete d;
|
||||
return static_cast<const Environment &>(baseNameValueDictionary());
|
||||
}
|
||||
|
||||
QString EnvironmentModel::indexToVariable(const QModelIndex &index) const
|
||||
{
|
||||
return d->m_resultEnvironment.key(d->m_resultEnvironment.constBegin() + index.row());
|
||||
}
|
||||
|
||||
void EnvironmentModel::setBaseEnvironment(const Environment &env)
|
||||
{
|
||||
if (d->m_baseEnvironment == env)
|
||||
return;
|
||||
beginResetModel();
|
||||
d->m_baseEnvironment = env;
|
||||
d->updateResultEnvironment();
|
||||
endResetModel();
|
||||
setBaseNameValueDictionary(env);
|
||||
}
|
||||
|
||||
int EnvironmentModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
if (parent.isValid())
|
||||
return 0;
|
||||
|
||||
return d->m_resultEnvironment.size();
|
||||
}
|
||||
int EnvironmentModel::columnCount(const QModelIndex &parent) const
|
||||
{
|
||||
if (parent.isValid())
|
||||
return 0;
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
bool EnvironmentModel::changes(const QString &name) const
|
||||
{
|
||||
return d->findInChanges(name) >= 0;
|
||||
}
|
||||
|
||||
Environment EnvironmentModel::baseEnvironment() const
|
||||
{
|
||||
return d->m_baseEnvironment;
|
||||
}
|
||||
|
||||
QVariant EnvironmentModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return QVariant();
|
||||
|
||||
if (role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::ToolTipRole) {
|
||||
if (index.column() == 0) {
|
||||
return d->m_resultEnvironment.key(d->m_resultEnvironment.constBegin() + index.row());
|
||||
} else if (index.column() == 1) {
|
||||
// Do not return "<UNSET>" when editing a previously unset variable:
|
||||
if (role == Qt::EditRole) {
|
||||
int pos = d->findInChanges(indexToVariable(index));
|
||||
if (pos >= 0)
|
||||
return d->m_items.at(pos).value;
|
||||
}
|
||||
QString value = d->m_resultEnvironment.value(d->m_resultEnvironment.constBegin() + index.row());
|
||||
if (role == Qt::ToolTipRole && value.length() > 80) {
|
||||
// Use html to enable text wrapping
|
||||
value = value.toHtmlEscaped();
|
||||
value.prepend(QLatin1String("<html><body>"));
|
||||
value.append(QLatin1String("</body></html>"));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
if (role == Qt::FontRole) {
|
||||
// check whether this environment variable exists in d->m_items
|
||||
if (changes(d->m_resultEnvironment.key(d->m_resultEnvironment.constBegin() + index.row()))) {
|
||||
QFont f;
|
||||
f.setBold(true);
|
||||
return QVariant(f);
|
||||
}
|
||||
return QFont();
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
Qt::ItemFlags EnvironmentModel::flags(const QModelIndex &index) const
|
||||
{
|
||||
Q_UNUSED(index)
|
||||
return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled;
|
||||
}
|
||||
|
||||
QVariant EnvironmentModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if (orientation == Qt::Vertical || role != Qt::DisplayRole)
|
||||
return QVariant();
|
||||
return section == 0 ? tr("Variable") : tr("Value");
|
||||
}
|
||||
|
||||
/// *****************
|
||||
/// Utility functions
|
||||
/// *****************
|
||||
QModelIndex EnvironmentModel::variableToIndex(const QString &name) const
|
||||
{
|
||||
int row = d->findInResult(name);
|
||||
if (row == -1)
|
||||
return QModelIndex();
|
||||
return index(row, 0);
|
||||
}
|
||||
|
||||
bool EnvironmentModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
{
|
||||
if (!index.isValid() || role != Qt::EditRole)
|
||||
return false;
|
||||
|
||||
// ignore changes to already set values:
|
||||
if (data(index, role) == value)
|
||||
return true;
|
||||
|
||||
const QString oldName = data(this->index(index.row(), 0, QModelIndex())).toString();
|
||||
const QString oldValue = data(this->index(index.row(), 1, QModelIndex()), Qt::EditRole).toString();
|
||||
int changesPos = d->findInChanges(oldName);
|
||||
|
||||
if (index.column() == 0) {
|
||||
//fail if a variable with the same name already exists
|
||||
const QString &newName = HostOsInfo::isWindowsHost()
|
||||
? value.toString().toUpper() : value.toString();
|
||||
if (newName.isEmpty() || newName.contains('='))
|
||||
return false;
|
||||
// Does the new name exist already?
|
||||
if (d->m_resultEnvironment.hasKey(newName) || newName.isEmpty())
|
||||
return false;
|
||||
|
||||
EnvironmentItem newVariable(newName, oldValue);
|
||||
|
||||
if (changesPos != -1)
|
||||
resetVariable(oldName); // restore the original base variable again
|
||||
|
||||
QModelIndex newIndex = addVariable(newVariable); // add the new variable
|
||||
emit focusIndex(newIndex.sibling(newIndex.row(), 1)); // hint to focus on the value
|
||||
return true;
|
||||
} else if (index.column() == 1) {
|
||||
// We are changing an existing value:
|
||||
const QString stringValue = value.toString();
|
||||
if (changesPos != -1) {
|
||||
// We have already changed this value
|
||||
if (d->m_baseEnvironment.hasKey(oldName) && stringValue == d->m_baseEnvironment.value(oldName)) {
|
||||
// ... and now went back to the base value
|
||||
d->m_items.removeAt(changesPos);
|
||||
} else {
|
||||
// ... and changed it again
|
||||
d->m_items[changesPos].value = stringValue;
|
||||
d->m_items[changesPos].operation = EnvironmentItem::Set;
|
||||
}
|
||||
} else {
|
||||
// Add a new change item:
|
||||
d->m_items.append(EnvironmentItem(oldName, stringValue));
|
||||
}
|
||||
d->updateResultEnvironment();
|
||||
emit dataChanged(index, index);
|
||||
emit userChangesChanged();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QModelIndex EnvironmentModel::addVariable()
|
||||
{
|
||||
//: Name when inserting a new variable
|
||||
return addVariable(EnvironmentItem(tr("<VARIABLE>"),
|
||||
//: Value when inserting a new variable
|
||||
tr("<VALUE>")));
|
||||
}
|
||||
|
||||
QModelIndex EnvironmentModel::addVariable(const EnvironmentItem &item)
|
||||
{
|
||||
|
||||
// Return existing index if the name is already in the result set:
|
||||
int pos = d->findInResult(item.name);
|
||||
if (pos >= 0 && pos < d->m_resultEnvironment.size())
|
||||
return index(pos, 0, QModelIndex());
|
||||
|
||||
int insertPos = d->findInResultInsertPosition(item.name);
|
||||
int changePos = d->findInChanges(item.name);
|
||||
if (d->m_baseEnvironment.hasKey(item.name)) {
|
||||
// We previously unset this!
|
||||
Q_ASSERT(changePos >= 0);
|
||||
// Do not insert a line here as we listed the variable as <UNSET> before!
|
||||
Q_ASSERT(d->m_items.at(changePos).name == item.name);
|
||||
Q_ASSERT(d->m_items.at(changePos).operation == EnvironmentItem::Unset);
|
||||
Q_ASSERT(d->m_items.at(changePos).value.isEmpty());
|
||||
d->m_items[changePos] = item;
|
||||
emit dataChanged(index(insertPos, 0, QModelIndex()), index(insertPos, 1, QModelIndex()));
|
||||
} else {
|
||||
// We add something that is not in the base environment
|
||||
// Insert a new line!
|
||||
beginInsertRows(QModelIndex(), insertPos, insertPos);
|
||||
Q_ASSERT(changePos < 0);
|
||||
d->m_items.append(item);
|
||||
d->updateResultEnvironment();
|
||||
endInsertRows();
|
||||
}
|
||||
emit userChangesChanged();
|
||||
return index(insertPos, 0, QModelIndex());
|
||||
}
|
||||
|
||||
void EnvironmentModel::resetVariable(const QString &name)
|
||||
{
|
||||
int rowInChanges = d->findInChanges(name);
|
||||
if (rowInChanges < 0)
|
||||
return;
|
||||
|
||||
int rowInResult = d->findInResult(name);
|
||||
if (rowInResult < 0)
|
||||
return;
|
||||
|
||||
if (d->m_baseEnvironment.hasKey(name)) {
|
||||
d->m_items.removeAt(rowInChanges);
|
||||
d->updateResultEnvironment();
|
||||
emit dataChanged(index(rowInResult, 0, QModelIndex()), index(rowInResult, 1, QModelIndex()));
|
||||
emit userChangesChanged();
|
||||
} else {
|
||||
// Remove the line completely!
|
||||
beginRemoveRows(QModelIndex(), rowInResult, rowInResult);
|
||||
d->m_items.removeAt(rowInChanges);
|
||||
d->updateResultEnvironment();
|
||||
endRemoveRows();
|
||||
emit userChangesChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void EnvironmentModel::unsetVariable(const QString &name)
|
||||
{
|
||||
// This does not change the number of rows as we will display a <UNSET>
|
||||
// in place of the original variable!
|
||||
int row = d->findInResult(name);
|
||||
if (row < 0)
|
||||
return;
|
||||
|
||||
// look in d->m_items for the variable
|
||||
int pos = d->findInChanges(name);
|
||||
if (pos != -1) {
|
||||
d->m_items[pos].operation = EnvironmentItem::Unset;
|
||||
d->m_items[pos].value.clear();
|
||||
d->updateResultEnvironment();
|
||||
emit dataChanged(index(row, 0, QModelIndex()), index(row, 1, QModelIndex()));
|
||||
emit userChangesChanged();
|
||||
return;
|
||||
}
|
||||
d->m_items.append(EnvironmentItem(name, QString(), EnvironmentItem::Unset));
|
||||
d->updateResultEnvironment();
|
||||
emit dataChanged(index(row, 0, QModelIndex()), index(row, 1, QModelIndex()));
|
||||
emit userChangesChanged();
|
||||
}
|
||||
|
||||
bool EnvironmentModel::canUnset(const QString &name)
|
||||
{
|
||||
int pos = d->findInChanges(name);
|
||||
if (pos != -1)
|
||||
return d->m_items.at(pos).operation == EnvironmentItem::Unset;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EnvironmentModel::canReset(const QString &name)
|
||||
{
|
||||
return d->m_baseEnvironment.hasKey(name);
|
||||
}
|
||||
|
||||
QList<EnvironmentItem> EnvironmentModel::userChanges() const
|
||||
{
|
||||
return d->m_items;
|
||||
}
|
||||
|
||||
void EnvironmentModel::setUserChanges(const QList<EnvironmentItem> &list)
|
||||
{
|
||||
QList<EnvironmentItem> filtered = Utils::filtered(list, [](const EnvironmentItem &i) {
|
||||
return i.name != "export " && !i.name.contains('=');
|
||||
});
|
||||
// We assume nobody is reordering the items here.
|
||||
if (filtered == d->m_items)
|
||||
return;
|
||||
beginResetModel();
|
||||
d->m_items = filtered;
|
||||
for (EnvironmentItem &item : d->m_items) {
|
||||
QString &name = item.name;
|
||||
name = name.trimmed();
|
||||
if (name.startsWith("export "))
|
||||
name = name.mid(7).trimmed();
|
||||
if (d->m_baseEnvironment.osType() == OsTypeWindows) {
|
||||
// Environment variable names are case-insensitive under windows, but we still
|
||||
// want to preserve the case of pre-existing variables.
|
||||
auto it = d->m_baseEnvironment.constFind(name);
|
||||
if (it != d->m_baseEnvironment.constEnd())
|
||||
name = d->m_baseEnvironment.key(it);
|
||||
}
|
||||
}
|
||||
|
||||
d->updateResultEnvironment();
|
||||
endResetModel();
|
||||
emit userChangesChanged();
|
||||
}
|
||||
|
||||
} // namespace Utils
|
||||
|
@@ -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
|
||||
|
282
src/libs/utils/namevaluedictionary.cpp
Normal file
282
src/libs/utils/namevaluedictionary.cpp
Normal file
@@ -0,0 +1,282 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "algorithm.h"
|
||||
#include "namevaluedictionary.h"
|
||||
#include "qtcassert.h"
|
||||
|
||||
#include <QDir>
|
||||
|
||||
namespace Utils {
|
||||
|
||||
namespace {
|
||||
NameValueMap::iterator findKey(NameValueMap &input, Utils::OsType osType, const QString &key)
|
||||
{
|
||||
const Qt::CaseSensitivity casing = (osType == Utils::OsTypeWindows) ? Qt::CaseInsensitive
|
||||
: Qt::CaseSensitive;
|
||||
for (auto it = input.begin(); it != input.end(); ++it) {
|
||||
if (key.compare(it.key(), casing) == 0)
|
||||
return it;
|
||||
}
|
||||
return input.end();
|
||||
}
|
||||
|
||||
NameValueMap::const_iterator findKey(const NameValueMap &input, Utils::OsType osType, const QString &key)
|
||||
{
|
||||
const Qt::CaseSensitivity casing = (osType == Utils::OsTypeWindows) ? Qt::CaseInsensitive
|
||||
: Qt::CaseSensitive;
|
||||
for (auto it = input.constBegin(); it != input.constEnd(); ++it) {
|
||||
if (key.compare(it.key(), casing) == 0)
|
||||
return it;
|
||||
}
|
||||
return input.constEnd();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
NameValueDictionary::NameValueDictionary(const QStringList &env, OsType osType)
|
||||
: m_osType(osType)
|
||||
{
|
||||
for (const QString &s : env) {
|
||||
int i = s.indexOf('=', 1);
|
||||
if (i >= 0) {
|
||||
const QString key = s.left(i);
|
||||
if (!key.contains('=')) {
|
||||
const QString value = s.mid(i + 1);
|
||||
set(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NameValueDictionary::NameValueDictionary(const NameValuePairs &nameValues)
|
||||
{
|
||||
for (const auto &nameValue : nameValues)
|
||||
set(nameValue.first, nameValue.second);
|
||||
}
|
||||
|
||||
QStringList NameValueDictionary::toStringList() const
|
||||
{
|
||||
QStringList result;
|
||||
for (auto it = m_values.constBegin(); it != m_values.constEnd(); ++it)
|
||||
result.append(it.key() + '=' + it.value());
|
||||
return result;
|
||||
}
|
||||
|
||||
void NameValueDictionary::set(const QString &key, const QString &value)
|
||||
{
|
||||
QTC_ASSERT(!key.contains('='), return );
|
||||
auto it = findKey(m_values, m_osType, key);
|
||||
if (it == m_values.end())
|
||||
m_values.insert(key, value);
|
||||
else
|
||||
it.value() = value;
|
||||
}
|
||||
|
||||
void NameValueDictionary::unset(const QString &key)
|
||||
{
|
||||
QTC_ASSERT(!key.contains('='), return );
|
||||
auto it = findKey(m_values, m_osType, key);
|
||||
if (it != m_values.end())
|
||||
m_values.erase(it);
|
||||
}
|
||||
|
||||
void NameValueDictionary::clear()
|
||||
{
|
||||
m_values.clear();
|
||||
}
|
||||
|
||||
QString NameValueDictionary::value(const QString &key) const
|
||||
{
|
||||
const auto it = findKey(m_values, m_osType, key);
|
||||
return it != m_values.end() ? it.value() : QString();
|
||||
}
|
||||
|
||||
NameValueDictionary::const_iterator NameValueDictionary::constFind(const QString &name) const
|
||||
{
|
||||
return findKey(m_values, m_osType, name);
|
||||
}
|
||||
|
||||
int NameValueDictionary::size() const
|
||||
{
|
||||
return m_values.size();
|
||||
}
|
||||
|
||||
void NameValueDictionary::modify(const NameValueItems &items)
|
||||
{
|
||||
NameValueDictionary resultKeyValueDictionary = *this;
|
||||
for (const NameValueItem &item : items)
|
||||
item.apply(&resultKeyValueDictionary);
|
||||
*this = resultKeyValueDictionary;
|
||||
}
|
||||
|
||||
enum : char {
|
||||
#ifdef Q_OS_WIN
|
||||
pathSepC = ';'
|
||||
#else
|
||||
pathSepC = ':'
|
||||
#endif
|
||||
};
|
||||
|
||||
NameValueItems NameValueDictionary::diff(const NameValueDictionary &other, bool checkAppendPrepend) const
|
||||
{
|
||||
NameValueMap::const_iterator thisIt = constBegin();
|
||||
NameValueMap::const_iterator otherIt = other.constBegin();
|
||||
|
||||
NameValueItems result;
|
||||
while (thisIt != constEnd() || otherIt != other.constEnd()) {
|
||||
if (thisIt == constEnd()) {
|
||||
result.append(NameValueItem(otherIt.key(), otherIt.value()));
|
||||
++otherIt;
|
||||
} else if (otherIt == other.constEnd()) {
|
||||
result.append(NameValueItem(thisIt.key(), QString(), NameValueItem::Unset));
|
||||
++thisIt;
|
||||
} else if (thisIt.key() < otherIt.key()) {
|
||||
result.append(NameValueItem(thisIt.key(), QString(), NameValueItem::Unset));
|
||||
++thisIt;
|
||||
} else if (thisIt.key() > otherIt.key()) {
|
||||
result.append(NameValueItem(otherIt.key(), otherIt.value()));
|
||||
++otherIt;
|
||||
} else {
|
||||
const QString &oldValue = thisIt.value();
|
||||
const QString &newValue = otherIt.value();
|
||||
if (oldValue != newValue) {
|
||||
if (checkAppendPrepend && newValue.startsWith(oldValue)) {
|
||||
QString appended = newValue.right(newValue.size() - oldValue.size());
|
||||
if (appended.startsWith(QLatin1Char(pathSepC)))
|
||||
appended.remove(0, 1);
|
||||
result.append(NameValueItem(otherIt.key(), appended, NameValueItem::Append));
|
||||
} else if (checkAppendPrepend && newValue.endsWith(oldValue)) {
|
||||
QString prepended = newValue.left(newValue.size() - oldValue.size());
|
||||
if (prepended.endsWith(QLatin1Char(pathSepC)))
|
||||
prepended.chop(1);
|
||||
result.append(NameValueItem(otherIt.key(), prepended, NameValueItem::Prepend));
|
||||
} else {
|
||||
result.append(NameValueItem(otherIt.key(), newValue));
|
||||
}
|
||||
}
|
||||
++otherIt;
|
||||
++thisIt;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool NameValueDictionary::hasKey(const QString &key) const
|
||||
{
|
||||
return m_values.contains(key);
|
||||
}
|
||||
|
||||
OsType NameValueDictionary::osType() const
|
||||
{
|
||||
return m_osType;
|
||||
}
|
||||
|
||||
QString NameValueDictionary::userName() const
|
||||
{
|
||||
return value(QString::fromLatin1(m_osType == OsTypeWindows ? "USERNAME" : "USER"));
|
||||
}
|
||||
|
||||
/** Expand environment variables in a string.
|
||||
*
|
||||
* KeyValueDictionary variables are accepted in the following forms:
|
||||
* $SOMEVAR, ${SOMEVAR} on Unix and %SOMEVAR% on Windows.
|
||||
* No escapes and quoting are supported.
|
||||
* If a variable is not found, it is not substituted.
|
||||
*/
|
||||
QString NameValueDictionary::expandVariables(const QString &input) const
|
||||
{
|
||||
QString result = input;
|
||||
|
||||
if (m_osType == OsTypeWindows) {
|
||||
for (int vStart = -1, i = 0; i < result.length();) {
|
||||
if (result.at(i++) == '%') {
|
||||
if (vStart > 0) {
|
||||
const_iterator it = findKey(m_values, m_osType, result.mid(vStart, i - vStart - 1));
|
||||
if (it != m_values.constEnd()) {
|
||||
result.replace(vStart - 1, i - vStart + 1, *it);
|
||||
i = vStart - 1 + it->length();
|
||||
vStart = -1;
|
||||
} else {
|
||||
vStart = i;
|
||||
}
|
||||
} else {
|
||||
vStart = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
enum { BASE, OPTIONALVARIABLEBRACE, VARIABLE, BRACEDVARIABLE } state = BASE;
|
||||
int vStart = -1;
|
||||
|
||||
for (int i = 0; i < result.length();) {
|
||||
QChar c = result.at(i++);
|
||||
if (state == BASE) {
|
||||
if (c == '$')
|
||||
state = OPTIONALVARIABLEBRACE;
|
||||
} else if (state == OPTIONALVARIABLEBRACE) {
|
||||
if (c == '{') {
|
||||
state = BRACEDVARIABLE;
|
||||
vStart = i;
|
||||
} else if (c.isLetterOrNumber() || c == '_') {
|
||||
state = VARIABLE;
|
||||
vStart = i - 1;
|
||||
} else {
|
||||
state = BASE;
|
||||
}
|
||||
} else if (state == BRACEDVARIABLE) {
|
||||
if (c == '}') {
|
||||
const_iterator it = m_values.constFind(result.mid(vStart, i - 1 - vStart));
|
||||
if (it != constEnd()) {
|
||||
result.replace(vStart - 2, i - vStart + 2, *it);
|
||||
i = vStart - 2 + it->length();
|
||||
}
|
||||
state = BASE;
|
||||
}
|
||||
} else if (state == VARIABLE) {
|
||||
if (!c.isLetterOrNumber() && c != '_') {
|
||||
const_iterator it = m_values.constFind(result.mid(vStart, i - vStart - 1));
|
||||
if (it != constEnd()) {
|
||||
result.replace(vStart - 1, i - vStart, *it);
|
||||
i = vStart - 1 + it->length();
|
||||
}
|
||||
state = BASE;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (state == VARIABLE) {
|
||||
const_iterator it = m_values.constFind(result.mid(vStart));
|
||||
if (it != constEnd())
|
||||
result.replace(vStart - 1, result.length() - vStart + 1, *it);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QStringList NameValueDictionary::expandVariables(const QStringList &variables) const
|
||||
{
|
||||
return Utils::transform(variables, [this](const QString &i) { return expandVariables(i); });
|
||||
}
|
||||
|
||||
} // namespace Utils
|
92
src/libs/utils/namevaluedictionary.h
Normal file
92
src/libs/utils/namevaluedictionary.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fileutils.h"
|
||||
#include "hostosinfo.h"
|
||||
#include "namevalueitem.h"
|
||||
|
||||
namespace Utils {
|
||||
|
||||
using NameValuePair = std::pair<QString, QString>;
|
||||
using NameValuePairs = QVector<NameValuePair>;
|
||||
using NameValueMap = QMap<QString, QString>;
|
||||
|
||||
class QTCREATOR_UTILS_EXPORT NameValueDictionary
|
||||
{
|
||||
public:
|
||||
using const_iterator = NameValueMap::const_iterator;
|
||||
|
||||
explicit NameValueDictionary(OsType osType = HostOsInfo::hostOs())
|
||||
: m_osType(osType)
|
||||
{}
|
||||
explicit NameValueDictionary(const QStringList &env, OsType osType = HostOsInfo::hostOs());
|
||||
explicit NameValueDictionary(const NameValuePairs &nameValues);
|
||||
|
||||
QStringList toStringList() const;
|
||||
QString value(const QString &key) const;
|
||||
void set(const QString &key, const QString &value);
|
||||
void unset(const QString &key);
|
||||
void modify(const NameValueItems &items);
|
||||
/// Return the KeyValueDictionary changes necessary to modify this into the other environment.
|
||||
NameValueItems diff(const NameValueDictionary &other, bool checkAppendPrepend = false) const;
|
||||
bool hasKey(const QString &key) const;
|
||||
OsType osType() const;
|
||||
|
||||
QString userName() const;
|
||||
|
||||
void clear();
|
||||
int size() const;
|
||||
|
||||
QString key(NameValueDictionary::const_iterator it) const { return it.key(); }
|
||||
|
||||
QString value(NameValueDictionary::const_iterator it) const { return it.value(); }
|
||||
|
||||
NameValueDictionary::const_iterator constBegin() const { return m_values.constBegin(); }
|
||||
|
||||
NameValueDictionary::const_iterator constEnd() const { return m_values.constEnd(); }
|
||||
|
||||
NameValueDictionary::const_iterator constFind(const QString &name) const;
|
||||
|
||||
QString expandVariables(const QString &input) const;
|
||||
QStringList expandVariables(const QStringList &input) const;
|
||||
|
||||
friend bool operator!=(const NameValueDictionary &first, const NameValueDictionary &second)
|
||||
{
|
||||
return !(first == second);
|
||||
}
|
||||
|
||||
friend bool operator==(const NameValueDictionary &first, const NameValueDictionary &second)
|
||||
{
|
||||
return first.m_osType == second.m_osType && first.m_values == second.m_values;
|
||||
}
|
||||
|
||||
protected:
|
||||
NameValueMap m_values;
|
||||
OsType m_osType;
|
||||
};
|
||||
|
||||
} // namespace Utils
|
199
src/libs/utils/namevalueitem.cpp
Normal file
199
src/libs/utils/namevalueitem.cpp
Normal file
@@ -0,0 +1,199 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "namevalueitem.h"
|
||||
#include "algorithm.h"
|
||||
#include "namevaluedictionary.h"
|
||||
#include "qtcassert.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
namespace Utils {
|
||||
|
||||
void NameValueItem::sort(NameValueItems *list)
|
||||
{
|
||||
Utils::sort(*list, &NameValueItem::name);
|
||||
}
|
||||
|
||||
NameValueItems NameValueItem::fromStringList(const QStringList &list)
|
||||
{
|
||||
NameValueItems result;
|
||||
for (const QString &string : list) {
|
||||
int pos = string.indexOf('=', 1);
|
||||
if (pos == -1)
|
||||
result.append(NameValueItem(string, QString(), NameValueItem::Unset));
|
||||
else
|
||||
result.append(NameValueItem(string.left(pos), string.mid(pos + 1)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QStringList NameValueItem::toStringList(const NameValueItems &list)
|
||||
{
|
||||
return Utils::transform<QStringList>(list, [](const NameValueItem &item) {
|
||||
if (item.operation == NameValueItem::Unset)
|
||||
return QString(item.name);
|
||||
return QString(item.name + '=' + item.value);
|
||||
});
|
||||
}
|
||||
|
||||
NameValueItems NameValueItem::itemsFromVariantList(const QVariantList &list)
|
||||
{
|
||||
return Utils::transform<NameValueItems>(list, [](const QVariant &item) {
|
||||
return itemFromVariantList(item.toList());
|
||||
});
|
||||
}
|
||||
|
||||
QVariantList NameValueItem::toVariantList(const NameValueItems &list)
|
||||
{
|
||||
return Utils::transform<QVariantList>(list, [](const NameValueItem &item) {
|
||||
return QVariant(toVariantList(item));
|
||||
});
|
||||
}
|
||||
|
||||
NameValueItem NameValueItem::itemFromVariantList(const QVariantList &list)
|
||||
{
|
||||
QTC_ASSERT(list.size() == 3, return NameValueItem("", ""));
|
||||
QString key = list.value(0).toString();
|
||||
Operation operation = Operation(list.value(1).toInt());
|
||||
QString value = list.value(2).toString();
|
||||
return NameValueItem(key, value, operation);
|
||||
}
|
||||
|
||||
QVariantList NameValueItem::toVariantList(const NameValueItem &item)
|
||||
{
|
||||
return QVariantList() << item.name << item.operation << item.value;
|
||||
}
|
||||
|
||||
static QString expand(const NameValueDictionary *dictionary, QString value)
|
||||
{
|
||||
int replaceCount = 0;
|
||||
for (int i = 0; i < value.size(); ++i) {
|
||||
if (value.at(i) == '$') {
|
||||
if ((i + 1) < value.size()) {
|
||||
const QChar &c = value.at(i + 1);
|
||||
int end = -1;
|
||||
if (c == '(')
|
||||
end = value.indexOf(')', i);
|
||||
else if (c == '{')
|
||||
end = value.indexOf('}', i);
|
||||
if (end != -1) {
|
||||
const QString &key = value.mid(i + 2, end - i - 2);
|
||||
NameValueDictionary::const_iterator it = dictionary->constFind(key);
|
||||
if (it != dictionary->constEnd())
|
||||
value.replace(i, end - i + 1, it.value());
|
||||
++replaceCount;
|
||||
QTC_ASSERT(replaceCount < 100, break);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
enum : char {
|
||||
#ifdef Q_OS_WIN
|
||||
pathSepC = ';'
|
||||
#else
|
||||
pathSepC = ':'
|
||||
#endif
|
||||
};
|
||||
|
||||
void NameValueItem::apply(NameValueDictionary *dictionary, Operation op) const
|
||||
{
|
||||
switch (op) {
|
||||
case Set:
|
||||
dictionary->set(name, expand(dictionary, value));
|
||||
break;
|
||||
case Unset:
|
||||
dictionary->unset(name);
|
||||
break;
|
||||
case Prepend: {
|
||||
const NameValueDictionary::const_iterator it = dictionary->constFind(name);
|
||||
if (it != dictionary->constEnd()) {
|
||||
QString v = it.value();
|
||||
const QChar pathSep{QLatin1Char(pathSepC)};
|
||||
int sepCount = 0;
|
||||
if (v.startsWith(pathSep))
|
||||
++sepCount;
|
||||
if (value.endsWith(pathSep))
|
||||
++sepCount;
|
||||
if (sepCount == 2)
|
||||
v.remove(0, 1);
|
||||
else if (sepCount == 0)
|
||||
v.prepend(pathSep);
|
||||
v.prepend(expand(dictionary, value));
|
||||
dictionary->set(name, v);
|
||||
} else {
|
||||
apply(dictionary, Set);
|
||||
}
|
||||
} break;
|
||||
case Append: {
|
||||
const NameValueDictionary::const_iterator it = dictionary->constFind(name);
|
||||
if (it != dictionary->constEnd()) {
|
||||
QString v = it.value();
|
||||
const QChar pathSep{QLatin1Char(pathSepC)};
|
||||
int sepCount = 0;
|
||||
if (v.endsWith(pathSep))
|
||||
++sepCount;
|
||||
if (value.startsWith(pathSep))
|
||||
++sepCount;
|
||||
if (sepCount == 2)
|
||||
v.chop(1);
|
||||
else if (sepCount == 0)
|
||||
v.append(pathSep);
|
||||
v.append(expand(dictionary, value));
|
||||
dictionary->set(name, v);
|
||||
} else {
|
||||
apply(dictionary, Set);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug debug, const NameValueItem &i)
|
||||
{
|
||||
QDebugStateSaver saver(debug);
|
||||
debug.noquote();
|
||||
debug.nospace();
|
||||
debug << "KeyValueItem(";
|
||||
switch (i.operation) {
|
||||
case NameValueItem::Set:
|
||||
debug << "set \"" << i.name << "\" to \"" << i.value << '"';
|
||||
break;
|
||||
case NameValueItem::Unset:
|
||||
debug << "unset \"" << i.name << '"';
|
||||
break;
|
||||
case NameValueItem::Prepend:
|
||||
debug << "prepend to \"" << i.name << "\":\"" << i.value << '"';
|
||||
break;
|
||||
case NameValueItem::Append:
|
||||
debug << "append to \"" << i.name << "\":\"" << i.value << '"';
|
||||
break;
|
||||
}
|
||||
debug << ')';
|
||||
return debug;
|
||||
}
|
||||
} // namespace Utils
|
80
src/libs/utils/namevalueitem.h
Normal file
80
src/libs/utils/namevalueitem.h
Normal file
@@ -0,0 +1,80 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "environmentfwd.h"
|
||||
#include "utils_global.h"
|
||||
|
||||
#include <QStringList>
|
||||
#include <QVariantList>
|
||||
#include <QVector>
|
||||
|
||||
namespace Utils {
|
||||
|
||||
class QTCREATOR_UTILS_EXPORT NameValueItem
|
||||
{
|
||||
public:
|
||||
enum Operation : char { Set, Unset, Prepend, Append };
|
||||
NameValueItem() = default;
|
||||
NameValueItem(const QString &key, const QString &value, Operation operation = Set)
|
||||
: name(key)
|
||||
, value(value)
|
||||
, operation(operation)
|
||||
{}
|
||||
|
||||
void apply(NameValueDictionary *dictionary) const { apply(dictionary, operation); }
|
||||
|
||||
static void sort(NameValueItems *list);
|
||||
static NameValueItems fromStringList(const QStringList &list);
|
||||
static QStringList toStringList(const NameValueItems &list);
|
||||
static NameValueItems itemsFromVariantList(const QVariantList &list);
|
||||
static QVariantList toVariantList(const NameValueItems &list);
|
||||
static NameValueItem itemFromVariantList(const QVariantList &list);
|
||||
static QVariantList toVariantList(const NameValueItem &item);
|
||||
|
||||
friend bool operator==(const NameValueItem &first, const NameValueItem &second)
|
||||
{
|
||||
return first.operation == second.operation && first.name == second.name
|
||||
&& first.value == second.value;
|
||||
}
|
||||
|
||||
friend bool operator!=(const NameValueItem &first, const NameValueItem &second)
|
||||
{
|
||||
return !(first == second);
|
||||
}
|
||||
|
||||
public:
|
||||
QString name;
|
||||
QString value;
|
||||
Operation operation = Unset;
|
||||
|
||||
private:
|
||||
void apply(NameValueDictionary *dictionary, Operation op) const;
|
||||
};
|
||||
|
||||
QTCREATOR_UTILS_EXPORT QDebug operator<<(QDebug debug, const NameValueItem &i);
|
||||
|
||||
} // namespace Utils
|
397
src/libs/utils/namevaluemodel.cpp
Normal file
397
src/libs/utils/namevaluemodel.cpp
Normal file
@@ -0,0 +1,397 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "namevaluemodel.h"
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/hostosinfo.h>
|
||||
#include <utils/namevaluedictionary.h>
|
||||
|
||||
#include <QFont>
|
||||
#include <QString>
|
||||
|
||||
namespace Utils {
|
||||
namespace Internal {
|
||||
|
||||
class NameValueModelPrivate
|
||||
{
|
||||
public:
|
||||
void updateResultNameValueDictionary()
|
||||
{
|
||||
m_resultNameValueDictionary = m_baseNameValueDictionary;
|
||||
m_resultNameValueDictionary.modify(m_items);
|
||||
// Add removed variables again and mark them as "<UNSET>" so
|
||||
// that the user can actually see those removals:
|
||||
foreach (const NameValueItem &item, m_items) {
|
||||
if (item.operation == NameValueItem::Unset)
|
||||
m_resultNameValueDictionary.set(item.name, NameValueModel::tr("<UNSET>"));
|
||||
}
|
||||
}
|
||||
|
||||
int findInChanges(const QString &name) const
|
||||
{
|
||||
for (int i = 0; i < m_items.size(); ++i)
|
||||
if (m_items.at(i).name == name)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int findInResultInsertPosition(const QString &name) const
|
||||
{
|
||||
NameValueDictionary::const_iterator it;
|
||||
int i = 0;
|
||||
for (it = m_resultNameValueDictionary.constBegin();
|
||||
it != m_resultNameValueDictionary.constEnd();
|
||||
++it, ++i)
|
||||
if (m_resultNameValueDictionary.key(it) > name)
|
||||
return i;
|
||||
return m_resultNameValueDictionary.size();
|
||||
}
|
||||
|
||||
int findInResult(const QString &name) const
|
||||
{
|
||||
NameValueDictionary::const_iterator it;
|
||||
int i = 0;
|
||||
for (it = m_resultNameValueDictionary.constBegin();
|
||||
it != m_resultNameValueDictionary.constEnd();
|
||||
++it, ++i)
|
||||
if (m_resultNameValueDictionary.key(it) == name)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
NameValueDictionary m_baseNameValueDictionary;
|
||||
NameValueDictionary m_resultNameValueDictionary;
|
||||
NameValueItems m_items;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
NameValueModel::NameValueModel(QObject *parent)
|
||||
: QAbstractTableModel(parent)
|
||||
, d(std::make_unique<Internal::NameValueModelPrivate>())
|
||||
{}
|
||||
|
||||
NameValueModel::~NameValueModel() = default;
|
||||
|
||||
QString NameValueModel::indexToVariable(const QModelIndex &index) const
|
||||
{
|
||||
return d->m_resultNameValueDictionary.key(d->m_resultNameValueDictionary.constBegin()
|
||||
+ index.row());
|
||||
}
|
||||
|
||||
void NameValueModel::setBaseNameValueDictionary(const NameValueDictionary &dictionary)
|
||||
{
|
||||
if (d->m_baseNameValueDictionary == dictionary)
|
||||
return;
|
||||
beginResetModel();
|
||||
d->m_baseNameValueDictionary = dictionary;
|
||||
d->updateResultNameValueDictionary();
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
int NameValueModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
if (parent.isValid())
|
||||
return 0;
|
||||
|
||||
return d->m_resultNameValueDictionary.size();
|
||||
}
|
||||
int NameValueModel::columnCount(const QModelIndex &parent) const
|
||||
{
|
||||
if (parent.isValid())
|
||||
return 0;
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
bool NameValueModel::changes(const QString &name) const
|
||||
{
|
||||
return d->findInChanges(name) >= 0;
|
||||
}
|
||||
|
||||
const NameValueDictionary &NameValueModel::baseNameValueDictionary() const
|
||||
{
|
||||
return d->m_baseNameValueDictionary;
|
||||
}
|
||||
|
||||
QVariant NameValueModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return QVariant();
|
||||
|
||||
if (role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::ToolTipRole) {
|
||||
if (index.column() == 0) {
|
||||
return d->m_resultNameValueDictionary.key(d->m_resultNameValueDictionary.constBegin()
|
||||
+ index.row());
|
||||
} else if (index.column() == 1) {
|
||||
// Do not return "<UNSET>" when editing a previously unset variable:
|
||||
if (role == Qt::EditRole) {
|
||||
int pos = d->findInChanges(indexToVariable(index));
|
||||
if (pos >= 0)
|
||||
return d->m_items.at(pos).value;
|
||||
}
|
||||
QString value = d->m_resultNameValueDictionary.value(
|
||||
d->m_resultNameValueDictionary.constBegin() + index.row());
|
||||
if (role == Qt::ToolTipRole && value.length() > 80) {
|
||||
// Use html to enable text wrapping
|
||||
value = value.toHtmlEscaped();
|
||||
value.prepend(QLatin1String("<html><body>"));
|
||||
value.append(QLatin1String("</body></html>"));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
if (role == Qt::FontRole) {
|
||||
// check whether this name value item variable exists in d->m_items
|
||||
if (changes(d->m_resultNameValueDictionary.key(d->m_resultNameValueDictionary.constBegin()
|
||||
+ index.row()))) {
|
||||
QFont f;
|
||||
f.setBold(true);
|
||||
return QVariant(f);
|
||||
}
|
||||
return QFont();
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
Qt::ItemFlags NameValueModel::flags(const QModelIndex &index) const
|
||||
{
|
||||
Q_UNUSED(index)
|
||||
return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled;
|
||||
}
|
||||
|
||||
QVariant NameValueModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if (orientation == Qt::Vertical || role != Qt::DisplayRole)
|
||||
return QVariant();
|
||||
return section == 0 ? tr("Variable") : tr("Value");
|
||||
}
|
||||
|
||||
/// *****************
|
||||
/// Utility functions
|
||||
/// *****************
|
||||
QModelIndex NameValueModel::variableToIndex(const QString &name) const
|
||||
{
|
||||
int row = d->findInResult(name);
|
||||
if (row == -1)
|
||||
return QModelIndex();
|
||||
return index(row, 0);
|
||||
}
|
||||
|
||||
bool NameValueModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
{
|
||||
if (!index.isValid() || role != Qt::EditRole)
|
||||
return false;
|
||||
|
||||
// ignore changes to already set values:
|
||||
if (data(index, role) == value)
|
||||
return true;
|
||||
|
||||
const QString oldName = data(this->index(index.row(), 0, QModelIndex())).toString();
|
||||
const QString oldValue = data(this->index(index.row(), 1, QModelIndex()), Qt::EditRole).toString();
|
||||
int changesPos = d->findInChanges(oldName);
|
||||
|
||||
if (index.column() == 0) {
|
||||
//fail if a variable with the same name already exists
|
||||
const QString &newName = HostOsInfo::isWindowsHost() ? value.toString().toUpper()
|
||||
: value.toString();
|
||||
if (newName.isEmpty() || newName.contains('='))
|
||||
return false;
|
||||
// Does the new name exist already?
|
||||
if (d->m_resultNameValueDictionary.hasKey(newName) || newName.isEmpty())
|
||||
return false;
|
||||
|
||||
NameValueItem newVariable(newName, oldValue);
|
||||
|
||||
if (changesPos != -1)
|
||||
resetVariable(oldName); // restore the original base variable again
|
||||
|
||||
QModelIndex newIndex = addVariable(newVariable); // add the new variable
|
||||
emit focusIndex(newIndex.sibling(newIndex.row(), 1)); // hint to focus on the value
|
||||
return true;
|
||||
} else if (index.column() == 1) {
|
||||
// We are changing an existing value:
|
||||
const QString stringValue = value.toString();
|
||||
if (changesPos != -1) {
|
||||
// We have already changed this value
|
||||
if (d->m_baseNameValueDictionary.hasKey(oldName)
|
||||
&& stringValue == d->m_baseNameValueDictionary.value(oldName)) {
|
||||
// ... and now went back to the base value
|
||||
d->m_items.removeAt(changesPos);
|
||||
} else {
|
||||
// ... and changed it again
|
||||
d->m_items[changesPos].value = stringValue;
|
||||
d->m_items[changesPos].operation = NameValueItem::Set;
|
||||
}
|
||||
} else {
|
||||
// Add a new change item:
|
||||
d->m_items.append(NameValueItem(oldName, stringValue));
|
||||
}
|
||||
d->updateResultNameValueDictionary();
|
||||
emit dataChanged(index, index);
|
||||
emit userChangesChanged();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QModelIndex NameValueModel::addVariable()
|
||||
{
|
||||
//: Name when inserting a new variable
|
||||
return addVariable(NameValueItem(tr("<VARIABLE>"),
|
||||
//: Value when inserting a new variable
|
||||
tr("<VALUE>")));
|
||||
}
|
||||
|
||||
QModelIndex NameValueModel::addVariable(const NameValueItem &item)
|
||||
{
|
||||
// Return existing index if the name is already in the result set:
|
||||
int pos = d->findInResult(item.name);
|
||||
if (pos >= 0 && pos < d->m_resultNameValueDictionary.size())
|
||||
return index(pos, 0, QModelIndex());
|
||||
|
||||
int insertPos = d->findInResultInsertPosition(item.name);
|
||||
int changePos = d->findInChanges(item.name);
|
||||
if (d->m_baseNameValueDictionary.hasKey(item.name)) {
|
||||
// We previously unset this!
|
||||
Q_ASSERT(changePos >= 0);
|
||||
// Do not insert a line here as we listed the variable as <UNSET> before!
|
||||
Q_ASSERT(d->m_items.at(changePos).name == item.name);
|
||||
Q_ASSERT(d->m_items.at(changePos).operation == NameValueItem::Unset);
|
||||
Q_ASSERT(d->m_items.at(changePos).value.isEmpty());
|
||||
d->m_items[changePos] = item;
|
||||
emit dataChanged(index(insertPos, 0, QModelIndex()), index(insertPos, 1, QModelIndex()));
|
||||
} else {
|
||||
// We add something that is not in the base dictionary
|
||||
// Insert a new line!
|
||||
beginInsertRows(QModelIndex(), insertPos, insertPos);
|
||||
Q_ASSERT(changePos < 0);
|
||||
d->m_items.append(item);
|
||||
d->updateResultNameValueDictionary();
|
||||
endInsertRows();
|
||||
}
|
||||
emit userChangesChanged();
|
||||
return index(insertPos, 0, QModelIndex());
|
||||
}
|
||||
|
||||
void NameValueModel::resetVariable(const QString &name)
|
||||
{
|
||||
int rowInChanges = d->findInChanges(name);
|
||||
if (rowInChanges < 0)
|
||||
return;
|
||||
|
||||
int rowInResult = d->findInResult(name);
|
||||
if (rowInResult < 0)
|
||||
return;
|
||||
|
||||
if (d->m_baseNameValueDictionary.hasKey(name)) {
|
||||
d->m_items.removeAt(rowInChanges);
|
||||
d->updateResultNameValueDictionary();
|
||||
emit dataChanged(index(rowInResult, 0, QModelIndex()), index(rowInResult, 1, QModelIndex()));
|
||||
emit userChangesChanged();
|
||||
} else {
|
||||
// Remove the line completely!
|
||||
beginRemoveRows(QModelIndex(), rowInResult, rowInResult);
|
||||
d->m_items.removeAt(rowInChanges);
|
||||
d->updateResultNameValueDictionary();
|
||||
endRemoveRows();
|
||||
emit userChangesChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void NameValueModel::unsetVariable(const QString &name)
|
||||
{
|
||||
// This does not change the number of rows as we will display a <UNSET>
|
||||
// in place of the original variable!
|
||||
int row = d->findInResult(name);
|
||||
if (row < 0)
|
||||
return;
|
||||
|
||||
// look in d->m_items for the variable
|
||||
int pos = d->findInChanges(name);
|
||||
if (pos != -1) {
|
||||
d->m_items[pos].operation = NameValueItem::Unset;
|
||||
d->m_items[pos].value.clear();
|
||||
d->updateResultNameValueDictionary();
|
||||
emit dataChanged(index(row, 0, QModelIndex()), index(row, 1, QModelIndex()));
|
||||
emit userChangesChanged();
|
||||
return;
|
||||
}
|
||||
d->m_items.append(NameValueItem(name, QString(), NameValueItem::Unset));
|
||||
d->updateResultNameValueDictionary();
|
||||
emit dataChanged(index(row, 0, QModelIndex()), index(row, 1, QModelIndex()));
|
||||
emit userChangesChanged();
|
||||
}
|
||||
|
||||
bool NameValueModel::canUnset(const QString &name)
|
||||
{
|
||||
int pos = d->findInChanges(name);
|
||||
if (pos != -1)
|
||||
return d->m_items.at(pos).operation == NameValueItem::Unset;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NameValueModel::canReset(const QString &name)
|
||||
{
|
||||
return d->m_baseNameValueDictionary.hasKey(name);
|
||||
}
|
||||
|
||||
NameValueItems NameValueModel::userChanges() const
|
||||
{
|
||||
return d->m_items;
|
||||
}
|
||||
|
||||
void NameValueModel::setUserChanges(const NameValueItems &items)
|
||||
{
|
||||
NameValueItems filtered = Utils::filtered(items, [](const NameValueItem &i) {
|
||||
return i.name != "export " && !i.name.contains('=');
|
||||
});
|
||||
// We assume nobody is reordering the items here.
|
||||
if (filtered == d->m_items)
|
||||
return;
|
||||
beginResetModel();
|
||||
d->m_items = filtered;
|
||||
for (NameValueItem &item : d->m_items) {
|
||||
QString &name = item.name;
|
||||
name = name.trimmed();
|
||||
if (name.startsWith("export "))
|
||||
name = name.mid(7).trimmed();
|
||||
if (d->m_baseNameValueDictionary.osType() == OsTypeWindows) {
|
||||
// NameValueDictionary variable names are case-insensitive under windows, but we still
|
||||
// want to preserve the case of pre-existing variables.
|
||||
auto it = d->m_baseNameValueDictionary.constFind(name);
|
||||
if (it != d->m_baseNameValueDictionary.constEnd())
|
||||
name = d->m_baseNameValueDictionary.key(it);
|
||||
}
|
||||
}
|
||||
|
||||
d->updateResultNameValueDictionary();
|
||||
endResetModel();
|
||||
emit userChangesChanged();
|
||||
}
|
||||
|
||||
} // namespace Utils
|
83
src/libs/utils/namevaluemodel.h
Normal file
83
src/libs/utils/namevaluemodel.h
Normal file
@@ -0,0 +1,83 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "environmentfwd.h"
|
||||
#include "utils_global.h"
|
||||
|
||||
#include <QAbstractTableModel>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace Utils {
|
||||
|
||||
namespace Internal {
|
||||
class NameValueModelPrivate;
|
||||
}
|
||||
|
||||
class QTCREATOR_UTILS_EXPORT NameValueModel : public QAbstractTableModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit NameValueModel(QObject *parent = nullptr);
|
||||
~NameValueModel() override;
|
||||
|
||||
int rowCount(const QModelIndex &parent) const override;
|
||||
int columnCount(const QModelIndex &parent) const override;
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||
QVariant headerData(int section,
|
||||
Qt::Orientation orientation,
|
||||
int role = Qt::DisplayRole) const override;
|
||||
|
||||
QModelIndex addVariable();
|
||||
QModelIndex addVariable(const NameValueItem &item);
|
||||
void resetVariable(const QString &name);
|
||||
void unsetVariable(const QString &name);
|
||||
bool canUnset(const QString &name);
|
||||
bool canReset(const QString &name);
|
||||
QString indexToVariable(const QModelIndex &index) const;
|
||||
QModelIndex variableToIndex(const QString &name) const;
|
||||
bool changes(const QString &key) const;
|
||||
const NameValueDictionary &baseNameValueDictionary() const;
|
||||
void setBaseNameValueDictionary(const NameValueDictionary &dictionary);
|
||||
NameValueItems userChanges() const;
|
||||
void setUserChanges(const NameValueItems &items);
|
||||
|
||||
signals:
|
||||
void userChangesChanged();
|
||||
/// Hint to the view where it should make sense to focus on next
|
||||
// This is a hack since there is no way for a model to suggest
|
||||
// the next interesting place to focus on to the view.
|
||||
void focusIndex(const QModelIndex &index);
|
||||
|
||||
private:
|
||||
std::unique_ptr<Internal::NameValueModelPrivate> d;
|
||||
};
|
||||
|
||||
} // namespace Utils
|
147
src/libs/utils/namevaluesdialog.cpp
Normal file
147
src/libs/utils/namevaluesdialog.cpp
Normal file
@@ -0,0 +1,147 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "namevaluesdialog.h"
|
||||
|
||||
#include <utils/environment.h>
|
||||
#include <utils/hostosinfo.h>
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
#include <QLabel>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
namespace Utils {
|
||||
|
||||
namespace Internal {
|
||||
|
||||
static EnvironmentItems cleanUp(const EnvironmentItems &items)
|
||||
{
|
||||
EnvironmentItems uniqueItems;
|
||||
QSet<QString> uniqueSet;
|
||||
for (int i = items.count() - 1; i >= 0; i--) {
|
||||
EnvironmentItem item = items.at(i);
|
||||
if (HostOsInfo::isWindowsHost())
|
||||
item.name = item.name.toUpper();
|
||||
const QString &itemName = item.name;
|
||||
QString emptyName = itemName;
|
||||
emptyName.remove(QLatin1Char(' '));
|
||||
if (!emptyName.isEmpty() && !uniqueSet.contains(itemName)) {
|
||||
uniqueItems.prepend(item);
|
||||
uniqueSet.insert(itemName);
|
||||
}
|
||||
}
|
||||
return uniqueItems;
|
||||
}
|
||||
|
||||
NameValueItemsWidget::NameValueItemsWidget(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
m_editor = new QPlainTextEdit(this);
|
||||
auto layout = new QVBoxLayout(this);
|
||||
layout->setMargin(0);
|
||||
layout->addWidget(m_editor);
|
||||
}
|
||||
|
||||
void NameValueItemsWidget::setEnvironmentItems(const EnvironmentItems &items)
|
||||
{
|
||||
EnvironmentItems sortedItems = items;
|
||||
EnvironmentItem::sort(&sortedItems);
|
||||
const QStringList list = EnvironmentItem::toStringList(sortedItems);
|
||||
m_editor->document()->setPlainText(list.join(QLatin1Char('\n')));
|
||||
}
|
||||
|
||||
EnvironmentItems NameValueItemsWidget::environmentItems() const
|
||||
{
|
||||
const QStringList list = m_editor->document()->toPlainText().split(QLatin1String("\n"));
|
||||
EnvironmentItems items = EnvironmentItem::fromStringList(list);
|
||||
return cleanUp(items);
|
||||
}
|
||||
|
||||
void NameValueItemsWidget::setPlaceholderText(const QString &text)
|
||||
{
|
||||
m_editor->setPlaceholderText(text);
|
||||
}
|
||||
} // namespace Internal
|
||||
|
||||
NameValuesDialog::NameValuesDialog(const QString &windowTitle, const QString &helpText, QWidget *parent)
|
||||
: QDialog(parent)
|
||||
{
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
resize(640, 480);
|
||||
m_editor = new Internal::NameValueItemsWidget(this);
|
||||
auto box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel,
|
||||
Qt::Horizontal,
|
||||
this);
|
||||
connect(box, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(box, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
|
||||
auto helpLabel = new QLabel(this);
|
||||
helpLabel->setText(helpText);
|
||||
|
||||
auto layout = new QVBoxLayout(this);
|
||||
layout->addWidget(m_editor);
|
||||
layout->addWidget(helpLabel);
|
||||
|
||||
layout->addWidget(box);
|
||||
|
||||
setWindowTitle(windowTitle);
|
||||
}
|
||||
|
||||
void NameValuesDialog::setNameValueItems(const EnvironmentItems &items)
|
||||
{
|
||||
m_editor->setEnvironmentItems(items);
|
||||
}
|
||||
|
||||
EnvironmentItems NameValuesDialog::nameValueItems() const
|
||||
{
|
||||
return m_editor->environmentItems();
|
||||
}
|
||||
|
||||
void NameValuesDialog::setPlaceholderText(const QString &text)
|
||||
{
|
||||
m_editor->setPlaceholderText(text);
|
||||
}
|
||||
|
||||
Utils::optional<NameValueItems> NameValuesDialog::getNameValueItems(QWidget *parent,
|
||||
const NameValueItems &initial,
|
||||
const QString &placeholderText,
|
||||
Polisher polisher,
|
||||
const QString &windowTitle,
|
||||
const QString &helpText)
|
||||
{
|
||||
NameValuesDialog dialog(windowTitle, helpText, parent);
|
||||
if (polisher)
|
||||
polisher(&dialog);
|
||||
dialog.setNameValueItems(initial);
|
||||
dialog.setPlaceholderText(placeholderText);
|
||||
bool result = dialog.exec() == QDialog::Accepted;
|
||||
if (result)
|
||||
return dialog.nameValueItems();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace Utils
|
86
src/libs/utils/namevaluesdialog.h
Normal file
86
src/libs/utils/namevaluesdialog.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "environmentfwd.h"
|
||||
#include "optional.h"
|
||||
#include "utils_global.h"
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QPlainTextEdit;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Utils {
|
||||
|
||||
namespace Internal {
|
||||
class NameValueItemsWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit NameValueItemsWidget(QWidget *parent = nullptr);
|
||||
|
||||
void setEnvironmentItems(const EnvironmentItems &items);
|
||||
EnvironmentItems environmentItems() const;
|
||||
|
||||
void setPlaceholderText(const QString &text);
|
||||
|
||||
private:
|
||||
QPlainTextEdit *m_editor;
|
||||
};
|
||||
} // namespace Internal
|
||||
|
||||
class QTCREATOR_UTILS_EXPORT NameValuesDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
void setNameValueItems(const NameValueItems &items);
|
||||
NameValueItems nameValueItems() const;
|
||||
|
||||
void setPlaceholderText(const QString &text);
|
||||
|
||||
using Polisher = std::function<void(QWidget *)>;
|
||||
static Utils::optional<NameValueItems> getNameValueItems(QWidget *parent = nullptr,
|
||||
const NameValueItems &initial = {},
|
||||
const QString &placeholderText = {},
|
||||
Polisher polish = {},
|
||||
const QString &windowTitle = {},
|
||||
const QString &helpText = {});
|
||||
|
||||
protected:
|
||||
explicit NameValuesDialog(const QString &windowTitle,
|
||||
const QString &helpText,
|
||||
QWidget *parent = {});
|
||||
|
||||
private:
|
||||
Internal::NameValueItemsWidget *m_editor;
|
||||
};
|
||||
|
||||
} // namespace Utils
|
72
src/libs/utils/namevaluevalidator.cpp
Normal file
72
src/libs/utils/namevaluevalidator.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "namevaluevalidator.h"
|
||||
#include "namevaluemodel.h"
|
||||
#include "tooltip/tooltip.h"
|
||||
|
||||
#include <QTreeView>
|
||||
|
||||
namespace Utils {
|
||||
|
||||
NameValueValidator::NameValueValidator(QWidget *parent,
|
||||
Utils::NameValueModel *model,
|
||||
QTreeView *view,
|
||||
const QModelIndex &index,
|
||||
const QString &toolTipText)
|
||||
: QValidator(parent)
|
||||
, m_toolTipText(toolTipText)
|
||||
, m_model(model)
|
||||
, m_view(view)
|
||||
, m_index(index)
|
||||
{
|
||||
m_hideTipTimer.setInterval(2000);
|
||||
m_hideTipTimer.setSingleShot(true);
|
||||
connect(&m_hideTipTimer, &QTimer::timeout, this, []() { Utils::ToolTip::hide(); });
|
||||
}
|
||||
|
||||
QValidator::State NameValueValidator::validate(QString &in, int &pos) const
|
||||
{
|
||||
Q_UNUSED(pos)
|
||||
QModelIndex idx = m_model->variableToIndex(in);
|
||||
if (idx.isValid() && idx != m_index)
|
||||
return QValidator::Intermediate;
|
||||
Utils::ToolTip::hide();
|
||||
m_hideTipTimer.stop();
|
||||
return QValidator::Acceptable;
|
||||
}
|
||||
|
||||
void NameValueValidator::fixup(QString &input) const
|
||||
{
|
||||
Q_UNUSED(input)
|
||||
|
||||
QPoint pos = m_view->mapToGlobal(m_view->visualRect(m_index).topLeft());
|
||||
pos -= Utils::ToolTip::offsetFromPosition();
|
||||
Utils::ToolTip::show(pos, m_toolTipText);
|
||||
m_hideTipTimer.start();
|
||||
// do nothing
|
||||
}
|
||||
|
||||
} // namespace Utils
|
58
src/libs/utils/namevaluevalidator.h
Normal file
58
src/libs/utils/namevaluevalidator.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "environmentfwd.h"
|
||||
#include "utils_global.h"
|
||||
|
||||
#include <QModelIndex>
|
||||
#include <QTimer>
|
||||
#include <QValidator>
|
||||
|
||||
namespace Utils {
|
||||
|
||||
class QTCREATOR_UTILS_EXPORT NameValueValidator : public QValidator
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
NameValueValidator(QWidget *parent,
|
||||
Utils::NameValueModel *model,
|
||||
QTreeView *view,
|
||||
const QModelIndex &index,
|
||||
const QString &toolTipText);
|
||||
|
||||
QValidator::State validate(QString &in, int &pos) const override;
|
||||
|
||||
void fixup(QString &input) const override;
|
||||
|
||||
private:
|
||||
const QString &m_toolTipText;
|
||||
Utils::NameValueModel *m_model;
|
||||
QTreeView *m_view;
|
||||
QModelIndex m_index;
|
||||
mutable QTimer m_hideTipTimer;
|
||||
};
|
||||
} // namespace Utils
|
@@ -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 \
|
||||
|
@@ -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());
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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();
|
||||
|
||||
|
@@ -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
|
||||
|
96
src/plugins/clangpchmanager/clangindexingprojectsettings.cpp
Normal file
96
src/plugins/clangpchmanager/clangindexingprojectsettings.cpp
Normal file
@@ -0,0 +1,96 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "clangindexingprojectsettings.h"
|
||||
|
||||
#include <projectexplorer/project.h>
|
||||
|
||||
namespace ClangPchManager {
|
||||
|
||||
namespace {
|
||||
Utils::NameValueItems fromQVariantMap(const QVariantMap &variantMap,
|
||||
Utils::NameValueItem::Operation operation)
|
||||
{
|
||||
Utils::NameValueItems nameValueItems;
|
||||
nameValueItems.reserve(variantMap.size());
|
||||
|
||||
auto end = variantMap.end();
|
||||
|
||||
for (auto iterator = variantMap.cbegin(); iterator != end; ++iterator) // QMap iterators are broken
|
||||
nameValueItems.push_back({iterator.key(), iterator.value().toString(), operation});
|
||||
|
||||
return nameValueItems;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ClangIndexingProjectSettings::ClangIndexingProjectSettings(ProjectExplorer::Project *project)
|
||||
: m_project(project)
|
||||
{}
|
||||
|
||||
void ClangIndexingProjectSettings::saveMacros(const Utils::NameValueItems &items)
|
||||
{
|
||||
QVariantMap unsets;
|
||||
QVariantMap sets;
|
||||
|
||||
for (const Utils::NameValueItem &item : items) {
|
||||
using Operation = Utils::NameValueItem::Operation;
|
||||
switch (item.operation) {
|
||||
case Operation::Set:
|
||||
sets[item.name] = item.value;
|
||||
break;
|
||||
case Operation::Unset:
|
||||
unsets[item.name] = item.value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (sets.size())
|
||||
m_project->setNamedSettings("set_indexing_macro", sets);
|
||||
else
|
||||
m_project->setNamedSettings("set_indexing_macro", {});
|
||||
|
||||
if (unsets.size())
|
||||
m_project->setNamedSettings("unset_indexing_macro", unsets);
|
||||
else
|
||||
m_project->setNamedSettings("unset_indexing_macro", {});
|
||||
}
|
||||
|
||||
Utils::NameValueItems ClangIndexingProjectSettings::readMacros() const
|
||||
{
|
||||
QVariant unsets = m_project->namedSettings("unset_indexing_macro");
|
||||
|
||||
Utils::NameValueItems items = fromQVariantMap(unsets.toMap(), Utils::NameValueItem::Unset);
|
||||
|
||||
QVariant sets = m_project->namedSettings("set_indexing_macro");
|
||||
|
||||
items += fromQVariantMap(sets.toMap(), Utils::NameValueItem::Set);
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
} // namespace ClangPchManager
|
50
src/plugins/clangpchmanager/clangindexingprojectsettings.h
Normal file
50
src/plugins/clangpchmanager/clangindexingprojectsettings.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "clangpchmanager_global.h"
|
||||
|
||||
#include <utils/namevalueitem.h>
|
||||
|
||||
namespace ProjectExplorer {
|
||||
class Project;
|
||||
}
|
||||
|
||||
namespace ClangPchManager {
|
||||
|
||||
class CLANGPCHMANAGER_EXPORT ClangIndexingProjectSettings
|
||||
{
|
||||
public:
|
||||
ClangIndexingProjectSettings(ProjectExplorer::Project *project);
|
||||
|
||||
Utils::NameValueItems readMacros() const;
|
||||
void saveMacros(const Utils::NameValueItems &items);
|
||||
|
||||
private:
|
||||
ProjectExplorer::Project *m_project;
|
||||
};
|
||||
|
||||
} // namespace ClangPchManager
|
@@ -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
|
@@ -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
|
@@ -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>
|
55
src/plugins/clangpchmanager/clangindexingsettingsmanager.cpp
Normal file
55
src/plugins/clangpchmanager/clangindexingsettingsmanager.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "clangindexingsettingsmanager.h"
|
||||
|
||||
#include "clangindexingprojectsettings.h"
|
||||
|
||||
namespace ClangPchManager {
|
||||
|
||||
ClangIndexingSettingsManager::ClangIndexingSettingsManager() = default;
|
||||
|
||||
ClangIndexingSettingsManager::~ClangIndexingSettingsManager() = default;
|
||||
|
||||
ClangIndexingProjectSettings *ClangIndexingSettingsManager::settings(ProjectExplorer::Project *project)
|
||||
{
|
||||
auto &setting = m_settings[project];
|
||||
|
||||
if (!setting)
|
||||
setting = std::make_unique<ClangIndexingProjectSettings>(project);
|
||||
|
||||
return setting.get();
|
||||
}
|
||||
|
||||
void ClangIndexingSettingsManager::remove(ProjectExplorer::Project *project)
|
||||
{
|
||||
m_settings.erase(project);
|
||||
}
|
||||
|
||||
bool ClangIndexingSettingsManager::hasSettings(ProjectExplorer::Project *project) const
|
||||
{
|
||||
return m_settings.find(project) != m_settings.end();
|
||||
}
|
||||
} // namespace ClangPchManager
|
56
src/plugins/clangpchmanager/clangindexingsettingsmanager.h
Normal file
56
src/plugins/clangpchmanager/clangindexingsettingsmanager.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "clangpchmanager_global.h"
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
namespace ProjectExplorer {
|
||||
class Project;
|
||||
}
|
||||
|
||||
namespace ClangPchManager {
|
||||
|
||||
class ClangIndexingProjectSettings;
|
||||
|
||||
class CLANGPCHMANAGER_EXPORT ClangIndexingSettingsManager
|
||||
{
|
||||
public:
|
||||
ClangIndexingSettingsManager();
|
||||
~ClangIndexingSettingsManager();
|
||||
|
||||
ClangIndexingProjectSettings *settings(ProjectExplorer::Project *project);
|
||||
void remove(ProjectExplorer::Project *project);
|
||||
|
||||
bool hasSettings(ProjectExplorer::Project *project) const;
|
||||
|
||||
private:
|
||||
std::map<ProjectExplorer::Project *, std::unique_ptr<ClangIndexingProjectSettings>> m_settings;
|
||||
};
|
||||
|
||||
} // namespace ClangPchManager
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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();
|
||||
|
@@ -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)
|
||||
{}
|
||||
|
||||
|
68
src/plugins/clangpchmanager/preprocessormacrocollector.cpp
Normal file
68
src/plugins/clangpchmanager/preprocessormacrocollector.cpp
Normal file
@@ -0,0 +1,68 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "preprocessormacrocollector.h"
|
||||
|
||||
namespace ClangPchManager {
|
||||
|
||||
namespace {
|
||||
PreprocessorMacros toSortedMacros(const ProjectExplorer::Macros ¯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
|
51
src/plugins/clangpchmanager/preprocessormacrocollector.h
Normal file
51
src/plugins/clangpchmanager/preprocessormacrocollector.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
|
||||
#include <projectexplorer/projectmacro.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace ClangPchManager {
|
||||
|
||||
using PreprocessorMacro = std::pair<QString, QString>;
|
||||
using PreprocessorMacros = QVector<PreprocessorMacro>;
|
||||
|
||||
class PreprocessorMacroCollector
|
||||
{
|
||||
public:
|
||||
void add(const ProjectExplorer::Macros ¯os);
|
||||
|
||||
const PreprocessorMacros ¯os() const;
|
||||
|
||||
private:
|
||||
PreprocessorMacros m_macros;
|
||||
};
|
||||
|
||||
} // namespace ClangPchManager
|
282
src/plugins/clangpchmanager/preprocessormacrowidget.cpp
Normal file
282
src/plugins/clangpchmanager/preprocessormacrowidget.cpp
Normal file
@@ -0,0 +1,282 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "preprocessormacrowidget.h"
|
||||
#include "clangindexingprojectsettings.h"
|
||||
|
||||
#include <utils/detailswidget.h>
|
||||
#include <utils/headerviewstretcher.h>
|
||||
#include <utils/itemviews.h>
|
||||
#include <utils/namevaluedictionary.h>
|
||||
#include <utils/namevalueitem.h>
|
||||
#include <utils/namevaluemodel.h>
|
||||
#include <utils/namevaluesdialog.h>
|
||||
#include <utils/namevaluevalidator.h>
|
||||
|
||||
#include <coreplugin/find/itemviewfind.h>
|
||||
|
||||
#include <QLineEdit>
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
namespace ClangPchManager {
|
||||
|
||||
class ProcessorMacroDelegate : public QStyledItemDelegate
|
||||
{
|
||||
public:
|
||||
ProcessorMacroDelegate(Utils::NameValueModel *model, QTreeView *view)
|
||||
: QStyledItemDelegate(view)
|
||||
, m_model(model)
|
||||
, m_view(view)
|
||||
{}
|
||||
|
||||
QWidget *createEditor(QWidget *parent,
|
||||
const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const override
|
||||
{
|
||||
QWidget *w = QStyledItemDelegate::createEditor(parent, option, index);
|
||||
if (index.column() != 0)
|
||||
return w;
|
||||
|
||||
if (auto edit = qobject_cast<QLineEdit *>(w))
|
||||
edit->setValidator(new Utils::NameValueValidator(
|
||||
edit, m_model, m_view, index, PreprocessorMacroWidget::tr("Macro already exists.")));
|
||||
return w;
|
||||
}
|
||||
|
||||
private:
|
||||
Utils::NameValueModel *m_model;
|
||||
QTreeView *m_view;
|
||||
};
|
||||
|
||||
PreprocessorMacroWidget::PreprocessorMacroWidget(QWidget *parent) : QWidget(parent)
|
||||
{
|
||||
m_model = std::make_unique<Utils::NameValueModel>();
|
||||
connect(m_model.get(),
|
||||
&Utils::NameValueModel::userChangesChanged,
|
||||
this,
|
||||
&PreprocessorMacroWidget::userChangesChanged);
|
||||
connect(m_model.get(),
|
||||
&QAbstractItemModel::modelReset,
|
||||
this,
|
||||
&PreprocessorMacroWidget::invalidateCurrentIndex);
|
||||
|
||||
connect(m_model.get(), &Utils::NameValueModel::focusIndex, this, &PreprocessorMacroWidget::focusIndex);
|
||||
|
||||
auto vbox = new QVBoxLayout(this);
|
||||
vbox->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
m_detailsContainer = new Utils::DetailsWidget(this);
|
||||
|
||||
auto details = new QWidget(m_detailsContainer);
|
||||
m_detailsContainer->setWidget(details);
|
||||
details->setVisible(false);
|
||||
|
||||
auto vbox2 = new QVBoxLayout(details);
|
||||
vbox2->setMargin(0);
|
||||
|
||||
auto horizontalLayout = new QHBoxLayout;
|
||||
horizontalLayout->setMargin(0);
|
||||
auto tree = new Utils::TreeView(this);
|
||||
connect(tree, &QAbstractItemView::activated, tree, [tree](const QModelIndex &idx) {
|
||||
tree->edit(idx);
|
||||
});
|
||||
m_preprocessorMacrosView = tree;
|
||||
m_preprocessorMacrosView->setModel(m_model.get());
|
||||
m_preprocessorMacrosView->setItemDelegate(
|
||||
new ProcessorMacroDelegate(m_model.get(), m_preprocessorMacrosView));
|
||||
m_preprocessorMacrosView->setMinimumHeight(400);
|
||||
m_preprocessorMacrosView->setRootIsDecorated(false);
|
||||
m_preprocessorMacrosView->setUniformRowHeights(true);
|
||||
new Utils::HeaderViewStretcher(m_preprocessorMacrosView->header(), 1);
|
||||
m_preprocessorMacrosView->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
m_preprocessorMacrosView->setSelectionBehavior(QAbstractItemView::SelectItems);
|
||||
m_preprocessorMacrosView->setFrameShape(QFrame::NoFrame);
|
||||
QFrame *findWrapper = Core::ItemViewFind::createSearchableWrapper(m_preprocessorMacrosView,
|
||||
Core::ItemViewFind::LightColored);
|
||||
findWrapper->setFrameStyle(QFrame::StyledPanel);
|
||||
horizontalLayout->addWidget(findWrapper);
|
||||
|
||||
auto buttonLayout = new QVBoxLayout();
|
||||
|
||||
m_editButton = new QPushButton(this);
|
||||
m_editButton->setText(tr("Ed&it"));
|
||||
buttonLayout->addWidget(m_editButton);
|
||||
|
||||
m_addButton = new QPushButton(this);
|
||||
m_addButton->setText(tr("&Add"));
|
||||
buttonLayout->addWidget(m_addButton);
|
||||
|
||||
m_resetButton = new QPushButton(this);
|
||||
m_resetButton->setEnabled(false);
|
||||
m_resetButton->setText(tr("&Reset"));
|
||||
buttonLayout->addWidget(m_resetButton);
|
||||
|
||||
m_unsetButton = new QPushButton(this);
|
||||
m_unsetButton->setEnabled(false);
|
||||
m_unsetButton->setText(tr("&Unset"));
|
||||
buttonLayout->addWidget(m_unsetButton);
|
||||
|
||||
buttonLayout->addStretch();
|
||||
|
||||
horizontalLayout->addLayout(buttonLayout);
|
||||
vbox2->addLayout(horizontalLayout);
|
||||
|
||||
vbox->addWidget(m_detailsContainer);
|
||||
|
||||
connect(m_model.get(),
|
||||
&QAbstractItemModel::dataChanged,
|
||||
this,
|
||||
&PreprocessorMacroWidget::updateButtons);
|
||||
|
||||
connect(m_editButton, &QAbstractButton::clicked, this, &PreprocessorMacroWidget::editButtonClicked);
|
||||
connect(m_addButton, &QAbstractButton::clicked, this, &PreprocessorMacroWidget::addButtonClicked);
|
||||
connect(m_resetButton, &QAbstractButton::clicked, this, &PreprocessorMacroWidget::removeButtonClicked);
|
||||
connect(m_unsetButton, &QAbstractButton::clicked, this, &PreprocessorMacroWidget::unsetButtonClicked);
|
||||
connect(m_preprocessorMacrosView->selectionModel(),
|
||||
&QItemSelectionModel::currentChanged,
|
||||
this,
|
||||
&PreprocessorMacroWidget::currentIndexChanged);
|
||||
connect(m_detailsContainer,
|
||||
&Utils::DetailsWidget::linkActivated,
|
||||
this,
|
||||
&PreprocessorMacroWidget::linkActivated);
|
||||
connect(m_model.get(),
|
||||
&Utils::NameValueModel::userChangesChanged,
|
||||
this,
|
||||
&PreprocessorMacroWidget::updateSummaryText);
|
||||
connect(m_model.get(),
|
||||
&Utils::NameValueModel::userChangesChanged,
|
||||
this,
|
||||
&PreprocessorMacroWidget::saveSettings);
|
||||
}
|
||||
|
||||
void PreprocessorMacroWidget::setBasePreprocessorMacros(const PreprocessorMacros ¯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
|
79
src/plugins/clangpchmanager/preprocessormacrowidget.h
Normal file
79
src/plugins/clangpchmanager/preprocessormacrowidget.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QTreeView>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace Utils {
|
||||
class DetailsWidget;
|
||||
class NameValueModel;
|
||||
} // namespace Utils
|
||||
|
||||
namespace ClangPchManager {
|
||||
class ClangIndexingProjectSettings;
|
||||
using PreprocessorMacro = std::pair<QString, QString>;
|
||||
using PreprocessorMacros = QVector<PreprocessorMacro>;
|
||||
|
||||
class PreprocessorMacroWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit PreprocessorMacroWidget(QWidget *parent = nullptr);
|
||||
~PreprocessorMacroWidget() override;
|
||||
void setBasePreprocessorMacros(const PreprocessorMacros ¯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
|
@@ -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),
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
}
|
||||
|
||||
|
@@ -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();
|
||||
|
||||
|
@@ -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();
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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)
|
||||
{
|
||||
|
@@ -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()
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
|
||||
{
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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"
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -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));
|
||||
|
@@ -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
|
||||
|
@@ -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) {
|
||||
|
@@ -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.
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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
|
||||
};
|
||||
|
||||
|
@@ -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();
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -107,7 +107,7 @@ FilePath QnxConfiguration::qccCompilerPath() const
|
||||
return m_qccCompiler;
|
||||
}
|
||||
|
||||
QList<EnvironmentItem> QnxConfiguration::qnxEnv() const
|
||||
EnvironmentItems QnxConfiguration::qnxEnv() const
|
||||
{
|
||||
return m_qnxEnv;
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -184,7 +184,7 @@ void QnxQtVersion::updateEnvironment() const
|
||||
}
|
||||
}
|
||||
|
||||
QList<Utils::EnvironmentItem> QnxQtVersion::environment() const
|
||||
Utils::EnvironmentItems QnxQtVersion::environment() const
|
||||
{
|
||||
return QnxUtils::qnxEnvironment(sdpPath());
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
@@ -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));
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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 \
|
||||
|
@@ -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
|
||||
|
83
tests/unit/unittest/clangindexingsettingsmanager-test.cpp
Normal file
83
tests/unit/unittest/clangindexingsettingsmanager-test.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "googletest.h"
|
||||
|
||||
#include <clangindexingsettingsmanager.h>
|
||||
#include <projectexplorer/project.h>
|
||||
|
||||
namespace {
|
||||
class ClangIndexingSettingsManager : public testing::Test
|
||||
{
|
||||
protected:
|
||||
ClangPchManager::ClangIndexingSettingsManager manager;
|
||||
ProjectExplorer::Project project;
|
||||
ProjectExplorer::Project project2;
|
||||
};
|
||||
|
||||
TEST_F(ClangIndexingSettingsManager, FetchSettings)
|
||||
{
|
||||
auto setting = manager.settings(&project);
|
||||
|
||||
ASSERT_THAT(setting, Not(IsNull()));
|
||||
}
|
||||
|
||||
TEST_F(ClangIndexingSettingsManager, SettingsAreTheSameForTheSameProject)
|
||||
{
|
||||
auto setting1 = manager.settings(&project);
|
||||
|
||||
auto setting2 = manager.settings(&project);
|
||||
|
||||
ASSERT_THAT(setting1, Eq(setting2));
|
||||
}
|
||||
|
||||
TEST_F(ClangIndexingSettingsManager, SettingsAreTheDifferentForDifferentProjects)
|
||||
{
|
||||
manager.settings(&project);
|
||||
manager.settings(&project2);
|
||||
|
||||
auto setting1 = manager.settings(&project);
|
||||
|
||||
auto setting2 = manager.settings(&project2);
|
||||
|
||||
ASSERT_THAT(setting1, Not(Eq(setting2)));
|
||||
}
|
||||
|
||||
TEST_F(ClangIndexingSettingsManager, RemoveSettings)
|
||||
{
|
||||
manager.settings(&project);
|
||||
|
||||
manager.remove(&project);
|
||||
|
||||
ASSERT_FALSE(manager.hasSettings(&project));
|
||||
}
|
||||
|
||||
TEST_F(ClangIndexingSettingsManager, RemoveNonExistingSettings)
|
||||
{
|
||||
manager.remove(&project);
|
||||
|
||||
ASSERT_FALSE(manager.hasSettings(&project));
|
||||
}
|
||||
} // namespace
|
@@ -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}}};
|
||||
|
71
tests/unit/unittest/preprocessormacrocollector-test.cpp
Normal file
71
tests/unit/unittest/preprocessormacrocollector-test.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "googletest.h"
|
||||
|
||||
#include <preprocessormacrocollector.h>
|
||||
|
||||
namespace {
|
||||
|
||||
using ProjectExplorer::Macros;
|
||||
using ProjectExplorer::MacroType;
|
||||
using PM = ClangPchManager::PreprocessorMacro;
|
||||
|
||||
class PreprocessorMacrosManager : public testing::Test
|
||||
{
|
||||
protected:
|
||||
Macros macros1{{"yi", "1"}, {"er", "2"}};
|
||||
Macros macros2{{"san", "3"}, {"se", "4"}};
|
||||
Macros macrosWithDuplicates{{"yi", "1"}, {"san", "3"}, {"se", "4"}, {"er", "0"}};
|
||||
ClangPchManager::PreprocessorMacroCollector manager;
|
||||
};
|
||||
|
||||
TEST_F(PreprocessorMacrosManager, Add)
|
||||
{
|
||||
manager.add(macros1);
|
||||
|
||||
ASSERT_THAT(manager.macros(), ElementsAre(PM{"er", "2"}, PM{"yi", "1"}));
|
||||
}
|
||||
|
||||
TEST_F(PreprocessorMacrosManager, AddMore)
|
||||
{
|
||||
manager.add(macros1);
|
||||
|
||||
manager.add(macros2);
|
||||
|
||||
ASSERT_THAT(manager.macros(),
|
||||
ElementsAre(PM{"er", "2"}, PM{"san", "3"}, PM{"se", "4"}, PM{"yi", "1"}));
|
||||
}
|
||||
|
||||
TEST_F(PreprocessorMacrosManager, FilterDuplicates)
|
||||
{
|
||||
manager.add(macros1);
|
||||
|
||||
manager.add(macrosWithDuplicates);
|
||||
|
||||
ASSERT_THAT(manager.macros(),
|
||||
ElementsAre(PM{"er", "0"}, PM{"er", "2"}, PM{"san", "3"}, PM{"se", "4"}, PM{"yi", "1"}));
|
||||
}
|
||||
} // namespace
|
@@ -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;
|
||||
|
@@ -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;
|
||||
};
|
||||
|
||||
|
@@ -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 \
|
||||
|
Reference in New Issue
Block a user