diff options
author | Christian Kandeler <christian.kandeler@qt.io> | 2024-04-10 16:22:48 +0200 |
---|---|---|
committer | Christian Kandeler <christian.kandeler@qt.io> | 2024-04-11 08:48:19 +0000 |
commit | 4b29ad78e3f8f8917767818d97e29c7b9f8c4686 (patch) | |
tree | 236136175f2ba69157715b1a67e631274fcdcf70 /src/plugins/cppeditor | |
parent | 459cb8274407ea6d5d52a0ad27e969a542d3b664 (diff) |
CppEditor: Move ClangdSettingsPage alongside ClangdSettings
Change-Id: Ib22438817ff3a14904decbb23f3c2288f355e378
Reviewed-by: David Schulz <david.schulz@qt.io>
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Diffstat (limited to 'src/plugins/cppeditor')
-rw-r--r-- | src/plugins/cppeditor/clangdsettings.cpp | 540 | ||||
-rw-r--r-- | src/plugins/cppeditor/clangdsettings.h | 5 | ||||
-rw-r--r-- | src/plugins/cppeditor/cppcodemodelsettingspage.cpp | 527 | ||||
-rw-r--r-- | src/plugins/cppeditor/cppcodemodelsettingspage.h | 2 | ||||
-rw-r--r-- | src/plugins/cppeditor/cppeditorplugin.cpp | 1 |
5 files changed, 546 insertions, 529 deletions
diff --git a/src/plugins/cppeditor/clangdsettings.cpp b/src/plugins/cppeditor/clangdsettings.cpp index b5352af6d8..253914704a 100644 --- a/src/plugins/cppeditor/clangdsettings.cpp +++ b/src/plugins/cppeditor/clangdsettings.cpp @@ -4,19 +4,41 @@ #include "clangdsettings.h" #include "clangdiagnosticconfigsmodel.h" +#include "clangdiagnosticconfigsselectionwidget.h" +#include "clangdiagnosticconfigswidget.h" #include "cppeditorconstants.h" #include "cppeditortr.h" #include "cpptoolsreuse.h" +#include <coreplugin/dialogs/ioptionspage.h> #include <coreplugin/icore.h> #include <coreplugin/session.h> #include <projectexplorer/project.h> +#include <projectexplorer/projectpanelfactory.h> +#include <projectexplorer/projectsettingswidget.h> #include <utils/clangutils.h> +#include <utils/itemviews.h> +#include <utils/layoutbuilder.h> #include <utils/qtcprocess.h> +#include <utils/variablechooser.h> +#include <QCheckBox> +#include <QDesktopServices> +#include <QFormLayout> +#include <QGroupBox> +#include <QGuiApplication> +#include <QInputDialog> +#include <QPushButton> +#include <QSpinBox> #include <QStandardPaths> +#include <QStringListModel> +#include <QTimer> +#include <QVBoxLayout> #include <QVersionNumber> +#include <limits> + +using namespace ProjectExplorer; using namespace Utils; namespace CppEditor { @@ -338,7 +360,7 @@ void ClangdSettings::setClangdFilePath(const FilePath &filePath) } #endif -ClangdProjectSettings::ClangdProjectSettings(ProjectExplorer::Project *project) : m_project(project) +ClangdProjectSettings::ClangdProjectSettings(Project *project) : m_project(project) { loadSettings(); } @@ -488,4 +510,520 @@ int ClangdSettings::Data::defaultCompletionResults() return ok ? userValue : 100; } +namespace Internal { +class ClangdSettingsWidget final : public QWidget +{ + Q_OBJECT + +public: + ClangdSettingsWidget(const ClangdSettings::Data &settingsData, bool isForProject); + + ClangdSettings::Data settingsData() const; + +signals: + void settingsDataChanged(); + +private: + QCheckBox m_useClangdCheckBox; + QComboBox m_indexingComboBox; + Utils::FancyLineEdit m_projectIndexPathTemplateLineEdit; + Utils::FancyLineEdit m_sessionIndexPathTemplateLineEdit; + QComboBox m_headerSourceSwitchComboBox; + QComboBox m_completionRankingModelComboBox; + QCheckBox m_autoIncludeHeadersCheckBox; + QCheckBox m_sizeThresholdCheckBox; + QSpinBox m_threadLimitSpinBox; + QSpinBox m_documentUpdateThreshold; + QSpinBox m_sizeThresholdSpinBox; + QSpinBox m_completionResults; + Utils::PathChooser m_clangdChooser; + Utils::InfoLabel m_versionWarningLabel; + ClangDiagnosticConfigsSelectionWidget *m_configSelectionWidget = nullptr; + QGroupBox *m_sessionsGroupBox = nullptr; + QStringListModel m_sessionsModel; +}; + +ClangdSettingsWidget::ClangdSettingsWidget(const ClangdSettings::Data &settingsData, + bool isForProject) +{ + const ClangdSettings settings(settingsData); + const QString indexingToolTip = Tr::tr( + "<p>If background indexing is enabled, global symbol searches will yield more accurate " + "results, at the cost of additional CPU load when the project is first opened. The " + "indexing result is persisted in the project's build directory. If you disable background " + "indexing, a faster, but less accurate, built-in indexer is used instead. The thread " + "priority for building the background index can be adjusted since clangd 15.</p>" + "<p>Background Priority: Minimum priority, runs on idle CPUs. May leave 'performance' " + "cores unused.</p>" + "<p>Normal Priority: Reduced priority compared to interactive work.</p>" + "<p>Low Priority: Same priority as other clangd work.</p>"); + const QString projectIndexPathToolTip = Tr::tr( + "The location of the per-project clangd index.<p>" + "This is also where the compile_commands.json file will go."); + const QString sessionIndexPathToolTip = Tr::tr( + "The location of the per-session clangd index.<p>" + "This is also where the compile_commands.json file will go."); + const QString headerSourceSwitchToolTip = Tr::tr( + "<p>The C/C++ backend to use for switching between header and source files.</p>" + "<p>While the clangd implementation has more capabilities than the built-in " + "code model, it tends to find false positives.</p>" + "<p>When \"Try Both\" is selected, clangd is used only if the built-in variant " + "does not find anything.</p>"); + using RankingModel = ClangdSettings::CompletionRankingModel; + const QString completionRankingModelToolTip = Tr::tr( + "<p>Which model clangd should use to rank possible completions.</p>" + "<p>This determines the order of candidates in the combo box when doing code completion.</p>" + "<p>The \"%1\" model used by default results from (pre-trained) machine learning and " + "provides superior results on average.</p>" + "<p>If you feel that its suggestions stray too much from your expectations for your " + "code base, you can try switching to the hand-crafted \"%2\" model.</p>").arg( + ClangdSettings::rankingModelToDisplayString(RankingModel::DecisionForest), + ClangdSettings::rankingModelToDisplayString(RankingModel::Heuristics)); + const QString workerThreadsToolTip = Tr::tr( + "Number of worker threads used by clangd. Background indexing also uses this many " + "worker threads."); + const QString autoIncludeToolTip = Tr::tr( + "Controls whether clangd may insert header files as part of symbol completion."); + const QString documentUpdateToolTip + //: %1 is the application name (Qt Creator) + = Tr::tr("Defines the amount of time %1 waits before sending document changes to the " + "server.\n" + "If the document changes again while waiting, this timeout resets.") + .arg(QGuiApplication::applicationDisplayName()); + const QString sizeThresholdToolTip = Tr::tr( + "Files greater than this will not be opened as documents in clangd.\n" + "The built-in code model will handle highlighting, completion and so on."); + const QString completionResultToolTip = Tr::tr( + "The maximum number of completion results returned by clangd."); + + m_useClangdCheckBox.setText(Tr::tr("Use clangd")); + m_useClangdCheckBox.setChecked(settings.useClangd()); + m_clangdChooser.setExpectedKind(Utils::PathChooser::ExistingCommand); + m_clangdChooser.setFilePath(settings.clangdFilePath()); + m_clangdChooser.setAllowPathFromDevice(true); + m_clangdChooser.setEnabled(m_useClangdCheckBox.isChecked()); + m_clangdChooser.setCommandVersionArguments({"--version"}); + using Priority = ClangdSettings::IndexingPriority; + for (Priority prio : {Priority::Off, Priority::Background, Priority::Low, Priority::Normal}) { + m_indexingComboBox.addItem(ClangdSettings::priorityToDisplayString(prio), int(prio)); + if (prio == settings.indexingPriority()) + m_indexingComboBox.setCurrentIndex(m_indexingComboBox.count() - 1); + } + m_indexingComboBox.setToolTip(indexingToolTip); + m_projectIndexPathTemplateLineEdit.setText(settings.data().projectIndexPathTemplate); + m_sessionIndexPathTemplateLineEdit.setText(settings.data().sessionIndexPathTemplate); + using SwitchMode = ClangdSettings::HeaderSourceSwitchMode; + for (SwitchMode mode : {SwitchMode::BuiltinOnly, SwitchMode::ClangdOnly, SwitchMode::Both}) { + m_headerSourceSwitchComboBox.addItem( + ClangdSettings::headerSourceSwitchModeToDisplayString(mode), int(mode)); + if (mode == settings.headerSourceSwitchMode()) + m_headerSourceSwitchComboBox.setCurrentIndex( + m_headerSourceSwitchComboBox.count() - 1); + } + m_headerSourceSwitchComboBox.setToolTip(headerSourceSwitchToolTip); + for (RankingModel model : {RankingModel::Default, RankingModel::DecisionForest, + RankingModel::Heuristics}) { + m_completionRankingModelComboBox.addItem( + ClangdSettings::rankingModelToDisplayString(model), int(model)); + if (model == settings.completionRankingModel()) + m_completionRankingModelComboBox.setCurrentIndex( + m_completionRankingModelComboBox.count() - 1); + } + m_completionRankingModelComboBox.setToolTip(completionRankingModelToolTip); + + m_autoIncludeHeadersCheckBox.setText(Tr::tr("Insert header files on completion")); + m_autoIncludeHeadersCheckBox.setChecked(settings.autoIncludeHeaders()); + m_autoIncludeHeadersCheckBox.setToolTip(autoIncludeToolTip); + m_threadLimitSpinBox.setValue(settings.workerThreadLimit()); + m_threadLimitSpinBox.setSpecialValueText(Tr::tr("Automatic")); + m_threadLimitSpinBox.setToolTip(workerThreadsToolTip); + m_documentUpdateThreshold.setMinimum(50); + m_documentUpdateThreshold.setMaximum(10000); + m_documentUpdateThreshold.setValue(settings.documentUpdateThreshold()); + m_documentUpdateThreshold.setSingleStep(100); + m_documentUpdateThreshold.setSuffix(" ms"); + m_documentUpdateThreshold.setToolTip(documentUpdateToolTip); + m_sizeThresholdCheckBox.setText(Tr::tr("Ignore files greater than")); + m_sizeThresholdCheckBox.setChecked(settings.sizeThresholdEnabled()); + m_sizeThresholdCheckBox.setToolTip(sizeThresholdToolTip); + m_sizeThresholdSpinBox.setMinimum(1); + m_sizeThresholdSpinBox.setMaximum(std::numeric_limits<int>::max()); + m_sizeThresholdSpinBox.setSuffix(" KB"); + m_sizeThresholdSpinBox.setValue(settings.sizeThresholdInKb()); + m_sizeThresholdSpinBox.setToolTip(sizeThresholdToolTip); + + const auto completionResultsLabel = new QLabel(Tr::tr("Completion results:")); + completionResultsLabel->setToolTip(completionResultToolTip); + m_completionResults.setMinimum(0); + m_completionResults.setMaximum(std::numeric_limits<int>::max()); + m_completionResults.setValue(settings.completionResults()); + m_completionResults.setToolTip(completionResultToolTip); + m_completionResults.setSpecialValueText(Tr::tr("No limit")); + + const auto layout = new QVBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + layout->addWidget(&m_useClangdCheckBox); + + const auto formLayout = new QFormLayout; + const auto chooserLabel = new QLabel(Tr::tr("Path to executable:")); + formLayout->addRow(chooserLabel, &m_clangdChooser); + formLayout->addRow(QString(), &m_versionWarningLabel); + + const auto indexingPriorityLayout = new QHBoxLayout; + indexingPriorityLayout->addWidget(&m_indexingComboBox); + indexingPriorityLayout->addStretch(1); + const auto indexingPriorityLabel = new QLabel(Tr::tr("Background indexing:")); + indexingPriorityLabel->setToolTip(indexingToolTip); + formLayout->addRow(indexingPriorityLabel, indexingPriorityLayout); + + for (const auto &[text, edit, toolTip, defaultValue] : + {std::make_tuple(Tr::tr("Per-project index location:"), + &m_projectIndexPathTemplateLineEdit, + projectIndexPathToolTip, + ClangdSettings::defaultProjectIndexPathTemplate()), + std::make_tuple(Tr::tr("Per-session index location:"), + &m_sessionIndexPathTemplateLineEdit, + sessionIndexPathToolTip, + ClangdSettings::defaultSessionIndexPathTemplate())}) { + if (isForProject && edit == &m_sessionIndexPathTemplateLineEdit) + continue; + + const auto chooser = new Utils::VariableChooser(edit); + chooser->addSupportedWidget(edit); + chooser->addMacroExpanderProvider([] { return Utils::globalMacroExpander(); }); + + const auto resetButton = new QPushButton(Tr::tr("Reset")); + connect(resetButton, &QPushButton::clicked, [e = edit, v = defaultValue] { e->setText(v); }); + const auto layout = new QHBoxLayout; + const auto label = new QLabel(text); + label->setToolTip(toolTip); + edit->setToolTip(toolTip); + layout->addWidget(edit); + layout->addWidget(resetButton); + formLayout->addRow(label, layout); + } + + const auto headerSourceSwitchLayout = new QHBoxLayout; + headerSourceSwitchLayout->addWidget(&m_headerSourceSwitchComboBox); + headerSourceSwitchLayout->addStretch(1); + const auto headerSourceSwitchLabel = new QLabel(Tr::tr("Header/source switch mode:")); + headerSourceSwitchLabel->setToolTip(headerSourceSwitchToolTip); + formLayout->addRow(headerSourceSwitchLabel, headerSourceSwitchLayout); + + const auto threadLimitLayout = new QHBoxLayout; + threadLimitLayout->addWidget(&m_threadLimitSpinBox); + threadLimitLayout->addStretch(1); + const auto threadLimitLabel = new QLabel(Tr::tr("Worker thread count:")); + threadLimitLabel->setToolTip(workerThreadsToolTip); + formLayout->addRow(threadLimitLabel, threadLimitLayout); + + formLayout->addRow(QString(), &m_autoIncludeHeadersCheckBox); + const auto limitResultsLayout = new QHBoxLayout; + limitResultsLayout->addWidget(&m_completionResults); + limitResultsLayout->addStretch(1); + formLayout->addRow(completionResultsLabel, limitResultsLayout); + + const auto completionRankingModelLayout = new QHBoxLayout; + completionRankingModelLayout->addWidget(&m_completionRankingModelComboBox); + completionRankingModelLayout->addStretch(1); + const auto completionRankingModelLabel = new QLabel(Tr::tr("Completion ranking model:")); + completionRankingModelLabel->setToolTip(completionRankingModelToolTip); + formLayout->addRow(completionRankingModelLabel, completionRankingModelLayout); + + const auto documentUpdateThresholdLayout = new QHBoxLayout; + documentUpdateThresholdLayout->addWidget(&m_documentUpdateThreshold); + documentUpdateThresholdLayout->addStretch(1); + const auto documentUpdateThresholdLabel = new QLabel(Tr::tr("Document update threshold:")); + documentUpdateThresholdLabel->setToolTip(documentUpdateToolTip); + formLayout->addRow(documentUpdateThresholdLabel, documentUpdateThresholdLayout); + const auto sizeThresholdLayout = new QHBoxLayout; + sizeThresholdLayout->addWidget(&m_sizeThresholdSpinBox); + sizeThresholdLayout->addStretch(1); + formLayout->addRow(&m_sizeThresholdCheckBox, sizeThresholdLayout); + + m_configSelectionWidget = new ClangDiagnosticConfigsSelectionWidget(formLayout); + m_configSelectionWidget->refresh( + diagnosticConfigsModel(settings.customDiagnosticConfigs()), + settings.diagnosticConfigId(), + [](const ClangDiagnosticConfigs &configs, const Utils::Id &configToSelect) { + return new CppEditor::ClangDiagnosticConfigsWidget(configs, configToSelect); + }); + + layout->addLayout(formLayout); + if (!isForProject) { + m_sessionsModel.setStringList(settingsData.sessionsWithOneClangd); + m_sessionsModel.sort(0); + m_sessionsGroupBox = new QGroupBox(Tr::tr("Sessions with a single clangd instance")); + const auto sessionsView = new Utils::ListView; + sessionsView->setModel(&m_sessionsModel); + sessionsView->setToolTip( + Tr::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(m_sessionsGroupBox); + const auto buttonsLayout = new QVBoxLayout; + const auto addButton = new QPushButton(Tr::tr("Add ...")); + const auto removeButton = new QPushButton(Tr::tr("Remove")); + buttonsLayout->addWidget(addButton); + buttonsLayout->addWidget(removeButton); + buttonsLayout->addStretch(1); + innerSessionsLayout->addWidget(sessionsView); + innerSessionsLayout->addLayout(buttonsLayout); + outerSessionsLayout->addWidget(m_sessionsGroupBox); + outerSessionsLayout->addStretch(1); + + const auto separator = new QFrame; + separator->setFrameShape(QFrame::HLine); + layout->addWidget(separator); + 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); + m_sessionsModel.removeRow(selection.indexes().first().row()); + }); + + connect(addButton, &QPushButton::clicked, this, [this, sessionsView] { + QInputDialog dlg(sessionsView); + QStringList sessions = Core::SessionManager::sessions(); + QStringList currentSessions = m_sessionsModel.stringList(); + for (const QString &s : std::as_const(currentSessions)) + sessions.removeOne(s); + if (sessions.isEmpty()) + return; + sessions.sort(); + dlg.setLabelText(Tr::tr("Choose a session:")); + dlg.setComboBoxItems(sessions); + if (dlg.exec() == QDialog::Accepted) { + currentSessions << dlg.textValue(); + m_sessionsModel.setStringList(currentSessions); + m_sessionsModel.sort(0); + } + }); + } + + const auto configFilesHelpLabel = new QLabel; + configFilesHelpLabel->setText(Tr::tr("Additional settings are available via " + "<a href=\"https://clangd.llvm.org/config\"> clangd configuration files</a>.<br>" + "User-specific settings go <a href=\"%1\">here</a>, " + "project-specific settings can be configured by putting a .clangd file into " + "the project source tree.") + .arg(ClangdSettings::clangdUserConfigFilePath().toUserOutput())); + configFilesHelpLabel->setWordWrap(true); + connect(configFilesHelpLabel, &QLabel::linkHovered, configFilesHelpLabel, &QLabel::setToolTip); + connect(configFilesHelpLabel, &QLabel::linkActivated, [](const QString &link) { + if (link.startsWith("https")) + QDesktopServices::openUrl(link); + else + Core::EditorManager::openEditor(Utils::FilePath::fromString(link)); + }); + layout->addWidget(Layouting::createHr()); + layout->addWidget(configFilesHelpLabel); + + layout->addStretch(1); + + static const auto setWidgetsEnabled = [](QLayout *layout, bool enabled, const auto &f) -> void { + for (int i = 0; i < layout->count(); ++i) { + if (QWidget * const w = layout->itemAt(i)->widget()) + w->setEnabled(enabled); + else if (QLayout * const l = layout->itemAt(i)->layout()) + f(l, enabled, f); + } + }; + const auto toggleEnabled = [this, formLayout](const bool checked) { + setWidgetsEnabled(formLayout, checked, setWidgetsEnabled); + if (m_sessionsGroupBox) + m_sessionsGroupBox->setEnabled(checked); + }; + connect(&m_useClangdCheckBox, &QCheckBox::toggled, toggleEnabled); + toggleEnabled(m_useClangdCheckBox.isChecked()); + m_threadLimitSpinBox.setEnabled(m_useClangdCheckBox.isChecked()); + + m_versionWarningLabel.setType(Utils::InfoLabel::Warning); + const auto updateWarningLabel = [this] { + class WarningLabelSetter { + public: + WarningLabelSetter(QLabel &label) : m_label(label) { m_label.clear(); } + ~WarningLabelSetter() { m_label.setVisible(!m_label.text().isEmpty()); } + void setWarning(const QString &text) { m_label.setText(text); } + private: + QLabel &m_label; + }; + WarningLabelSetter labelSetter(m_versionWarningLabel); + + if (!m_clangdChooser.isValid()) + return; + const Utils::FilePath clangdPath = m_clangdChooser.filePath(); + QString errorMessage; + if (!Utils::checkClangdVersion(clangdPath, &errorMessage)) + labelSetter.setWarning(errorMessage); + }; + connect(&m_clangdChooser, &Utils::PathChooser::textChanged, this, updateWarningLabel); + connect(&m_clangdChooser, &Utils::PathChooser::validChanged, this, updateWarningLabel); + updateWarningLabel(); + + connect(&m_useClangdCheckBox, &QCheckBox::toggled, + this, &ClangdSettingsWidget::settingsDataChanged); + connect(&m_indexingComboBox, &QComboBox::currentIndexChanged, + this, &ClangdSettingsWidget::settingsDataChanged); + connect(&m_projectIndexPathTemplateLineEdit, &QLineEdit::textChanged, + this, &ClangdSettingsWidget::settingsDataChanged); + connect(&m_sessionIndexPathTemplateLineEdit, &QLineEdit::textChanged, + this, &ClangdSettingsWidget::settingsDataChanged); + connect(&m_headerSourceSwitchComboBox, &QComboBox::currentIndexChanged, + this, &ClangdSettingsWidget::settingsDataChanged); + connect(&m_completionRankingModelComboBox, &QComboBox::currentIndexChanged, + this, &ClangdSettingsWidget::settingsDataChanged); + connect(&m_autoIncludeHeadersCheckBox, &QCheckBox::toggled, + this, &ClangdSettingsWidget::settingsDataChanged); + connect(&m_threadLimitSpinBox, &QSpinBox::valueChanged, + this, &ClangdSettingsWidget::settingsDataChanged); + connect(&m_sizeThresholdCheckBox, &QCheckBox::toggled, + this, &ClangdSettingsWidget::settingsDataChanged); + connect(&m_sizeThresholdSpinBox, &QSpinBox::valueChanged, + this, &ClangdSettingsWidget::settingsDataChanged); + connect(&m_documentUpdateThreshold, &QSpinBox::valueChanged, + this, &ClangdSettingsWidget::settingsDataChanged); + connect(&m_clangdChooser, &Utils::PathChooser::textChanged, + this, &ClangdSettingsWidget::settingsDataChanged); + connect(m_configSelectionWidget, &ClangDiagnosticConfigsSelectionWidget::changed, + this, &ClangdSettingsWidget::settingsDataChanged); + connect(&m_completionResults, &QSpinBox::valueChanged, + this, &ClangdSettingsWidget::settingsDataChanged); +} + +ClangdSettings::Data ClangdSettingsWidget::settingsData() const +{ + ClangdSettings::Data data; + data.useClangd = m_useClangdCheckBox.isChecked(); + data.executableFilePath = m_clangdChooser.filePath(); + data.indexingPriority = ClangdSettings::IndexingPriority( + m_indexingComboBox.currentData().toInt()); + data.projectIndexPathTemplate = m_projectIndexPathTemplateLineEdit.text(); + data.sessionIndexPathTemplate = m_sessionIndexPathTemplateLineEdit.text(); + data.headerSourceSwitchMode = ClangdSettings::HeaderSourceSwitchMode( + m_headerSourceSwitchComboBox.currentData().toInt()); + data.completionRankingModel = ClangdSettings::CompletionRankingModel( + m_completionRankingModelComboBox.currentData().toInt()); + data.autoIncludeHeaders = m_autoIncludeHeadersCheckBox.isChecked(); + data.workerThreadLimit = m_threadLimitSpinBox.value(); + data.documentUpdateThreshold = m_documentUpdateThreshold.value(); + data.sizeThresholdEnabled = m_sizeThresholdCheckBox.isChecked(); + data.sizeThresholdInKb = m_sizeThresholdSpinBox.value(); + data.sessionsWithOneClangd = m_sessionsModel.stringList(); + data.customDiagnosticConfigs = m_configSelectionWidget->customConfigs(); + data.diagnosticConfigId = m_configSelectionWidget->currentConfigId(); + data.completionResults = m_completionResults.value(); + return data; +} + +class ClangdSettingsPageWidget final : public Core::IOptionsPageWidget +{ +public: + ClangdSettingsPageWidget() : m_widget(ClangdSettings::instance().data(), false) + { + const auto layout = new QVBoxLayout(this); + layout->addWidget(&m_widget); + } + +private: + void apply() final { ClangdSettings::instance().setData(m_widget.settingsData()); } + + ClangdSettingsWidget m_widget; +}; + +class ClangdSettingsPage final : public Core::IOptionsPage +{ +public: + ClangdSettingsPage() + { + setId(Constants::CPP_CLANGD_SETTINGS_ID); + setDisplayName(Tr::tr("Clangd")); + setCategory(Constants::CPP_SETTINGS_CATEGORY); + setWidgetCreator([] { return new ClangdSettingsPageWidget; }); + } +}; + +void setupClangdSettingsPage() +{ + static ClangdSettingsPage theClangdSettingsPage; +} + +class ClangdProjectSettingsWidget : public ProjectSettingsWidget +{ +public: + ClangdProjectSettingsWidget(const ClangdProjectSettings &settings) + : m_settings(settings), m_widget(settings.settings(), true) + { + setGlobalSettingsId(Constants::CPP_CLANGD_SETTINGS_ID); + const auto layout = new QVBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + layout->addWidget(&m_widget); + + const auto updateGlobalSettingsCheckBox = [this] { + if (ClangdSettings::instance().granularity() == ClangdSettings::Granularity::Session) { + setUseGlobalSettingsCheckBoxEnabled(false); + setUseGlobalSettings(true); + } else { + setUseGlobalSettingsCheckBoxEnabled(true); + setUseGlobalSettings(m_settings.useGlobalSettings()); + } + m_widget.setEnabled(!useGlobalSettings()); + }; + + updateGlobalSettingsCheckBox(); + connect(&ClangdSettings::instance(), &ClangdSettings::changed, + this, updateGlobalSettingsCheckBox); + + connect(this, &ProjectSettingsWidget::useGlobalSettingsChanged, this, + [this](bool checked) { + m_widget.setEnabled(!checked); + m_settings.setUseGlobalSettings(checked); + if (!checked) + m_settings.setSettings(m_widget.settingsData()); + }); + + const auto timer = new QTimer(this); + timer->setSingleShot(true); + timer->setInterval(5000); + connect(timer, &QTimer::timeout, this, [this] { + m_settings.setSettings(m_widget.settingsData()); + }); + connect(&m_widget, &ClangdSettingsWidget::settingsDataChanged, + timer, qOverload<>(&QTimer::start)); + } + +private: + ClangdProjectSettings m_settings; + ClangdSettingsWidget m_widget; +}; + +class ClangdProjectSettingsPanelFactory final : public ProjectPanelFactory +{ +public: + ClangdProjectSettingsPanelFactory() + { + setPriority(100); + setDisplayName(Tr::tr("Clangd")); + setCreateWidgetFunction([](Project *project) { + return new ClangdProjectSettingsWidget(project); + }); + } +}; + +void setupClangdProjectSettingsPanel() +{ + static ClangdProjectSettingsPanelFactory theClangdProjectSettingsPanelFactory; +} + +} // namespace Internal } // namespace CppEditor + +#include <clangdsettings.moc> diff --git a/src/plugins/cppeditor/clangdsettings.h b/src/plugins/cppeditor/clangdsettings.h index c1fb3b6ae8..f3e66fb9f6 100644 --- a/src/plugins/cppeditor/clangdsettings.h +++ b/src/plugins/cppeditor/clangdsettings.h @@ -159,4 +159,9 @@ private: bool m_blockIndexing = false; }; +namespace Internal { +void setupClangdProjectSettingsPanel(); +void setupClangdSettingsPage(); +} + } // namespace CppEditor diff --git a/src/plugins/cppeditor/cppcodemodelsettingspage.cpp b/src/plugins/cppeditor/cppcodemodelsettingspage.cpp index 0e880fcb55..1b5872c7a3 100644 --- a/src/plugins/cppeditor/cppcodemodelsettingspage.cpp +++ b/src/plugins/cppeditor/cppcodemodelsettingspage.cpp @@ -3,13 +3,10 @@ #include "cppcodemodelsettingspage.h" -#include "clangdiagnosticconfigsselectionwidget.h" -#include "clangdiagnosticconfigswidget.h" #include "clangdsettings.h" #include "cppcodemodelsettings.h" #include "cppeditorconstants.h" #include "cppeditortr.h" -#include "cpptoolsreuse.h" #include <coreplugin/dialogs/ioptionspage.h> #include <coreplugin/icore.h> @@ -22,30 +19,20 @@ #include <utils/clangutils.h> #include <utils/fancylineedit.h> #include <utils/infolabel.h> -#include <utils/itemviews.h> #include <utils/layoutbuilder.h> #include <utils/pathchooser.h> #include <utils/qtcassert.h> -#include <utils/variablechooser.h> #include <QCheckBox> #include <QComboBox> -#include <QDesktopServices> -#include <QFormLayout> -#include <QGroupBox> -#include <QGuiApplication> -#include <QInputDialog> -#include <QPushButton> +#include <QPlainTextEdit> #include <QSpinBox> -#include <QStringListModel> #include <QTextBlock> #include <QTextStream> #include <QTimer> #include <QVBoxLayout> #include <QVersionNumber> -#include <limits> - using namespace ProjectExplorer; namespace CppEditor::Internal { @@ -232,518 +219,6 @@ void setupCppCodeModelProjectSettingsPanel() static CppCodeModelProjectSettingsPanelFactory factory; } -class ClangdSettingsWidget final : public QWidget -{ - Q_OBJECT - -public: - ClangdSettingsWidget(const ClangdSettings::Data &settingsData, bool isForProject); - - ClangdSettings::Data settingsData() const; - -signals: - void settingsDataChanged(); - -private: - QCheckBox m_useClangdCheckBox; - QComboBox m_indexingComboBox; - Utils::FancyLineEdit m_projectIndexPathTemplateLineEdit; - Utils::FancyLineEdit m_sessionIndexPathTemplateLineEdit; - QComboBox m_headerSourceSwitchComboBox; - QComboBox m_completionRankingModelComboBox; - QCheckBox m_autoIncludeHeadersCheckBox; - QCheckBox m_sizeThresholdCheckBox; - QSpinBox m_threadLimitSpinBox; - QSpinBox m_documentUpdateThreshold; - QSpinBox m_sizeThresholdSpinBox; - QSpinBox m_completionResults; - Utils::PathChooser m_clangdChooser; - Utils::InfoLabel m_versionWarningLabel; - ClangDiagnosticConfigsSelectionWidget *m_configSelectionWidget = nullptr; - QGroupBox *m_sessionsGroupBox = nullptr; - QStringListModel m_sessionsModel; -}; - -ClangdSettingsWidget::ClangdSettingsWidget(const ClangdSettings::Data &settingsData, - bool isForProject) -{ - const ClangdSettings settings(settingsData); - const QString indexingToolTip = Tr::tr( - "<p>If background indexing is enabled, global symbol searches will yield more accurate " - "results, at the cost of additional CPU load when the project is first opened. The " - "indexing result is persisted in the project's build directory. If you disable background " - "indexing, a faster, but less accurate, built-in indexer is used instead. The thread " - "priority for building the background index can be adjusted since clangd 15.</p>" - "<p>Background Priority: Minimum priority, runs on idle CPUs. May leave 'performance' " - "cores unused.</p>" - "<p>Normal Priority: Reduced priority compared to interactive work.</p>" - "<p>Low Priority: Same priority as other clangd work.</p>"); - const QString projectIndexPathToolTip = Tr::tr( - "The location of the per-project clangd index.<p>" - "This is also where the compile_commands.json file will go."); - const QString sessionIndexPathToolTip = Tr::tr( - "The location of the per-session clangd index.<p>" - "This is also where the compile_commands.json file will go."); - const QString headerSourceSwitchToolTip = Tr::tr( - "<p>The C/C++ backend to use for switching between header and source files.</p>" - "<p>While the clangd implementation has more capabilities than the built-in " - "code model, it tends to find false positives.</p>" - "<p>When \"Try Both\" is selected, clangd is used only if the built-in variant " - "does not find anything.</p>"); - using RankingModel = ClangdSettings::CompletionRankingModel; - const QString completionRankingModelToolTip = Tr::tr( - "<p>Which model clangd should use to rank possible completions.</p>" - "<p>This determines the order of candidates in the combo box when doing code completion.</p>" - "<p>The \"%1\" model used by default results from (pre-trained) machine learning and " - "provides superior results on average.</p>" - "<p>If you feel that its suggestions stray too much from your expectations for your " - "code base, you can try switching to the hand-crafted \"%2\" model.</p>").arg( - ClangdSettings::rankingModelToDisplayString(RankingModel::DecisionForest), - ClangdSettings::rankingModelToDisplayString(RankingModel::Heuristics)); - const QString workerThreadsToolTip = Tr::tr( - "Number of worker threads used by clangd. Background indexing also uses this many " - "worker threads."); - const QString autoIncludeToolTip = Tr::tr( - "Controls whether clangd may insert header files as part of symbol completion."); - const QString documentUpdateToolTip - //: %1 is the application name (Qt Creator) - = Tr::tr("Defines the amount of time %1 waits before sending document changes to the " - "server.\n" - "If the document changes again while waiting, this timeout resets.") - .arg(QGuiApplication::applicationDisplayName()); - const QString sizeThresholdToolTip = Tr::tr( - "Files greater than this will not be opened as documents in clangd.\n" - "The built-in code model will handle highlighting, completion and so on."); - const QString completionResultToolTip = Tr::tr( - "The maximum number of completion results returned by clangd."); - - m_useClangdCheckBox.setText(Tr::tr("Use clangd")); - m_useClangdCheckBox.setChecked(settings.useClangd()); - m_clangdChooser.setExpectedKind(Utils::PathChooser::ExistingCommand); - m_clangdChooser.setFilePath(settings.clangdFilePath()); - m_clangdChooser.setAllowPathFromDevice(true); - m_clangdChooser.setEnabled(m_useClangdCheckBox.isChecked()); - m_clangdChooser.setCommandVersionArguments({"--version"}); - using Priority = ClangdSettings::IndexingPriority; - for (Priority prio : {Priority::Off, Priority::Background, Priority::Low, Priority::Normal}) { - m_indexingComboBox.addItem(ClangdSettings::priorityToDisplayString(prio), int(prio)); - if (prio == settings.indexingPriority()) - m_indexingComboBox.setCurrentIndex(m_indexingComboBox.count() - 1); - } - m_indexingComboBox.setToolTip(indexingToolTip); - m_projectIndexPathTemplateLineEdit.setText(settings.data().projectIndexPathTemplate); - m_sessionIndexPathTemplateLineEdit.setText(settings.data().sessionIndexPathTemplate); - using SwitchMode = ClangdSettings::HeaderSourceSwitchMode; - for (SwitchMode mode : {SwitchMode::BuiltinOnly, SwitchMode::ClangdOnly, SwitchMode::Both}) { - m_headerSourceSwitchComboBox.addItem( - ClangdSettings::headerSourceSwitchModeToDisplayString(mode), int(mode)); - if (mode == settings.headerSourceSwitchMode()) - m_headerSourceSwitchComboBox.setCurrentIndex( - m_headerSourceSwitchComboBox.count() - 1); - } - m_headerSourceSwitchComboBox.setToolTip(headerSourceSwitchToolTip); - for (RankingModel model : {RankingModel::Default, RankingModel::DecisionForest, - RankingModel::Heuristics}) { - m_completionRankingModelComboBox.addItem( - ClangdSettings::rankingModelToDisplayString(model), int(model)); - if (model == settings.completionRankingModel()) - m_completionRankingModelComboBox.setCurrentIndex( - m_completionRankingModelComboBox.count() - 1); - } - m_completionRankingModelComboBox.setToolTip(completionRankingModelToolTip); - - m_autoIncludeHeadersCheckBox.setText(Tr::tr("Insert header files on completion")); - m_autoIncludeHeadersCheckBox.setChecked(settings.autoIncludeHeaders()); - m_autoIncludeHeadersCheckBox.setToolTip(autoIncludeToolTip); - m_threadLimitSpinBox.setValue(settings.workerThreadLimit()); - m_threadLimitSpinBox.setSpecialValueText(Tr::tr("Automatic")); - m_threadLimitSpinBox.setToolTip(workerThreadsToolTip); - m_documentUpdateThreshold.setMinimum(50); - m_documentUpdateThreshold.setMaximum(10000); - m_documentUpdateThreshold.setValue(settings.documentUpdateThreshold()); - m_documentUpdateThreshold.setSingleStep(100); - m_documentUpdateThreshold.setSuffix(" ms"); - m_documentUpdateThreshold.setToolTip(documentUpdateToolTip); - m_sizeThresholdCheckBox.setText(Tr::tr("Ignore files greater than")); - m_sizeThresholdCheckBox.setChecked(settings.sizeThresholdEnabled()); - m_sizeThresholdCheckBox.setToolTip(sizeThresholdToolTip); - m_sizeThresholdSpinBox.setMinimum(1); - m_sizeThresholdSpinBox.setMaximum(std::numeric_limits<int>::max()); - m_sizeThresholdSpinBox.setSuffix(" KB"); - m_sizeThresholdSpinBox.setValue(settings.sizeThresholdInKb()); - m_sizeThresholdSpinBox.setToolTip(sizeThresholdToolTip); - - const auto completionResultsLabel = new QLabel(Tr::tr("Completion results:")); - completionResultsLabel->setToolTip(completionResultToolTip); - m_completionResults.setMinimum(0); - m_completionResults.setMaximum(std::numeric_limits<int>::max()); - m_completionResults.setValue(settings.completionResults()); - m_completionResults.setToolTip(completionResultToolTip); - m_completionResults.setSpecialValueText(Tr::tr("No limit")); - - const auto layout = new QVBoxLayout(this); - layout->setContentsMargins(0, 0, 0, 0); - layout->addWidget(&m_useClangdCheckBox); - - const auto formLayout = new QFormLayout; - const auto chooserLabel = new QLabel(Tr::tr("Path to executable:")); - formLayout->addRow(chooserLabel, &m_clangdChooser); - formLayout->addRow(QString(), &m_versionWarningLabel); - - const auto indexingPriorityLayout = new QHBoxLayout; - indexingPriorityLayout->addWidget(&m_indexingComboBox); - indexingPriorityLayout->addStretch(1); - const auto indexingPriorityLabel = new QLabel(Tr::tr("Background indexing:")); - indexingPriorityLabel->setToolTip(indexingToolTip); - formLayout->addRow(indexingPriorityLabel, indexingPriorityLayout); - - for (const auto &[text, edit, toolTip, defaultValue] : - {std::make_tuple(Tr::tr("Per-project index location:"), - &m_projectIndexPathTemplateLineEdit, - projectIndexPathToolTip, - ClangdSettings::defaultProjectIndexPathTemplate()), - std::make_tuple(Tr::tr("Per-session index location:"), - &m_sessionIndexPathTemplateLineEdit, - sessionIndexPathToolTip, - ClangdSettings::defaultSessionIndexPathTemplate())}) { - if (isForProject && edit == &m_sessionIndexPathTemplateLineEdit) - continue; - - const auto chooser = new Utils::VariableChooser(edit); - chooser->addSupportedWidget(edit); - chooser->addMacroExpanderProvider([] { return Utils::globalMacroExpander(); }); - - const auto resetButton = new QPushButton(Tr::tr("Reset")); - connect(resetButton, &QPushButton::clicked, [e = edit, v = defaultValue] { e->setText(v); }); - const auto layout = new QHBoxLayout; - const auto label = new QLabel(text); - label->setToolTip(toolTip); - edit->setToolTip(toolTip); - layout->addWidget(edit); - layout->addWidget(resetButton); - formLayout->addRow(label, layout); - } - - const auto headerSourceSwitchLayout = new QHBoxLayout; - headerSourceSwitchLayout->addWidget(&m_headerSourceSwitchComboBox); - headerSourceSwitchLayout->addStretch(1); - const auto headerSourceSwitchLabel = new QLabel(Tr::tr("Header/source switch mode:")); - headerSourceSwitchLabel->setToolTip(headerSourceSwitchToolTip); - formLayout->addRow(headerSourceSwitchLabel, headerSourceSwitchLayout); - - const auto threadLimitLayout = new QHBoxLayout; - threadLimitLayout->addWidget(&m_threadLimitSpinBox); - threadLimitLayout->addStretch(1); - const auto threadLimitLabel = new QLabel(Tr::tr("Worker thread count:")); - threadLimitLabel->setToolTip(workerThreadsToolTip); - formLayout->addRow(threadLimitLabel, threadLimitLayout); - - formLayout->addRow(QString(), &m_autoIncludeHeadersCheckBox); - const auto limitResultsLayout = new QHBoxLayout; - limitResultsLayout->addWidget(&m_completionResults); - limitResultsLayout->addStretch(1); - formLayout->addRow(completionResultsLabel, limitResultsLayout); - - const auto completionRankingModelLayout = new QHBoxLayout; - completionRankingModelLayout->addWidget(&m_completionRankingModelComboBox); - completionRankingModelLayout->addStretch(1); - const auto completionRankingModelLabel = new QLabel(Tr::tr("Completion ranking model:")); - completionRankingModelLabel->setToolTip(completionRankingModelToolTip); - formLayout->addRow(completionRankingModelLabel, completionRankingModelLayout); - - const auto documentUpdateThresholdLayout = new QHBoxLayout; - documentUpdateThresholdLayout->addWidget(&m_documentUpdateThreshold); - documentUpdateThresholdLayout->addStretch(1); - const auto documentUpdateThresholdLabel = new QLabel(Tr::tr("Document update threshold:")); - documentUpdateThresholdLabel->setToolTip(documentUpdateToolTip); - formLayout->addRow(documentUpdateThresholdLabel, documentUpdateThresholdLayout); - const auto sizeThresholdLayout = new QHBoxLayout; - sizeThresholdLayout->addWidget(&m_sizeThresholdSpinBox); - sizeThresholdLayout->addStretch(1); - formLayout->addRow(&m_sizeThresholdCheckBox, sizeThresholdLayout); - - m_configSelectionWidget = new ClangDiagnosticConfigsSelectionWidget(formLayout); - m_configSelectionWidget->refresh( - diagnosticConfigsModel(settings.customDiagnosticConfigs()), - settings.diagnosticConfigId(), - [](const ClangDiagnosticConfigs &configs, const Utils::Id &configToSelect) { - return new CppEditor::ClangDiagnosticConfigsWidget(configs, configToSelect); - }); - - layout->addLayout(formLayout); - if (!isForProject) { - m_sessionsModel.setStringList(settingsData.sessionsWithOneClangd); - m_sessionsModel.sort(0); - m_sessionsGroupBox = new QGroupBox(Tr::tr("Sessions with a single clangd instance")); - const auto sessionsView = new Utils::ListView; - sessionsView->setModel(&m_sessionsModel); - sessionsView->setToolTip( - Tr::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(m_sessionsGroupBox); - const auto buttonsLayout = new QVBoxLayout; - const auto addButton = new QPushButton(Tr::tr("Add ...")); - const auto removeButton = new QPushButton(Tr::tr("Remove")); - buttonsLayout->addWidget(addButton); - buttonsLayout->addWidget(removeButton); - buttonsLayout->addStretch(1); - innerSessionsLayout->addWidget(sessionsView); - innerSessionsLayout->addLayout(buttonsLayout); - outerSessionsLayout->addWidget(m_sessionsGroupBox); - outerSessionsLayout->addStretch(1); - - const auto separator = new QFrame; - separator->setFrameShape(QFrame::HLine); - layout->addWidget(separator); - 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); - m_sessionsModel.removeRow(selection.indexes().first().row()); - }); - - connect(addButton, &QPushButton::clicked, this, [this, sessionsView] { - QInputDialog dlg(sessionsView); - QStringList sessions = Core::SessionManager::sessions(); - QStringList currentSessions = m_sessionsModel.stringList(); - for (const QString &s : std::as_const(currentSessions)) - sessions.removeOne(s); - if (sessions.isEmpty()) - return; - sessions.sort(); - dlg.setLabelText(Tr::tr("Choose a session:")); - dlg.setComboBoxItems(sessions); - if (dlg.exec() == QDialog::Accepted) { - currentSessions << dlg.textValue(); - m_sessionsModel.setStringList(currentSessions); - m_sessionsModel.sort(0); - } - }); - } - - const auto configFilesHelpLabel = new QLabel; - configFilesHelpLabel->setText(Tr::tr("Additional settings are available via " - "<a href=\"https://clangd.llvm.org/config\"> clangd configuration files</a>.<br>" - "User-specific settings go <a href=\"%1\">here</a>, " - "project-specific settings can be configured by putting a .clangd file into " - "the project source tree.") - .arg(ClangdSettings::clangdUserConfigFilePath().toUserOutput())); - configFilesHelpLabel->setWordWrap(true); - connect(configFilesHelpLabel, &QLabel::linkHovered, configFilesHelpLabel, &QLabel::setToolTip); - connect(configFilesHelpLabel, &QLabel::linkActivated, [](const QString &link) { - if (link.startsWith("https")) - QDesktopServices::openUrl(link); - else - Core::EditorManager::openEditor(Utils::FilePath::fromString(link)); - }); - layout->addWidget(Layouting::createHr()); - layout->addWidget(configFilesHelpLabel); - - layout->addStretch(1); - - static const auto setWidgetsEnabled = [](QLayout *layout, bool enabled, const auto &f) -> void { - for (int i = 0; i < layout->count(); ++i) { - if (QWidget * const w = layout->itemAt(i)->widget()) - w->setEnabled(enabled); - else if (QLayout * const l = layout->itemAt(i)->layout()) - f(l, enabled, f); - } - }; - const auto toggleEnabled = [this, formLayout](const bool checked) { - setWidgetsEnabled(formLayout, checked, setWidgetsEnabled); - if (m_sessionsGroupBox) - m_sessionsGroupBox->setEnabled(checked); - }; - connect(&m_useClangdCheckBox, &QCheckBox::toggled, toggleEnabled); - toggleEnabled(m_useClangdCheckBox.isChecked()); - m_threadLimitSpinBox.setEnabled(m_useClangdCheckBox.isChecked()); - - m_versionWarningLabel.setType(Utils::InfoLabel::Warning); - const auto updateWarningLabel = [this] { - class WarningLabelSetter { - public: - WarningLabelSetter(QLabel &label) : m_label(label) { m_label.clear(); } - ~WarningLabelSetter() { m_label.setVisible(!m_label.text().isEmpty()); } - void setWarning(const QString &text) { m_label.setText(text); } - private: - QLabel &m_label; - }; - WarningLabelSetter labelSetter(m_versionWarningLabel); - - if (!m_clangdChooser.isValid()) - return; - const Utils::FilePath clangdPath = m_clangdChooser.filePath(); - QString errorMessage; - if (!Utils::checkClangdVersion(clangdPath, &errorMessage)) - labelSetter.setWarning(errorMessage); - }; - connect(&m_clangdChooser, &Utils::PathChooser::textChanged, this, updateWarningLabel); - connect(&m_clangdChooser, &Utils::PathChooser::validChanged, this, updateWarningLabel); - updateWarningLabel(); - - connect(&m_useClangdCheckBox, &QCheckBox::toggled, - this, &ClangdSettingsWidget::settingsDataChanged); - connect(&m_indexingComboBox, &QComboBox::currentIndexChanged, - this, &ClangdSettingsWidget::settingsDataChanged); - connect(&m_projectIndexPathTemplateLineEdit, &QLineEdit::textChanged, - this, &ClangdSettingsWidget::settingsDataChanged); - connect(&m_sessionIndexPathTemplateLineEdit, &QLineEdit::textChanged, - this, &ClangdSettingsWidget::settingsDataChanged); - connect(&m_headerSourceSwitchComboBox, &QComboBox::currentIndexChanged, - this, &ClangdSettingsWidget::settingsDataChanged); - connect(&m_completionRankingModelComboBox, &QComboBox::currentIndexChanged, - this, &ClangdSettingsWidget::settingsDataChanged); - connect(&m_autoIncludeHeadersCheckBox, &QCheckBox::toggled, - this, &ClangdSettingsWidget::settingsDataChanged); - connect(&m_threadLimitSpinBox, &QSpinBox::valueChanged, - this, &ClangdSettingsWidget::settingsDataChanged); - connect(&m_sizeThresholdCheckBox, &QCheckBox::toggled, - this, &ClangdSettingsWidget::settingsDataChanged); - connect(&m_sizeThresholdSpinBox, &QSpinBox::valueChanged, - this, &ClangdSettingsWidget::settingsDataChanged); - connect(&m_documentUpdateThreshold, &QSpinBox::valueChanged, - this, &ClangdSettingsWidget::settingsDataChanged); - connect(&m_clangdChooser, &Utils::PathChooser::textChanged, - this, &ClangdSettingsWidget::settingsDataChanged); - connect(m_configSelectionWidget, &ClangDiagnosticConfigsSelectionWidget::changed, - this, &ClangdSettingsWidget::settingsDataChanged); - connect(&m_completionResults, &QSpinBox::valueChanged, - this, &ClangdSettingsWidget::settingsDataChanged); -} - -ClangdSettings::Data ClangdSettingsWidget::settingsData() const -{ - ClangdSettings::Data data; - data.useClangd = m_useClangdCheckBox.isChecked(); - data.executableFilePath = m_clangdChooser.filePath(); - data.indexingPriority = ClangdSettings::IndexingPriority( - m_indexingComboBox.currentData().toInt()); - data.projectIndexPathTemplate = m_projectIndexPathTemplateLineEdit.text(); - data.sessionIndexPathTemplate = m_sessionIndexPathTemplateLineEdit.text(); - data.headerSourceSwitchMode = ClangdSettings::HeaderSourceSwitchMode( - m_headerSourceSwitchComboBox.currentData().toInt()); - data.completionRankingModel = ClangdSettings::CompletionRankingModel( - m_completionRankingModelComboBox.currentData().toInt()); - data.autoIncludeHeaders = m_autoIncludeHeadersCheckBox.isChecked(); - data.workerThreadLimit = m_threadLimitSpinBox.value(); - data.documentUpdateThreshold = m_documentUpdateThreshold.value(); - data.sizeThresholdEnabled = m_sizeThresholdCheckBox.isChecked(); - data.sizeThresholdInKb = m_sizeThresholdSpinBox.value(); - data.sessionsWithOneClangd = m_sessionsModel.stringList(); - data.customDiagnosticConfigs = m_configSelectionWidget->customConfigs(); - data.diagnosticConfigId = m_configSelectionWidget->currentConfigId(); - data.completionResults = m_completionResults.value(); - return data; -} - -class ClangdSettingsPageWidget final : public Core::IOptionsPageWidget -{ -public: - ClangdSettingsPageWidget() : m_widget(ClangdSettings::instance().data(), false) - { - const auto layout = new QVBoxLayout(this); - layout->addWidget(&m_widget); - } - -private: - void apply() final { ClangdSettings::instance().setData(m_widget.settingsData()); } - - ClangdSettingsWidget m_widget; -}; - -class ClangdSettingsPage final : public Core::IOptionsPage -{ -public: - ClangdSettingsPage() - { - setId(Constants::CPP_CLANGD_SETTINGS_ID); - setDisplayName(Tr::tr("Clangd")); - setCategory(Constants::CPP_SETTINGS_CATEGORY); - setWidgetCreator([] { return new ClangdSettingsPageWidget; }); - } -}; - -void setupClangdSettingsPage() -{ - static ClangdSettingsPage theClangdSettingsPage; -} - -class ClangdProjectSettingsWidget : public ProjectSettingsWidget -{ -public: - ClangdProjectSettingsWidget(const ClangdProjectSettings &settings) - : m_settings(settings), m_widget(settings.settings(), true) - { - setGlobalSettingsId(Constants::CPP_CLANGD_SETTINGS_ID); - const auto layout = new QVBoxLayout(this); - layout->setContentsMargins(0, 0, 0, 0); - layout->addWidget(&m_widget); - - const auto updateGlobalSettingsCheckBox = [this] { - if (ClangdSettings::instance().granularity() == ClangdSettings::Granularity::Session) { - setUseGlobalSettingsCheckBoxEnabled(false); - setUseGlobalSettings(true); - } else { - setUseGlobalSettingsCheckBoxEnabled(true); - setUseGlobalSettings(m_settings.useGlobalSettings()); - } - m_widget.setEnabled(!useGlobalSettings()); - }; - - updateGlobalSettingsCheckBox(); - connect(&ClangdSettings::instance(), &ClangdSettings::changed, - this, updateGlobalSettingsCheckBox); - - connect(this, &ProjectSettingsWidget::useGlobalSettingsChanged, this, - [this](bool checked) { - m_widget.setEnabled(!checked); - m_settings.setUseGlobalSettings(checked); - if (!checked) - m_settings.setSettings(m_widget.settingsData()); - }); - - const auto timer = new QTimer(this); - timer->setSingleShot(true); - timer->setInterval(5000); - connect(timer, &QTimer::timeout, this, [this] { - m_settings.setSettings(m_widget.settingsData()); - }); - connect(&m_widget, &ClangdSettingsWidget::settingsDataChanged, - timer, qOverload<>(&QTimer::start)); - } - -private: - ClangdProjectSettings m_settings; - ClangdSettingsWidget m_widget; -}; - -class ClangdProjectSettingsPanelFactory final : public ProjectPanelFactory -{ -public: - ClangdProjectSettingsPanelFactory() - { - setPriority(100); - setDisplayName(Tr::tr("Clangd")); - setCreateWidgetFunction([](Project *project) { - return new ClangdProjectSettingsWidget(project); - }); - } -}; - -void setupClangdProjectSettingsPanel() -{ - static ClangdProjectSettingsPanelFactory theClangdProjectSettingsPanelFactory; -} - } // CppEditor::Internal #include "cppcodemodelsettingspage.moc" diff --git a/src/plugins/cppeditor/cppcodemodelsettingspage.h b/src/plugins/cppeditor/cppcodemodelsettingspage.h index 4c11b6251e..36d47ff875 100644 --- a/src/plugins/cppeditor/cppcodemodelsettingspage.h +++ b/src/plugins/cppeditor/cppcodemodelsettingspage.h @@ -7,7 +7,5 @@ namespace CppEditor::Internal { void setupCppCodeModelSettingsPage(); void setupCppCodeModelProjectSettingsPanel(); -void setupClangdProjectSettingsPanel(); -void setupClangdSettingsPage(); } // CppEditor::Internal diff --git a/src/plugins/cppeditor/cppeditorplugin.cpp b/src/plugins/cppeditor/cppeditorplugin.cpp index 9a11039876..77a9997244 100644 --- a/src/plugins/cppeditor/cppeditorplugin.cpp +++ b/src/plugins/cppeditor/cppeditorplugin.cpp @@ -1,6 +1,7 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +#include "clangdsettings.h" #include "cppautocompleter.h" #include "cppcodemodelinspectordialog.h" #include "cppcodemodelsettings.h" |