// 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 "cppcodemodelsettingspage.h" #include "clangdiagnosticconfigsselectionwidget.h" #include "clangdiagnosticconfigswidget.h" #include "cppcodemodelsettings.h" #include "cppeditorconstants.h" #include "cppeditortr.h" #include "cpptoolsreuse.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace ProjectExplorer; namespace CppEditor::Internal { class CppCodeModelSettingsWidget final : public Core::IOptionsPageWidget { public: CppCodeModelSettingsWidget(const CppCodeModelSettings::Data &data); private: void apply() final { CppCodeModelSettings::instance().setData(data()); } CppCodeModelSettings::Data data() const; QCheckBox *m_interpretAmbiguousHeadersAsCHeaders; QCheckBox *m_ignorePchCheckBox; QCheckBox *m_useBuiltinPreprocessorCheckBox; QCheckBox *m_skipIndexingBigFilesCheckBox; QSpinBox *m_bigFilesLimitSpinBox; QCheckBox *m_ignoreFilesCheckBox; QPlainTextEdit *m_ignorePatternTextEdit; }; CppCodeModelSettingsWidget::CppCodeModelSettingsWidget(const CppCodeModelSettings::Data &data) { m_interpretAmbiguousHeadersAsCHeaders = new QCheckBox(Tr::tr("Interpret ambiguous headers as C headers")); m_skipIndexingBigFilesCheckBox = new QCheckBox(Tr::tr("Do not index files greater than")); m_skipIndexingBigFilesCheckBox->setChecked(data.skipIndexingBigFiles); m_bigFilesLimitSpinBox = new QSpinBox; m_bigFilesLimitSpinBox->setSuffix(Tr::tr("MB")); m_bigFilesLimitSpinBox->setRange(1, 500); m_bigFilesLimitSpinBox->setValue(data.indexerFileSizeLimitInMb); m_ignoreFilesCheckBox = new QCheckBox(Tr::tr("Ignore files")); m_ignoreFilesCheckBox->setToolTip( "

" + Tr::tr("Ignore files that match these wildcard patterns, one wildcard per line.") + "

"); m_ignoreFilesCheckBox->setChecked(data.ignoreFiles); m_ignorePatternTextEdit = new QPlainTextEdit(data.ignorePattern); m_ignorePatternTextEdit->setToolTip(m_ignoreFilesCheckBox->toolTip()); m_ignorePatternTextEdit->setEnabled(m_ignoreFilesCheckBox->isChecked()); connect(m_ignoreFilesCheckBox, &QCheckBox::stateChanged, this, [this] { m_ignorePatternTextEdit->setEnabled(m_ignoreFilesCheckBox->isChecked()); }); m_ignorePchCheckBox = new QCheckBox(Tr::tr("Ignore precompiled headers")); m_ignorePchCheckBox->setToolTip(Tr::tr( "

When precompiled headers are not ignored, the parsing for code " "completion and semantic highlighting will process the precompiled header before " "processing any file.

")); m_useBuiltinPreprocessorCheckBox = new QCheckBox(Tr::tr("Use built-in preprocessor to show " "pre-processed files")); m_useBuiltinPreprocessorCheckBox->setToolTip (Tr::tr("Uncheck this to invoke the actual compiler " "to show a pre-processed source file in the editor.")); m_interpretAmbiguousHeadersAsCHeaders->setChecked(data.interpretAmbigiousHeadersAsC); m_ignorePchCheckBox->setChecked(data.pchUsage == CppCodeModelSettings::PchUse_None); m_useBuiltinPreprocessorCheckBox->setChecked(data.useBuiltinPreprocessor); using namespace Layouting; Column { Group { title(Tr::tr("General")), Column { m_interpretAmbiguousHeadersAsCHeaders, m_ignorePchCheckBox, m_useBuiltinPreprocessorCheckBox, Row { m_skipIndexingBigFilesCheckBox, m_bigFilesLimitSpinBox, st }, Row { Column { m_ignoreFilesCheckBox, st }, m_ignorePatternTextEdit }, } }, st }.attachTo(this); } CppCodeModelSettings::Data CppCodeModelSettingsWidget::data() const { CppCodeModelSettings::Data data; data.interpretAmbigiousHeadersAsC = m_interpretAmbiguousHeadersAsCHeaders->isChecked(); data.skipIndexingBigFiles = m_skipIndexingBigFilesCheckBox->isChecked(); data.useBuiltinPreprocessor = m_useBuiltinPreprocessorCheckBox->isChecked(); data.ignoreFiles = m_ignoreFilesCheckBox->isChecked(); data.ignorePattern = m_ignorePatternTextEdit->toPlainText(); data.indexerFileSizeLimitInMb = m_bigFilesLimitSpinBox->value(); data.pchUsage = m_ignorePchCheckBox->isChecked() ? CppCodeModelSettings::PchUse_None : CppCodeModelSettings::PchUse_BuildSystem; return data; } class CppCodeModelSettingsPage final : public Core::IOptionsPage { public: CppCodeModelSettingsPage() { setId(Constants::CPP_CODE_MODEL_SETTINGS_ID); setDisplayName(Tr::tr("Code Model")); setCategory(Constants::CPP_SETTINGS_CATEGORY); setDisplayCategory(Tr::tr("C++")); setCategoryIconPath(":/projectexplorer/images/settingscategory_cpp.png"); setWidgetCreator( [] { return new CppCodeModelSettingsWidget(CppCodeModelSettings::instance().data()); }); } }; void setupCppCodeModelSettings() { static CppCodeModelSettingsPage theCppCodeModelSettingsPage; } 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( "

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.

" "

Background Priority: Minimum priority, runs on idle CPUs. May leave 'performance' " "cores unused.

" "

Normal Priority: Reduced priority compared to interactive work.

" "

Low Priority: Same priority as other clangd work.

"); const QString projectIndexPathToolTip = Tr::tr( "The location of the per-project clangd index.

" "This is also where the compile_commands.json file will go."); const QString sessionIndexPathToolTip = Tr::tr( "The location of the per-session clangd index.

" "This is also where the compile_commands.json file will go."); const QString headerSourceSwitchToolTip = Tr::tr( "

The C/C++ backend to use for switching between header and source files.

" "

While the clangd implementation has more capabilities than the built-in " "code model, it tends to find false positives.

" "

When \"Try Both\" is selected, clangd is used only if the built-in variant " "does not find anything.

"); using RankingModel = ClangdSettings::CompletionRankingModel; const QString completionRankingModelToolTip = Tr::tr( "

Which model clangd should use to rank possible completions.

" "

This determines the order of candidates in the combo box when doing code completion.

" "

The \"%1\" model used by default results from (pre-trained) machine learning and " "provides superior results on average.

" "

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.

").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::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::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 " " clangd configuration files.
" "User-specific settings go here, " "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"