forked from qt-creator/qt-creator
CppEditor: Add settings for clangd session mode
Complete with (hidden) UI. Doesn't do anything yet, because some assumptions about projects need to be adapted on the LanguageClient side first. Task-number: QTCREATORBUG-26526 Change-Id: I34c92555e34c3d3ed98462261d47b35dfc015ce0 Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
@@ -31,6 +31,7 @@
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
#include <projectexplorer/project.h>
|
||||
#include <projectexplorer/session.h>
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/qtcassert.h>
|
||||
@@ -77,6 +78,7 @@ static QString clangdHeaderInsertionKey() { return QLatin1String("ClangdHeaderIn
|
||||
static QString clangdThreadLimitKey() { return QLatin1String("ClangdThreadLimit"); }
|
||||
static QString clangdDocumentThresholdKey() { return QLatin1String("ClangdDocumentThreshold"); }
|
||||
static QString clangdUseGlobalSettingsKey() { return QLatin1String("useGlobalSettings"); }
|
||||
static QString sessionsWithOneClangdKey() { return QLatin1String("SessionsWithOneClangd"); }
|
||||
|
||||
static FilePath g_defaultClangdFilePath;
|
||||
static FilePath fallbackClangdFilePath()
|
||||
@@ -316,6 +318,20 @@ ClangdSettings &ClangdSettings::instance()
|
||||
return settings;
|
||||
}
|
||||
|
||||
ClangdSettings::ClangdSettings()
|
||||
{
|
||||
loadSettings();
|
||||
const auto sessionMgr = ProjectExplorer::SessionManager::instance();
|
||||
connect(sessionMgr, &ProjectExplorer::SessionManager::sessionRemoved,
|
||||
this, [this](const QString &name) { m_data.sessionsWithOneClangd.removeOne(name); });
|
||||
connect(sessionMgr, &ProjectExplorer::SessionManager::sessionRenamed,
|
||||
this, [this](const QString &oldName, const QString &newName) {
|
||||
const auto index = m_data.sessionsWithOneClangd.indexOf(oldName);
|
||||
if (index != -1)
|
||||
m_data.sessionsWithOneClangd[index] = newName;
|
||||
});
|
||||
}
|
||||
|
||||
bool ClangdSettings::useClangd() const
|
||||
{
|
||||
return m_data.useClangd && clangdVersion(clangdFilePath()) >= QVersionNumber(13);
|
||||
@@ -333,6 +349,13 @@ FilePath ClangdSettings::clangdFilePath() const
|
||||
return fallbackClangdFilePath();
|
||||
}
|
||||
|
||||
ClangdSettings::Granularity ClangdSettings::granularity() const
|
||||
{
|
||||
if (m_data.sessionsWithOneClangd.contains(ProjectExplorer::SessionManager::activeSession()))
|
||||
return Granularity::Session;
|
||||
return Granularity::Project;
|
||||
}
|
||||
|
||||
void ClangdSettings::setData(const Data &data)
|
||||
{
|
||||
if (this == &instance() && data != m_data) {
|
||||
@@ -402,7 +425,12 @@ ClangdSettings::Data ClangdProjectSettings::settings() const
|
||||
{
|
||||
if (m_useGlobalSettings)
|
||||
return ClangdSettings::instance().data();
|
||||
return m_customSettings;
|
||||
ClangdSettings::Data data = m_customSettings;
|
||||
|
||||
// This property is global by definition.
|
||||
data.sessionsWithOneClangd = ClangdSettings::instance().data().sessionsWithOneClangd;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void ClangdProjectSettings::setSettings(const ClangdSettings::Data &data)
|
||||
@@ -450,6 +478,7 @@ QVariantMap ClangdSettings::Data::toMap() const
|
||||
map.insert(clangdHeaderInsertionKey(), autoIncludeHeaders);
|
||||
map.insert(clangdThreadLimitKey(), workerThreadLimit);
|
||||
map.insert(clangdDocumentThresholdKey(), documentUpdateThreshold);
|
||||
map.insert(sessionsWithOneClangdKey(), sessionsWithOneClangd);
|
||||
return map;
|
||||
}
|
||||
|
||||
@@ -461,6 +490,7 @@ void ClangdSettings::Data::fromMap(const QVariantMap &map)
|
||||
autoIncludeHeaders = map.value(clangdHeaderInsertionKey(), false).toBool();
|
||||
workerThreadLimit = map.value(clangdThreadLimitKey(), 0).toInt();
|
||||
documentUpdateThreshold = map.value(clangdDocumentThresholdKey(), 500).toInt();
|
||||
sessionsWithOneClangd = map.value(sessionsWithOneClangdKey()).toStringList();
|
||||
}
|
||||
|
||||
} // namespace CppEditor
|
||||
|
@@ -108,6 +108,7 @@ public:
|
||||
void fromMap(const QVariantMap &map);
|
||||
|
||||
Utils::FilePath executableFilePath;
|
||||
QStringList sessionsWithOneClangd;
|
||||
int workerThreadLimit = 0;
|
||||
bool useClangd = false;
|
||||
bool enableIndexing = true;
|
||||
@@ -127,6 +128,9 @@ public:
|
||||
int workerThreadLimit() const { return m_data.workerThreadLimit; }
|
||||
int documentUpdateThreshold() const { return m_data.documentUpdateThreshold; }
|
||||
|
||||
enum class Granularity { Project, Session };
|
||||
Granularity granularity() const;
|
||||
|
||||
void setData(const Data &data);
|
||||
Data data() const { return m_data; }
|
||||
|
||||
@@ -141,7 +145,7 @@ signals:
|
||||
void changed();
|
||||
|
||||
private:
|
||||
ClangdSettings() { loadSettings(); }
|
||||
ClangdSettings();
|
||||
|
||||
void loadSettings();
|
||||
void saveSettings();
|
||||
@@ -153,6 +157,7 @@ inline bool operator==(const ClangdSettings::Data &s1, const ClangdSettings::Dat
|
||||
{
|
||||
return s1.useClangd == s2.useClangd
|
||||
&& s1.executableFilePath == s2.executableFilePath
|
||||
&& s1.sessionsWithOneClangd == s2.sessionsWithOneClangd
|
||||
&& s1.workerThreadLimit == s2.workerThreadLimit
|
||||
&& s1.enableIndexing == s2.enableIndexing
|
||||
&& s1.autoIncludeHeaders == s2.autoIncludeHeaders
|
||||
|
@@ -32,13 +32,22 @@
|
||||
#include "cpptoolsreuse.h"
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
#include <projectexplorer/session.h>
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/infolabel.h>
|
||||
#include <utils/itemviews.h>
|
||||
#include <utils/pathchooser.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QFormLayout>
|
||||
#include <QGroupBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QInputDialog>
|
||||
#include <QPushButton>
|
||||
#include <QSpinBox>
|
||||
#include <QStringListModel>
|
||||
#include <QTextStream>
|
||||
#include <QVBoxLayout>
|
||||
#include <QVersionNumber>
|
||||
|
||||
namespace CppEditor::Internal {
|
||||
@@ -201,9 +210,12 @@ public:
|
||||
QSpinBox documentUpdateThreshold;
|
||||
Utils::PathChooser clangdChooser;
|
||||
Utils::InfoLabel versionWarningLabel;
|
||||
QGroupBox *sessionsGroupBox = nullptr;
|
||||
QStringListModel sessionsModel;
|
||||
};
|
||||
|
||||
ClangdSettingsWidget::ClangdSettingsWidget(const ClangdSettings::Data &settingsData)
|
||||
ClangdSettingsWidget::ClangdSettingsWidget(const ClangdSettings::Data &settingsData,
|
||||
bool isForProject)
|
||||
: d(new Private)
|
||||
{
|
||||
const ClangdSettings settings(settingsData);
|
||||
@@ -253,6 +265,64 @@ ClangdSettingsWidget::ClangdSettingsWidget(const ClangdSettings::Data &settingsD
|
||||
const auto documentUpdateThresholdLabel = new QLabel(tr("Document update threshold:"));
|
||||
formLayout->addRow(documentUpdateThresholdLabel, documentUpdateThresholdLayout);
|
||||
layout->addLayout(formLayout);
|
||||
|
||||
if (!isForProject) {
|
||||
d->sessionsModel.setStringList(settingsData.sessionsWithOneClangd);
|
||||
d->sessionsModel.sort(0);
|
||||
d->sessionsGroupBox = new QGroupBox(tr("Sessions with a single clangd instance"));
|
||||
const auto sessionsView = new Utils::ListView;
|
||||
sessionsView->setModel(&d->sessionsModel);
|
||||
sessionsView->setToolTip(
|
||||
tr("By default, Qt Creator runs one clangd process per project.\n"
|
||||
"If you have sessions with tightly coupled projects that should be\n"
|
||||
"managed by the same clangd process, add them here."));
|
||||
const auto outerSessionsLayout = new QHBoxLayout;
|
||||
const auto innerSessionsLayout = new QHBoxLayout(d->sessionsGroupBox);
|
||||
const auto buttonsLayout = new QVBoxLayout;
|
||||
const auto addButton = new QPushButton(tr("Add ..."));
|
||||
const auto removeButton = new QPushButton(tr("Remove"));
|
||||
buttonsLayout->addWidget(addButton);
|
||||
buttonsLayout->addWidget(removeButton);
|
||||
buttonsLayout->addStretch(1);
|
||||
innerSessionsLayout->addWidget(sessionsView);
|
||||
innerSessionsLayout->addLayout(buttonsLayout);
|
||||
outerSessionsLayout->addWidget(d->sessionsGroupBox);
|
||||
outerSessionsLayout->addStretch(1);
|
||||
layout->addLayout(outerSessionsLayout);
|
||||
|
||||
const auto updateRemoveButtonState = [removeButton, sessionsView] {
|
||||
removeButton->setEnabled(sessionsView->selectionModel()->hasSelection());
|
||||
};
|
||||
connect(sessionsView->selectionModel(), &QItemSelectionModel::selectionChanged,
|
||||
this, updateRemoveButtonState);
|
||||
updateRemoveButtonState();
|
||||
connect(removeButton, &QPushButton::clicked, this, [this, sessionsView] {
|
||||
const QItemSelection selection = sessionsView->selectionModel()->selection();
|
||||
QTC_ASSERT(!selection.isEmpty(), return);
|
||||
d->sessionsModel.removeRow(selection.indexes().first().row());
|
||||
});
|
||||
|
||||
connect(addButton, &QPushButton::clicked, this, [this, sessionsView] {
|
||||
QInputDialog dlg(sessionsView);
|
||||
QStringList sessions = ProjectExplorer::SessionManager::sessions();
|
||||
QStringList currentSessions = d->sessionsModel.stringList();
|
||||
for (const QString &s : qAsConst(currentSessions))
|
||||
sessions.removeOne(s);
|
||||
if (sessions.isEmpty())
|
||||
return;
|
||||
sessions.sort();
|
||||
dlg.setLabelText(tr("Choose a session:"));
|
||||
dlg.setComboBoxItems(sessions);
|
||||
if (dlg.exec() == QDialog::Accepted) {
|
||||
currentSessions << dlg.textValue();
|
||||
d->sessionsModel.setStringList(currentSessions);
|
||||
d->sessionsModel.sort(0);
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: Remove once the concept is functional.
|
||||
d->sessionsGroupBox->hide();
|
||||
}
|
||||
layout->addStretch(1);
|
||||
|
||||
static const auto setWidgetsEnabled = [](QLayout *layout, bool enabled, const auto &f) -> void {
|
||||
@@ -263,8 +333,10 @@ ClangdSettingsWidget::ClangdSettingsWidget(const ClangdSettings::Data &settingsD
|
||||
f(l, enabled, f);
|
||||
}
|
||||
};
|
||||
const auto toggleEnabled = [formLayout](const bool checked) {
|
||||
const auto toggleEnabled = [this, formLayout](const bool checked) {
|
||||
setWidgetsEnabled(formLayout, checked, setWidgetsEnabled);
|
||||
if (d->sessionsGroupBox)
|
||||
d->sessionsGroupBox->setEnabled(checked);
|
||||
};
|
||||
connect(&d->useClangdCheckBox, &QCheckBox::toggled, toggleEnabled);
|
||||
toggleEnabled(d->useClangdCheckBox.isChecked());
|
||||
@@ -326,6 +398,7 @@ ClangdSettings::Data ClangdSettingsWidget::settingsData() const
|
||||
data.autoIncludeHeaders = d->autoIncludeHeadersCheckBox.isChecked();
|
||||
data.workerThreadLimit = d->threadLimitSpinBox.value();
|
||||
data.documentUpdateThreshold = d->documentUpdateThreshold.value();
|
||||
data.sessionsWithOneClangd = d->sessionsModel.stringList();
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -334,7 +407,7 @@ class ClangdSettingsPageWidget final : public Core::IOptionsPageWidget
|
||||
Q_DECLARE_TR_FUNCTIONS(CppEditor::Internal::ClangdSettingsWidget)
|
||||
|
||||
public:
|
||||
ClangdSettingsPageWidget() : m_widget(ClangdSettings::instance().data())
|
||||
ClangdSettingsPageWidget() : m_widget(ClangdSettings::instance().data(), false)
|
||||
{
|
||||
const auto layout = new QVBoxLayout(this);
|
||||
layout->addWidget(&m_widget);
|
||||
@@ -348,7 +421,7 @@ private:
|
||||
|
||||
ClangdSettingsPage::ClangdSettingsPage()
|
||||
{
|
||||
setId("K.Clangd");
|
||||
setId(Constants::CPP_CLANGD_SETTINGS_ID);
|
||||
setDisplayName(ClangdSettingsWidget::tr("Clangd"));
|
||||
setCategory(Constants::CPP_SETTINGS_CATEGORY);
|
||||
setWidgetCreator([] { return new ClangdSettingsPageWidget; });
|
||||
@@ -358,7 +431,7 @@ ClangdSettingsPage::ClangdSettingsPage()
|
||||
class ClangdProjectSettingsWidget::Private
|
||||
{
|
||||
public:
|
||||
Private(const ClangdProjectSettings &s) : settings(s), widget(s.settings()) {}
|
||||
Private(const ClangdProjectSettings &s) : settings(s), widget(s.settings(), true) {}
|
||||
|
||||
ClangdProjectSettings settings;
|
||||
ClangdSettingsWidget widget;
|
||||
@@ -369,16 +442,35 @@ ClangdProjectSettingsWidget::ClangdProjectSettingsWidget(const ClangdProjectSett
|
||||
: d(new Private(settings))
|
||||
{
|
||||
const auto layout = new QVBoxLayout(this);
|
||||
d->useGlobalSettingsCheckBox.setText(tr("Use global settings"));
|
||||
layout->addWidget(&d->useGlobalSettingsCheckBox);
|
||||
const auto globalSettingsLayout = new QHBoxLayout;
|
||||
globalSettingsLayout->addWidget(&d->useGlobalSettingsCheckBox);
|
||||
const auto globalSettingsLabel = new QLabel("Use <a href=\"dummy\">global settings</a>");
|
||||
connect(globalSettingsLabel, &QLabel::linkActivated,
|
||||
this, [] { Core::ICore::showOptionsDialog(Constants::CPP_CLANGD_SETTINGS_ID); });
|
||||
globalSettingsLayout->addWidget(globalSettingsLabel);
|
||||
globalSettingsLayout->addStretch(1);
|
||||
layout->addLayout(globalSettingsLayout);
|
||||
|
||||
const auto separator = new QFrame;
|
||||
separator->setFrameShape(QFrame::HLine);
|
||||
layout->addWidget(separator);
|
||||
layout->addWidget(&d->widget);
|
||||
|
||||
const auto updateGlobalSettingsCheckBox = [this] {
|
||||
if (ClangdSettings::instance().granularity() == ClangdSettings::Granularity::Session) {
|
||||
d->useGlobalSettingsCheckBox.setEnabled(false);
|
||||
d->useGlobalSettingsCheckBox.setChecked(true);
|
||||
} else {
|
||||
d->useGlobalSettingsCheckBox.setEnabled(true);
|
||||
d->useGlobalSettingsCheckBox.setChecked(d->settings.useGlobalSettings());
|
||||
d->widget.setEnabled(!d->settings.useGlobalSettings());
|
||||
connect(&d->useGlobalSettingsCheckBox, &QCheckBox::toggled, [this](bool checked) {
|
||||
}
|
||||
d->widget.setEnabled(!d->useGlobalSettingsCheckBox.isChecked());
|
||||
};
|
||||
updateGlobalSettingsCheckBox();
|
||||
connect(&ClangdSettings::instance(), &ClangdSettings::changed,
|
||||
this, updateGlobalSettingsCheckBox);
|
||||
|
||||
connect(&d->useGlobalSettingsCheckBox, &QCheckBox::clicked, [this](bool checked) {
|
||||
d->widget.setEnabled(!checked);
|
||||
d->settings.setUseGlobalSettings(checked);
|
||||
if (!checked)
|
||||
|
@@ -48,7 +48,7 @@ class ClangdSettingsWidget : public QWidget
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ClangdSettingsWidget(const ClangdSettings::Data &settingsData);
|
||||
ClangdSettingsWidget(const ClangdSettings::Data &settingsData, bool isForProject);
|
||||
~ClangdSettingsWidget();
|
||||
|
||||
ClangdSettings::Data settingsData() const;
|
||||
|
@@ -119,6 +119,7 @@ const char CPP_FILE_SETTINGS_NAME[] = QT_TRANSLATE_NOOP("CppEditor", "File Namin
|
||||
const char CPP_CODE_MODEL_SETTINGS_ID[] = "C.Cpp.Code Model";
|
||||
const char CPP_DIAGNOSTIC_CONFIG_SETTINGS_ID[] = "C.Cpp.Diagnostic Config";
|
||||
const char CPP_DIAGNOSTIC_CONFIG_SETTINGS_NAME[] = QT_TRANSLATE_NOOP("CppEditor", "Diagnostic Configurations");
|
||||
const char CPP_CLANGD_SETTINGS_ID[] = "K.Cpp.Clangd";
|
||||
const char CPP_SETTINGS_CATEGORY[] = "I.C++";
|
||||
|
||||
const char CPP_CLANG_FIXIT_AVAILABLE_MARKER_ID[] = "ClangFixItAvailableMarker";
|
||||
|
@@ -831,6 +831,7 @@ bool SessionManager::renameSession(const QString &original, const QString &newNa
|
||||
return false;
|
||||
if (original == activeSession())
|
||||
loadSession(newName);
|
||||
emit instance()->sessionRenamed(original, newName);
|
||||
return deleteSession(original);
|
||||
}
|
||||
|
||||
@@ -858,6 +859,7 @@ bool SessionManager::deleteSession(const QString &session)
|
||||
if (!d->m_sessions.contains(session))
|
||||
return false;
|
||||
d->m_sessions.removeOne(session);
|
||||
emit instance()->sessionRemoved(session);
|
||||
QFile fi(sessionNameToFileName(session).toString());
|
||||
if (fi.exists())
|
||||
return fi.remove();
|
||||
|
@@ -141,7 +141,10 @@ signals:
|
||||
void aboutToSaveSession();
|
||||
void dependencyChanged(ProjectExplorer::Project *a, ProjectExplorer::Project *b);
|
||||
|
||||
signals: // for tests only
|
||||
void sessionRenamed(const QString &oldName, const QString &newName);
|
||||
void sessionRemoved(const QString &name);
|
||||
|
||||
// for tests only
|
||||
void projectFinishedParsing(ProjectExplorer::Project *project);
|
||||
|
||||
private:
|
||||
|
Reference in New Issue
Block a user