diff --git a/src/plugins/cppeditor/cppcodemodelsettings.cpp b/src/plugins/cppeditor/cppcodemodelsettings.cpp index e60d8464687..6e53c0bbc13 100644 --- a/src/plugins/cppeditor/cppcodemodelsettings.cpp +++ b/src/plugins/cppeditor/cppcodemodelsettings.cpp @@ -31,6 +31,7 @@ #include #include +#include #include #include @@ -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 diff --git a/src/plugins/cppeditor/cppcodemodelsettings.h b/src/plugins/cppeditor/cppcodemodelsettings.h index 7d3ecb27fae..afebead5525 100644 --- a/src/plugins/cppeditor/cppcodemodelsettings.h +++ b/src/plugins/cppeditor/cppcodemodelsettings.h @@ -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 diff --git a/src/plugins/cppeditor/cppcodemodelsettingspage.cpp b/src/plugins/cppeditor/cppcodemodelsettingspage.cpp index 022d4cf6fbf..b147d74d222 100644 --- a/src/plugins/cppeditor/cppcodemodelsettingspage.cpp +++ b/src/plugins/cppeditor/cppcodemodelsettingspage.cpp @@ -32,13 +32,22 @@ #include "cpptoolsreuse.h" #include +#include #include #include +#include #include +#include #include +#include +#include +#include +#include #include +#include #include +#include #include 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 global settings"); + 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); - d->useGlobalSettingsCheckBox.setChecked(d->settings.useGlobalSettings()); - d->widget.setEnabled(!d->settings.useGlobalSettings()); - connect(&d->useGlobalSettingsCheckBox, &QCheckBox::toggled, [this](bool checked) { + 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->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) diff --git a/src/plugins/cppeditor/cppcodemodelsettingspage.h b/src/plugins/cppeditor/cppcodemodelsettingspage.h index 44d235c5c89..6abe21cf2c3 100644 --- a/src/plugins/cppeditor/cppcodemodelsettingspage.h +++ b/src/plugins/cppeditor/cppcodemodelsettingspage.h @@ -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; diff --git a/src/plugins/cppeditor/cppeditorconstants.h b/src/plugins/cppeditor/cppeditorconstants.h index 60497dfa4d2..d657f317e31 100644 --- a/src/plugins/cppeditor/cppeditorconstants.h +++ b/src/plugins/cppeditor/cppeditorconstants.h @@ -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"; diff --git a/src/plugins/projectexplorer/session.cpp b/src/plugins/projectexplorer/session.cpp index e94debf9048..c3abf3119f4 100644 --- a/src/plugins/projectexplorer/session.cpp +++ b/src/plugins/projectexplorer/session.cpp @@ -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(); diff --git a/src/plugins/projectexplorer/session.h b/src/plugins/projectexplorer/session.h index 61b88ee412e..c55a6205567 100644 --- a/src/plugins/projectexplorer/session.h +++ b/src/plugins/projectexplorer/session.h @@ -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: