forked from qt-creator/qt-creator
Clangd: Add per-project settings
Users might want to use clangd for certain project, but not for others. Change-Id: Id29ce3349f0acd359cf7c824ece073b147ed2280 Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
@@ -30,6 +30,7 @@
|
||||
#include "cpptoolsreuse.h"
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
#include <projectexplorer/project.h>
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/qtcassert.h>
|
||||
@@ -63,10 +64,12 @@ static QString skipIndexingBigFilesKey()
|
||||
static QString indexerFileSizeLimitKey()
|
||||
{ return QLatin1String(Constants::CPPTOOLS_INDEXER_FILE_SIZE_LIMIT); }
|
||||
|
||||
static QString clangdSettingsKey() { return QLatin1String("ClangdSettings"); }
|
||||
static QString useClangdKey() { return QLatin1String("UseClangd"); }
|
||||
static QString clangdPathKey() { return QLatin1String("ClangdPath"); }
|
||||
static QString clangdIndexingKey() { return QLatin1String("ClangdIndexing"); }
|
||||
static QString clangdThreadLimitKey() { return QLatin1String("ClangdThreadLimit"); }
|
||||
static QString clangdUseGlobalSettingsKey() { return QLatin1String("useGlobalSettings"); }
|
||||
|
||||
static FilePath g_defaultClangdFilePath;
|
||||
static FilePath fallbackClangdFilePath()
|
||||
@@ -323,37 +326,29 @@ void ClangdSettings::setDefaultClangdPath(const Utils::FilePath &filePath)
|
||||
g_defaultClangdFilePath = filePath;
|
||||
}
|
||||
|
||||
FilePath ClangdSettings::clangdFilePath()
|
||||
FilePath ClangdSettings::clangdFilePath() const
|
||||
{
|
||||
if (!instance().m_data.executableFilePath.isEmpty())
|
||||
return instance().m_data.executableFilePath;
|
||||
if (!m_data.executableFilePath.isEmpty())
|
||||
return m_data.executableFilePath;
|
||||
return fallbackClangdFilePath();
|
||||
}
|
||||
|
||||
void ClangdSettings::setData(const Data &data)
|
||||
{
|
||||
if (data != instance().m_data) {
|
||||
instance().m_data = data;
|
||||
instance().saveSettings();
|
||||
if (this == &instance() && data != m_data) {
|
||||
m_data = data;
|
||||
saveSettings();
|
||||
}
|
||||
}
|
||||
|
||||
void ClangdSettings::loadSettings()
|
||||
{
|
||||
QSettings * const s = Core::ICore::settings();
|
||||
m_data.useClangd = s->value(useClangdKey(), false).toBool();
|
||||
m_data.executableFilePath = FilePath::fromString(s->value(clangdPathKey()).toString());
|
||||
m_data.enableIndexing = s->value(clangdIndexingKey(), true).toBool();
|
||||
m_data.workerThreadLimit = s->value(clangdThreadLimitKey(), 0).toInt();
|
||||
m_data.fromMap(Core::ICore::settings()->value(clangdSettingsKey()).toMap());
|
||||
}
|
||||
|
||||
void ClangdSettings::saveSettings()
|
||||
{
|
||||
QSettings * const s = Core::ICore::settings();
|
||||
s->setValue(useClangdKey(), useClangd());
|
||||
s->setValue(clangdPathKey(), m_data.executableFilePath.toString());
|
||||
s->setValue(clangdIndexingKey(), m_data.enableIndexing);
|
||||
s->setValue(clangdThreadLimitKey(), m_data.workerThreadLimit);
|
||||
Core::ICore::settings()->setValue(clangdSettingsKey(), m_data.toMap());
|
||||
}
|
||||
|
||||
#ifdef WITH_TESTS
|
||||
@@ -363,3 +358,66 @@ void ClangdSettings::setClangdFilePath(const Utils::FilePath &filePath)
|
||||
instance().m_data.executableFilePath = filePath;
|
||||
}
|
||||
#endif
|
||||
|
||||
ClangdProjectSettings::ClangdProjectSettings(ProjectExplorer::Project *project) : m_project(project)
|
||||
{
|
||||
loadSettings();
|
||||
}
|
||||
|
||||
ClangdSettings ClangdProjectSettings::settings() const
|
||||
{
|
||||
if (m_useGlobalSettings)
|
||||
return ClangdSettings::instance();
|
||||
return ClangdSettings(m_customSettings);
|
||||
}
|
||||
|
||||
void ClangdProjectSettings::setSettings(const ClangdSettings::Data &data)
|
||||
{
|
||||
m_customSettings = data;
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
void ClangdProjectSettings::setUseGlobalSettings(bool useGlobal)
|
||||
{
|
||||
m_useGlobalSettings = useGlobal;
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
void ClangdProjectSettings::loadSettings()
|
||||
{
|
||||
if (!m_project)
|
||||
return;
|
||||
const QVariantMap data = m_project->namedSettings(clangdSettingsKey()).toMap();
|
||||
m_useGlobalSettings = data.value(clangdUseGlobalSettingsKey(), true).toBool();
|
||||
if (!m_useGlobalSettings)
|
||||
m_customSettings.fromMap(data);
|
||||
}
|
||||
|
||||
void ClangdProjectSettings::saveSettings()
|
||||
{
|
||||
if (!m_project)
|
||||
return;
|
||||
QVariantMap data;
|
||||
if (!m_useGlobalSettings)
|
||||
data = m_customSettings.toMap();
|
||||
data.insert(clangdUseGlobalSettingsKey(), m_useGlobalSettings);
|
||||
m_project->setNamedSettings(clangdSettingsKey(), data);
|
||||
}
|
||||
|
||||
QVariantMap ClangdSettings::Data::toMap() const
|
||||
{
|
||||
QVariantMap map;
|
||||
map.insert(useClangdKey(), useClangd);
|
||||
map.insert(clangdPathKey(), executableFilePath.toString());
|
||||
map.insert(clangdIndexingKey(), enableIndexing);
|
||||
map.insert(clangdThreadLimitKey(), workerThreadLimit);
|
||||
return map;
|
||||
}
|
||||
|
||||
void ClangdSettings::Data::fromMap(const QVariantMap &map)
|
||||
{
|
||||
useClangd = map.value(useClangdKey(), false).toBool();
|
||||
executableFilePath = FilePath::fromString(map.value(clangdPathKey()).toString());
|
||||
enableIndexing = map.value(clangdIndexingKey(), true).toBool();
|
||||
workerThreadLimit = map.value(clangdThreadLimitKey(), 0).toInt();
|
||||
}
|
||||
|
||||
@@ -38,6 +38,8 @@ QT_BEGIN_NAMESPACE
|
||||
class QSettings;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace ProjectExplorer { class Project; }
|
||||
|
||||
namespace CppTools {
|
||||
|
||||
class CPPTOOLS_EXPORT CppCodeModelSettings : public QObject
|
||||
@@ -102,21 +104,27 @@ public:
|
||||
class Data
|
||||
{
|
||||
public:
|
||||
QVariantMap toMap() const;
|
||||
void fromMap(const QVariantMap &map);
|
||||
|
||||
Utils::FilePath executableFilePath;
|
||||
int workerThreadLimit = 0;
|
||||
bool useClangd = false;
|
||||
bool enableIndexing = true;
|
||||
};
|
||||
|
||||
static bool useClangd() { return instance().m_data.useClangd; }
|
||||
ClangdSettings(const Data &data) : m_data(data) {}
|
||||
|
||||
static ClangdSettings &instance();
|
||||
bool useClangd() const { return m_data.useClangd; }
|
||||
|
||||
static void setDefaultClangdPath(const Utils::FilePath &filePath);
|
||||
static Utils::FilePath clangdFilePath();
|
||||
static bool indexingEnabled() { return instance().m_data.enableIndexing; }
|
||||
static int workerThreadLimit() { return instance().m_data.workerThreadLimit; }
|
||||
Utils::FilePath clangdFilePath() const;
|
||||
bool indexingEnabled() const { return m_data.enableIndexing; }
|
||||
int workerThreadLimit() const { return m_data.workerThreadLimit; }
|
||||
|
||||
static void setData(const Data &data);
|
||||
static Data data() { return instance().m_data; }
|
||||
void setData(const Data &data);
|
||||
Data data() const { return m_data; }
|
||||
|
||||
#ifdef WITH_TESTS
|
||||
static void setUseClangd(bool use);
|
||||
@@ -125,7 +133,6 @@ public:
|
||||
|
||||
private:
|
||||
ClangdSettings() { loadSettings(); }
|
||||
static ClangdSettings &instance();
|
||||
|
||||
void loadSettings();
|
||||
void saveSettings();
|
||||
@@ -133,4 +140,23 @@ private:
|
||||
Data m_data;
|
||||
};
|
||||
|
||||
class CPPTOOLS_EXPORT ClangdProjectSettings
|
||||
{
|
||||
public:
|
||||
ClangdProjectSettings(ProjectExplorer::Project *project);
|
||||
|
||||
ClangdSettings settings() const;
|
||||
void setSettings(const ClangdSettings::Data &data);
|
||||
bool useGlobalSettings() const { return m_useGlobalSettings; }
|
||||
void setUseGlobalSettings(bool useGlobal);
|
||||
|
||||
private:
|
||||
void loadSettings();
|
||||
void saveSettings();
|
||||
|
||||
ProjectExplorer::Project * const m_project;
|
||||
ClangdSettings::Data m_customSettings;
|
||||
bool m_useGlobalSettings = true;
|
||||
};
|
||||
|
||||
} // namespace CppTools
|
||||
|
||||
@@ -191,71 +191,99 @@ CppCodeModelSettingsPage::CppCodeModelSettingsPage(CppCodeModelSettings *setting
|
||||
setWidgetCreator([settings] { return new CppCodeModelSettingsWidget(settings); });
|
||||
}
|
||||
|
||||
class ClangdSettingsWidget::Private
|
||||
{
|
||||
public:
|
||||
QCheckBox useClangdCheckBox;
|
||||
QCheckBox indexingCheckBox;
|
||||
QSpinBox threadLimitSpinBox;
|
||||
Utils::PathChooser clangdChooser;
|
||||
};
|
||||
|
||||
class ClangdSettingsWidget final : public Core::IOptionsPageWidget
|
||||
ClangdSettingsWidget::ClangdSettingsWidget(const ClangdSettings &settings) : d(new Private)
|
||||
{
|
||||
d->useClangdCheckBox.setText(tr("Use clangd (EXPERIMENTAL)"));
|
||||
d->useClangdCheckBox.setChecked(settings.useClangd());
|
||||
d->useClangdCheckBox.setToolTip(tr("Changing this option does not affect projects "
|
||||
"that are already open."));
|
||||
d->clangdChooser.setExpectedKind(Utils::PathChooser::ExistingCommand);
|
||||
d->clangdChooser.setFilePath(settings.clangdFilePath());
|
||||
d->clangdChooser.setEnabled(d->useClangdCheckBox.isChecked());
|
||||
d->indexingCheckBox.setChecked(settings.indexingEnabled());
|
||||
d->indexingCheckBox.setToolTip(tr(
|
||||
"If background indexing is enabled, global symbol searches will yield\n"
|
||||
"more accurate results, at the cost of additional CPU load when\n"
|
||||
"the project is first opened."));
|
||||
d->threadLimitSpinBox.setValue(settings.workerThreadLimit());
|
||||
d->threadLimitSpinBox.setSpecialValueText("Automatic");
|
||||
|
||||
const auto layout = new QVBoxLayout(this);
|
||||
layout->addWidget(&d->useClangdCheckBox);
|
||||
const auto formLayout = new QFormLayout;
|
||||
const auto chooserLabel = new QLabel(tr("Path to executable:"));
|
||||
formLayout->addRow(chooserLabel, &d->clangdChooser);
|
||||
const auto indexingLabel = new QLabel(tr("Enable background indexing:"));
|
||||
formLayout->addRow(indexingLabel, &d->indexingCheckBox);
|
||||
const auto threadLimitLayout = new QHBoxLayout;
|
||||
threadLimitLayout->addWidget(&d->threadLimitSpinBox);
|
||||
threadLimitLayout->addStretch(1);
|
||||
const auto threadLimitLabel = new QLabel(tr("Set worker thread count:"));
|
||||
formLayout->addRow(threadLimitLabel, threadLimitLayout);
|
||||
layout->addLayout(formLayout);
|
||||
layout->addStretch(1);
|
||||
|
||||
const auto toggleEnabled = [=](const bool checked) {
|
||||
chooserLabel->setEnabled(checked);
|
||||
d->clangdChooser.setEnabled(checked);
|
||||
indexingLabel->setEnabled(checked);
|
||||
d->indexingCheckBox.setEnabled(checked);
|
||||
d->threadLimitSpinBox.setEnabled(checked);
|
||||
};
|
||||
connect(&d->useClangdCheckBox, &QCheckBox::toggled, toggleEnabled);
|
||||
toggleEnabled(d->useClangdCheckBox.isChecked());
|
||||
d->threadLimitSpinBox.setEnabled(d->useClangdCheckBox.isChecked());
|
||||
|
||||
connect(&d->useClangdCheckBox, &QCheckBox::toggled,
|
||||
this, &ClangdSettingsWidget::settingsDataChanged);
|
||||
connect(&d->indexingCheckBox, &QCheckBox::toggled,
|
||||
this, &ClangdSettingsWidget::settingsDataChanged);
|
||||
connect(&d->threadLimitSpinBox, qOverload<int>(&QSpinBox::valueChanged),
|
||||
this, &ClangdSettingsWidget::settingsDataChanged);
|
||||
connect(&d->clangdChooser, &Utils::PathChooser::pathChanged,
|
||||
this, &ClangdSettingsWidget::settingsDataChanged);
|
||||
}
|
||||
|
||||
ClangdSettingsWidget::~ClangdSettingsWidget()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
ClangdSettings::Data ClangdSettingsWidget::settingsData() const
|
||||
{
|
||||
ClangdSettings::Data data;
|
||||
data.useClangd = d->useClangdCheckBox.isChecked();
|
||||
data.executableFilePath = d->clangdChooser.filePath();
|
||||
data.enableIndexing = d->indexingCheckBox.isChecked();
|
||||
data.workerThreadLimit = d->threadLimitSpinBox.value();
|
||||
return data;
|
||||
}
|
||||
|
||||
class ClangdSettingsPageWidget final : public Core::IOptionsPageWidget
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(CppTools::Internal::ClangdSettingsWidget)
|
||||
|
||||
public:
|
||||
ClangdSettingsWidget()
|
||||
ClangdSettingsPageWidget() : m_widget(ClangdSettings::instance())
|
||||
{
|
||||
m_useClangdCheckBox.setText(tr("Use clangd (EXPERIMENTAL)"));
|
||||
m_useClangdCheckBox.setChecked(ClangdSettings::useClangd());
|
||||
m_useClangdCheckBox.setToolTip(tr("Changing this option does not affect projects "
|
||||
"that are already open."));
|
||||
m_clangdChooser.setExpectedKind(Utils::PathChooser::ExistingCommand);
|
||||
m_clangdChooser.setFilePath(ClangdSettings::clangdFilePath());
|
||||
m_clangdChooser.setEnabled(m_useClangdCheckBox.isChecked());
|
||||
m_indexingCheckBox.setChecked(ClangdSettings::indexingEnabled());
|
||||
m_indexingCheckBox.setToolTip(tr(
|
||||
"If background indexing is enabled, global symbol searches will yield\n"
|
||||
"more accurate results, at the cost of additional CPU load when\n"
|
||||
"the project is first opened."));
|
||||
m_threadLimitSpinBox.setValue(ClangdSettings::workerThreadLimit());
|
||||
m_threadLimitSpinBox.setSpecialValueText("Automatic");
|
||||
|
||||
const auto layout = new QVBoxLayout(this);
|
||||
layout->addWidget(&m_useClangdCheckBox);
|
||||
const auto formLayout = new QFormLayout;
|
||||
const auto chooserLabel = new QLabel(tr("Path to executable:"));
|
||||
formLayout->addRow(chooserLabel, &m_clangdChooser);
|
||||
const auto indexingLabel = new QLabel(tr("Enable background indexing:"));
|
||||
formLayout->addRow(indexingLabel, &m_indexingCheckBox);
|
||||
const auto threadLimitLayout = new QHBoxLayout;
|
||||
threadLimitLayout->addWidget(&m_threadLimitSpinBox);
|
||||
threadLimitLayout->addStretch(1);
|
||||
const auto threadLimitLabel = new QLabel(tr("Set worker thread count:"));
|
||||
formLayout->addRow(threadLimitLabel, threadLimitLayout);
|
||||
layout->addLayout(formLayout);
|
||||
layout->addStretch(1);
|
||||
|
||||
const auto toggleEnabled = [=](const bool checked) {
|
||||
chooserLabel->setEnabled(checked);
|
||||
m_clangdChooser.setEnabled(checked);
|
||||
indexingLabel->setEnabled(checked);
|
||||
m_indexingCheckBox.setEnabled(checked);
|
||||
m_threadLimitSpinBox.setEnabled(checked);
|
||||
};
|
||||
connect(&m_useClangdCheckBox, &QCheckBox::toggled, toggleEnabled);
|
||||
toggleEnabled(m_useClangdCheckBox.isChecked());
|
||||
m_threadLimitSpinBox.setEnabled(m_useClangdCheckBox.isChecked());
|
||||
layout->addWidget(&m_widget);
|
||||
}
|
||||
|
||||
private:
|
||||
void apply() final
|
||||
{
|
||||
ClangdSettings::Data data;
|
||||
data.useClangd = m_useClangdCheckBox.isChecked();
|
||||
data.executableFilePath = m_clangdChooser.filePath();
|
||||
data.enableIndexing = m_indexingCheckBox.isChecked();
|
||||
data.workerThreadLimit = m_threadLimitSpinBox.value();
|
||||
ClangdSettings::setData(data);
|
||||
}
|
||||
void apply() final { ClangdSettings::instance().setData(m_widget.settingsData()); }
|
||||
|
||||
QCheckBox m_useClangdCheckBox;
|
||||
QCheckBox m_indexingCheckBox;
|
||||
QSpinBox m_threadLimitSpinBox;
|
||||
Utils::PathChooser m_clangdChooser;
|
||||
ClangdSettingsWidget m_widget;
|
||||
};
|
||||
|
||||
ClangdSettingsPage::ClangdSettingsPage()
|
||||
@@ -263,7 +291,47 @@ ClangdSettingsPage::ClangdSettingsPage()
|
||||
setId("K.Clangd");
|
||||
setDisplayName(ClangdSettingsWidget::tr("Clangd"));
|
||||
setCategory(Constants::CPP_SETTINGS_CATEGORY);
|
||||
setWidgetCreator([] { return new ClangdSettingsWidget; });
|
||||
setWidgetCreator([] { return new ClangdSettingsPageWidget; });
|
||||
}
|
||||
|
||||
|
||||
class ClangdProjectSettingsWidget::Private
|
||||
{
|
||||
public:
|
||||
Private(const ClangdProjectSettings &s) : settings(s), widget(s.settings()) {}
|
||||
|
||||
ClangdProjectSettings settings;
|
||||
ClangdSettingsWidget widget;
|
||||
QCheckBox useGlobalSettingsCheckBox;
|
||||
};
|
||||
|
||||
ClangdProjectSettingsWidget::ClangdProjectSettingsWidget(const ClangdProjectSettings &settings)
|
||||
: d(new Private(settings))
|
||||
{
|
||||
const auto layout = new QVBoxLayout(this);
|
||||
d->useGlobalSettingsCheckBox.setText(tr("Use global settings"));
|
||||
layout->addWidget(&d->useGlobalSettingsCheckBox);
|
||||
const auto separator = new QFrame;
|
||||
separator->setFrameShape(QFrame::HLine);
|
||||
layout->addWidget(separator);
|
||||
layout->addWidget(&d->widget);
|
||||
|
||||
d->useGlobalSettingsCheckBox.setChecked(d->settings.useGlobalSettings());
|
||||
d->widget.setEnabled(!d->settings.useGlobalSettings());
|
||||
connect(&d->useGlobalSettingsCheckBox, &QCheckBox::toggled, [this](bool checked) {
|
||||
d->widget.setEnabled(!checked);
|
||||
d->settings.setUseGlobalSettings(checked);
|
||||
if (!checked)
|
||||
d->settings.setSettings(d->widget.settingsData());
|
||||
});
|
||||
connect(&d->widget, &ClangdSettingsWidget::settingsDataChanged, [this] {
|
||||
d->settings.setSettings(d->widget.settingsData());
|
||||
});
|
||||
}
|
||||
|
||||
ClangdProjectSettingsWidget::~ClangdProjectSettingsWidget()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
} // Internal
|
||||
|
||||
@@ -44,5 +44,36 @@ public:
|
||||
explicit ClangdSettingsPage();
|
||||
};
|
||||
|
||||
class ClangdSettingsWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ClangdSettingsWidget(const ClangdSettings &settings);
|
||||
~ClangdSettingsWidget();
|
||||
|
||||
ClangdSettings::Data settingsData() const;
|
||||
|
||||
signals:
|
||||
void settingsDataChanged();
|
||||
|
||||
private:
|
||||
class Private;
|
||||
Private * const d;
|
||||
};
|
||||
|
||||
class ClangdProjectSettingsWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ClangdProjectSettingsWidget(const ClangdProjectSettings &settings);
|
||||
~ClangdProjectSettingsWidget();
|
||||
|
||||
private:
|
||||
class Private;
|
||||
Private * const d;
|
||||
};
|
||||
|
||||
} // Internal namespace
|
||||
} // CppTools namespace
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
#include <cppeditor/cppeditorconstants.h>
|
||||
#include <extensionsystem/pluginmanager.h>
|
||||
#include <projectexplorer/project.h>
|
||||
#include <projectexplorer/projectpanelfactory.h>
|
||||
#include <projectexplorer/projecttree.h>
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
@@ -210,6 +211,14 @@ bool CppToolsPlugin::initialize(const QStringList &arguments, QString *error)
|
||||
tr("Insert \"#pragma once\" instead of \"#ifndef\" include guards into header file"),
|
||||
[] { return usePragmaOnce() ? QString("true") : QString(); });
|
||||
|
||||
const auto panelFactory = new ProjectExplorer::ProjectPanelFactory;
|
||||
panelFactory->setPriority(100);
|
||||
panelFactory->setDisplayName(tr("Clangd"));
|
||||
panelFactory->setCreateWidgetFunction([](ProjectExplorer::Project *project) {
|
||||
return new ClangdProjectSettingsWidget(project);
|
||||
});
|
||||
ProjectExplorer::ProjectPanelFactory::registerFactory(panelFactory);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user