From ecafdb7543927ffe8a9066ce24ed532a097fa21d Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Tue, 23 Feb 2021 13:51:41 +0100 Subject: [PATCH] ClangCodeModel: Add experimental clangd support If the user has enabled clangd (default is off), we start up one instance per project when it is opened/changed (including build config switches), and trigger background indexing. So far, the index is used to provide results for locators and "Find Usages". Per-document functionality such as semantic highlighting and completion is still provided by libclang. Change-Id: I12532fca1b9c6278baab560e7238cba6189cde9f Reviewed-by: David Schulz --- .../creator-only/creator-clang-codemodel.qdoc | 4 + src/plugins/clangcodemodel/CMakeLists.txt | 3 +- src/plugins/clangcodemodel/clangcodemodel.pro | 6 +- src/plugins/clangcodemodel/clangcodemodel.qbs | 3 + .../clangcodemodel_dependencies.pri | 1 + .../clangcodemodel/clangcodemodelplugin.cpp | 3 +- .../clangeditordocumentprocessor.cpp | 136 +---------- .../clanggloballocatorfilters.cpp | 213 ++++++++++++++++++ .../clanggloballocatorfilters.h | 64 ++++++ .../clangmodelmanagersupport.cpp | 159 +++++++++++++ .../clangcodemodel/clangmodelmanagersupport.h | 9 + .../clangcodemodel/clangrefactoringengine.cpp | 28 +++ .../clangcodemodel/clangrefactoringengine.h | 3 +- src/plugins/clangcodemodel/clangutils.cpp | 196 +++++++++++++--- src/plugins/clangcodemodel/clangutils.h | 10 +- src/plugins/coreplugin/icore.cpp | 8 + src/plugins/coreplugin/icore.h | 1 + src/plugins/cppeditor/cppeditorwidget.cpp | 3 +- src/plugins/cppeditor/cppquickfixes.cpp | 2 +- src/plugins/cpptools/cppclassesfilter.h | 2 +- src/plugins/cpptools/cppcodemodelsettings.cpp | 29 +++ src/plugins/cpptools/cppcodemodelsettings.h | 11 + .../cpptools/cppcodemodelsettingspage.cpp | 23 ++ .../cpptools/cppcodemodelsettingspage.ui | 34 ++- src/plugins/cpptools/cppfunctionsfilter.cpp | 1 - src/plugins/cpptools/cppfunctionsfilter.h | 4 +- src/plugins/cpptools/cpplocatordata.h | 2 +- src/plugins/cpptools/cpplocatorfilter.cpp | 5 +- src/plugins/cpptools/cpplocatorfilter.h | 5 +- src/plugins/cpptools/cppmodelmanager.cpp | 12 +- src/plugins/cpptools/cppmodelmanager.h | 3 + src/plugins/cpptools/cursorineditor.h | 8 +- src/plugins/languageclient/client.cpp | 27 ++- src/plugins/languageclient/client.h | 9 + src/plugins/languageclient/languageclient.qbs | 2 + .../languageclient/languageclientmanager.cpp | 9 + .../languageclient/languageclientmanager.h | 1 + .../languageclientsymbolsupport.h | 4 +- src/plugins/languageclient/locatorfilter.cpp | 18 +- src/plugins/languageclient/locatorfilter.h | 10 +- 40 files changed, 881 insertions(+), 190 deletions(-) create mode 100644 src/plugins/clangcodemodel/clanggloballocatorfilters.cpp create mode 100644 src/plugins/clangcodemodel/clanggloballocatorfilters.h diff --git a/doc/qtcreator/src/editors/creator-only/creator-clang-codemodel.qdoc b/doc/qtcreator/src/editors/creator-only/creator-clang-codemodel.qdoc index 491c53cfd69..d190f3c97a4 100644 --- a/doc/qtcreator/src/editors/creator-only/creator-clang-codemodel.qdoc +++ b/doc/qtcreator/src/editors/creator-only/creator-clang-codemodel.qdoc @@ -138,6 +138,10 @@ edit the value for the \uicontrol {Do not index files greater than} check box. To index all files, deselect the check box. + \li To use clangd instead of the built-in code model for features such as + \e {Find References to Symbol}, check the \uicontrol {Use clangd} checkbox. + \note This is an experimental feature, which might not work reliably yet. + \li The \uicontrol {Diagnostic Configuration} field shows the Clang checks to perform. Click the value of the field to open the \uicontrol {Diagnostic Configurations} dialog, where you can diff --git a/src/plugins/clangcodemodel/CMakeLists.txt b/src/plugins/clangcodemodel/CMakeLists.txt index ac90081b3a8..7659ef06cc7 100644 --- a/src/plugins/clangcodemodel/CMakeLists.txt +++ b/src/plugins/clangcodemodel/CMakeLists.txt @@ -1,7 +1,7 @@ add_qtc_plugin(ClangCodeModel CONDITION TARGET libclang DEPENDS ClangSupport CPlusPlus - PLUGIN_DEPENDS Core CppTools TextEditor + PLUGIN_DEPENDS Core CppTools LanguageClient TextEditor PLUGIN_TEST_DEPENDS CppEditor QmakeProjectManager SOURCES clangactivationsequencecontextprocessor.cpp clangactivationsequencecontextprocessor.h @@ -29,6 +29,7 @@ add_qtc_plugin(ClangCodeModel clangfixitoperationsextractor.cpp clangfixitoperationsextractor.h clangfollowsymbol.cpp clangfollowsymbol.h clangfunctionhintmodel.cpp clangfunctionhintmodel.h + clanggloballocatorfilters.cpp clanggloballocatorfilters.h clanghighlightingresultreporter.cpp clanghighlightingresultreporter.h clanghoverhandler.cpp clanghoverhandler.h clangisdiagnosticrelatedtolocation.h diff --git a/src/plugins/clangcodemodel/clangcodemodel.pro b/src/plugins/clangcodemodel/clangcodemodel.pro index aba8a8ee7f3..64254e131fd 100644 --- a/src/plugins/clangcodemodel/clangcodemodel.pro +++ b/src/plugins/clangcodemodel/clangcodemodel.pro @@ -40,7 +40,8 @@ SOURCES += \ clangtextmark.cpp \ clanguiheaderondiskmanager.cpp \ clangutils.cpp \ - clangoverviewmodel.cpp + clangoverviewmodel.cpp \ + clanggloballocatorfilters.cpp HEADERS += \ clangactivationsequencecontextprocessor.h \ @@ -79,7 +80,8 @@ HEADERS += \ clangtextmark.h \ clanguiheaderondiskmanager.h \ clangutils.h \ - clangoverviewmodel.h + clangoverviewmodel.h \ + clanggloballocatorfilters.h FORMS += clangprojectsettingswidget.ui diff --git a/src/plugins/clangcodemodel/clangcodemodel.qbs b/src/plugins/clangcodemodel/clangcodemodel.qbs index dc9d9fb7574..e434fced5d4 100644 --- a/src/plugins/clangcodemodel/clangcodemodel.qbs +++ b/src/plugins/clangcodemodel/clangcodemodel.qbs @@ -11,6 +11,7 @@ QtcPlugin { Depends { name: "TextEditor" } Depends { name: "Utils" } Depends { name: "ClangSupport" } + Depends { name: "LanguageClient" } Depends { name: "libclang"; required: false } Depends { name: "clang_defines" } @@ -72,6 +73,8 @@ QtcPlugin { "clangfollowsymbol.h", "clangfunctionhintmodel.cpp", "clangfunctionhintmodel.h", + "clanggloballocatorfilters.cpp", + "clanggloballocatorfilters.h", "clanghighlightingresultreporter.cpp", "clanghighlightingresultreporter.h", "clanghoverhandler.cpp", diff --git a/src/plugins/clangcodemodel/clangcodemodel_dependencies.pri b/src/plugins/clangcodemodel/clangcodemodel_dependencies.pri index 9b44838b011..eb6635835be 100644 --- a/src/plugins/clangcodemodel/clangcodemodel_dependencies.pri +++ b/src/plugins/clangcodemodel/clangcodemodel_dependencies.pri @@ -5,6 +5,7 @@ QTC_LIB_DEPENDS += \ QTC_PLUGIN_DEPENDS += \ coreplugin \ cpptools \ + languageclient \ texteditor QTC_TEST_DEPENDS += \ cppeditor \ diff --git a/src/plugins/clangcodemodel/clangcodemodelplugin.cpp b/src/plugins/clangcodemodel/clangcodemodelplugin.cpp index 7393eb590d2..81c596b9092 100644 --- a/src/plugins/clangcodemodel/clangcodemodelplugin.cpp +++ b/src/plugins/clangcodemodel/clangcodemodelplugin.cpp @@ -77,7 +77,8 @@ void ClangCodeModelPlugin::generateCompilationDB() QFuture task = QtConcurrent::run(&Internal::generateCompilationDB, - CppModelManager::instance()->projectInfo(target->project())); + CppModelManager::instance()->projectInfo(target->project()), + CompilationDbPurpose::Project); Core::ProgressManager::addTask(task, tr("Generating Compilation DB"), "generate compilation db"); m_generatorWatcher.setFuture(task); } diff --git a/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp b/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp index 7fa8b9302e1..b282e754eb5 100644 --- a/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp +++ b/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp @@ -31,7 +31,6 @@ #include "clangfixitoperationsextractor.h" #include "clangmodelmanagersupport.h" #include "clanghighlightingresultreporter.h" -#include "clangprojectsettings.h" #include "clangutils.h" #include @@ -66,12 +65,6 @@ namespace ClangCodeModel { namespace Internal { -static ClangProjectSettings &getProjectSettings(ProjectExplorer::Project *project) -{ - QTC_CHECK(project); - return ClangModelManagerSupport::instance()->projectSettings(project); -} - ClangEditorDocumentProcessor::ClangEditorDocumentProcessor( BackendCommunicator &communicator, TextEditor::TextDocument *document) @@ -439,123 +432,6 @@ void ClangEditorDocumentProcessor::onParserFinished() updateBackendProjectPartAndDocument(); } -namespace { -// TODO: Can we marry this with CompilerOptionsBuilder? -class FileOptionsBuilder -{ -public: - FileOptionsBuilder(const QString &filePath, CppTools::ProjectPart &projectPart) - : m_filePath(filePath) - , m_projectPart(projectPart) - , m_builder(projectPart) - { - // Determine the driver mode from toolchain and flags. - m_builder.evaluateCompilerFlags(); - m_isClMode = m_builder.isClStyle(); - - addLanguageOptions(); - addGlobalDiagnosticOptions(); // Before addDiagnosticOptions() so users still can overwrite. - addDiagnosticOptions(); - addGlobalOptions(); - addPrecompiledHeaderOptions(); - } - - const QStringList &options() const { return m_options; } - const ::Utils::Id &diagnosticConfigId() const { return m_diagnosticConfigId; } - CppTools::UseBuildSystemWarnings useBuildSystemWarnings() const - { - return m_useBuildSystemWarnings; - } - -private: - void addLanguageOptions() - { - // Determine file kind with respect to ambiguous headers. - CppTools::ProjectFile::Kind fileKind = CppTools::ProjectFile::classify(m_filePath); - if (fileKind == CppTools::ProjectFile::AmbiguousHeader) { - fileKind = m_projectPart.languageVersion <= ::Utils::LanguageVersion::LatestC - ? CppTools::ProjectFile::CHeader - : CppTools::ProjectFile::CXXHeader; - } - - m_builder.reset(); - m_builder.updateFileLanguage(fileKind); - - m_options.append(m_builder.options()); - } - - void addDiagnosticOptions() - { - if (m_projectPart.project) { - ClangProjectSettings &projectSettings = getProjectSettings(m_projectPart.project); - if (!projectSettings.useGlobalConfig()) { - const ::Utils::Id warningConfigId = projectSettings.warningConfigId(); - const CppTools::ClangDiagnosticConfigsModel configsModel - = CppTools::diagnosticConfigsModel(); - if (configsModel.hasConfigWithId(warningConfigId)) { - addDiagnosticOptionsForConfig(configsModel.configWithId(warningConfigId)); - return; - } - } - } - - addDiagnosticOptionsForConfig(CppTools::codeModelSettings()->clangDiagnosticConfig()); - } - - void addDiagnosticOptionsForConfig(const CppTools::ClangDiagnosticConfig &diagnosticConfig) - { - m_diagnosticConfigId = diagnosticConfig.id(); - m_useBuildSystemWarnings = diagnosticConfig.useBuildSystemWarnings() - ? CppTools::UseBuildSystemWarnings::Yes - : CppTools::UseBuildSystemWarnings::No; - - const QStringList options = m_isClMode - ? CppTools::clangArgsForCl(diagnosticConfig.clangOptions()) - : diagnosticConfig.clangOptions(); - m_options.append(options); - } - - void addGlobalDiagnosticOptions() - { - m_options += CppTools::ClangDiagnosticConfigsModel::globalDiagnosticOptions(); - } - - void addGlobalOptions() - { - if (!m_projectPart.project) - m_options.append(ClangProjectSettings::globalCommandLineOptions()); - else - m_options.append(getProjectSettings(m_projectPart.project).commandLineOptions()); - } - - void addPrecompiledHeaderOptions() - { - using namespace CppTools; - - if (getPchUsage() == UsePrecompiledHeaders::No) - return; - - if (m_projectPart.precompiledHeaders.contains(m_filePath)) - return; - - m_builder.reset(); - m_builder.addPrecompiledHeaderOptions(UsePrecompiledHeaders::Yes); - - m_options.append(m_builder.options()); - } - -private: - const QString &m_filePath; - const CppTools::ProjectPart &m_projectPart; - - ::Utils::Id m_diagnosticConfigId; - CppTools::UseBuildSystemWarnings m_useBuildSystemWarnings = CppTools::UseBuildSystemWarnings::No; - CppTools::CompilerOptionsBuilder m_builder; - bool m_isClMode = false; - QStringList m_options; -}; -} // namespace - void ClangEditorDocumentProcessor::updateBackendDocument(CppTools::ProjectPart &projectPart) { // On registration we send the document content immediately as an unsaved @@ -572,17 +448,11 @@ void ClangEditorDocumentProcessor::updateBackendDocument(CppTools::ProjectPart & return; } - const FileOptionsBuilder fileOptions(filePath(), projectPart); - m_diagnosticConfigId = fileOptions.diagnosticConfigId(); - - const QStringList projectPartOptions = createClangOptions( - projectPart, fileOptions.useBuildSystemWarnings(), - CppTools::ProjectFile::Unsupported); // No language option as FileOptionsBuilder adds it. - - const QStringList compilationArguments = projectPartOptions + fileOptions.options(); + const auto clangOptions = createClangOptions(projectPart, filePath()); + m_diagnosticConfigId = clangOptions.first; m_communicator.documentsOpened( - {fileContainerWithOptionsAndDocumentContent(compilationArguments, projectPart.headerPaths)}); + {fileContainerWithOptionsAndDocumentContent(clangOptions.second, projectPart.headerPaths)}); setLastSentDocumentRevision(filePath(), revision()); } diff --git a/src/plugins/clangcodemodel/clanggloballocatorfilters.cpp b/src/plugins/clangcodemodel/clanggloballocatorfilters.cpp new file mode 100644 index 00000000000..34ed11de098 --- /dev/null +++ b/src/plugins/clangcodemodel/clanggloballocatorfilters.cpp @@ -0,0 +1,213 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "clanggloballocatorfilters.h" + +#include "clangmodelmanagersupport.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace ClangCodeModel { +namespace Internal { + +class CppLocatorFilter : public CppTools::CppLocatorFilter +{ +public: + CppLocatorFilter() + : CppTools::CppLocatorFilter(CppTools::CppModelManager::instance()->locatorData()) + { + setId({}); + setDisplayName({}); + setDefaultShortcutString({}); + setEnabled(false); + } +}; + +class LspWorkspaceFilter : public LanguageClient::WorkspaceLocatorFilter +{ +public: + LspWorkspaceFilter() + { + setId({}); + setDisplayName({}); + setDefaultShortcutString({}); + setEnabled(false); + } +}; + + +class CppClassesFilter : public CppTools::CppClassesFilter +{ +public: + CppClassesFilter() + : CppTools::CppClassesFilter(CppTools::CppModelManager::instance()->locatorData()) + { + setId({}); + setDisplayName({}); + setDefaultShortcutString({}); + setEnabled(false); + } +}; + +class LspClassesFilter : public LanguageClient::WorkspaceClassLocatorFilter +{ +public: + LspClassesFilter() { + setId({}); + setDisplayName({}); + setDefaultShortcutString({}); + setEnabled(false); + } +}; + +class CppFunctionsFilter : public CppTools::CppFunctionsFilter +{ +public: + CppFunctionsFilter() + : CppTools::CppFunctionsFilter(CppTools::CppModelManager::instance()->locatorData()) + { + setId({}); + setDisplayName({}); + setDefaultShortcutString({}); + setEnabled(false); + } +}; + +class LspFunctionsFilter : public LanguageClient::WorkspaceMethodLocatorFilter +{ +public: + LspFunctionsFilter() + { + setId({}); + setDisplayName({}); + setDefaultShortcutString({}); + setEnabled(false); + } +}; + + +ClangGlobalSymbolFilter::ClangGlobalSymbolFilter() + : ClangGlobalSymbolFilter(new CppLocatorFilter, new LspWorkspaceFilter) +{ +} + +ClangGlobalSymbolFilter::ClangGlobalSymbolFilter(ILocatorFilter *cppFilter, + ILocatorFilter *lspFilter) + : m_cppFilter(cppFilter), m_lspFilter(lspFilter) +{ + setId(CppTools::Constants::LOCATOR_FILTER_ID); + setDisplayName(CppTools::Constants::LOCATOR_FILTER_DISPLAY_NAME); + setDefaultShortcutString(":"); + setDefaultIncludedByDefault(false); +} + +ClangGlobalSymbolFilter::~ClangGlobalSymbolFilter() +{ + delete m_cppFilter; + delete m_lspFilter; +} + +void ClangGlobalSymbolFilter::prepareSearch(const QString &entry) +{ + m_cppFilter->prepareSearch(entry); + QVector clients; + for (ProjectExplorer::Project * const project : ProjectExplorer::SessionManager::projects()) { + LanguageClient::Client * const client + = ClangModelManagerSupport::instance()->clientForProject(project); + if (client) + clients << client; + } + if (!clients.isEmpty()) { + static_cast(m_lspFilter) + ->prepareSearch(entry, clients); + } +} + +QList ClangGlobalSymbolFilter::matchesFor( + QFutureInterface &future, const QString &entry) +{ + QList matches = m_cppFilter->matchesFor(future, entry); + const QList lspMatches = m_lspFilter->matchesFor(future, entry); + if (!lspMatches.isEmpty()) { + std::set> locations; + for (const auto &entry : qAsConst(matches)) { + const CppTools::IndexItem::Ptr item + = qvariant_cast(entry.internalData); + locations.insert(std::make_tuple(item->fileName(), item->line(), item->column())); + } + for (const auto &entry : lspMatches) { + if (!entry.internalData.canConvert()) + continue; + const auto link = qvariant_cast(entry.internalData); + if (locations.find(std::make_tuple(link.targetFileName, link.targetLine, + link.targetColumn)) == locations.cend()) { + matches << entry; // TODO: Insert sorted? + } + } + } + + return matches; +} + +void ClangGlobalSymbolFilter::accept(Core::LocatorFilterEntry selection, QString *newText, + int *selectionStart, int *selectionLength) const +{ + if (qvariant_cast(selection.internalData)) + m_cppFilter->accept(selection, newText, selectionStart, selectionLength); + else + m_lspFilter->accept(selection, newText, selectionStart, selectionLength); +} + + +ClangClassesFilter::ClangClassesFilter() + : ClangGlobalSymbolFilter(new CppClassesFilter, new LspClassesFilter) +{ + setId(CppTools::Constants::CLASSES_FILTER_ID); + setDisplayName(CppTools::Constants::CLASSES_FILTER_DISPLAY_NAME); + setDefaultShortcutString("c"); + setDefaultIncludedByDefault(false); +} + +ClangFunctionsFilter::ClangFunctionsFilter() + : ClangGlobalSymbolFilter(new CppFunctionsFilter, new LspFunctionsFilter) +{ + setId(CppTools::Constants::FUNCTIONS_FILTER_ID); + setDisplayName(CppTools::Constants::FUNCTIONS_FILTER_DISPLAY_NAME); + setDefaultShortcutString("m"); + setDefaultIncludedByDefault(false); +} + +} // namespace Internal +} // namespace ClangCodeModel diff --git a/src/plugins/clangcodemodel/clanggloballocatorfilters.h b/src/plugins/clangcodemodel/clanggloballocatorfilters.h new file mode 100644 index 00000000000..e01ac4b3568 --- /dev/null +++ b/src/plugins/clangcodemodel/clanggloballocatorfilters.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include + +namespace ClangCodeModel { +namespace Internal { + +class ClangGlobalSymbolFilter : public Core::ILocatorFilter +{ +public: + ClangGlobalSymbolFilter(); + ClangGlobalSymbolFilter(Core::ILocatorFilter *cppFilter, Core::ILocatorFilter *lspFilter); + ~ClangGlobalSymbolFilter() override; + +private: + void prepareSearch(const QString &entry) override; + QList matchesFor(QFutureInterface &future, + const QString &entry) override; + void accept(Core::LocatorFilterEntry selection, QString *newText, + int *selectionStart, int *selectionLength) const override; + + Core::ILocatorFilter * const m_cppFilter; + Core::ILocatorFilter * const m_lspFilter; +}; + +class ClangClassesFilter : public ClangGlobalSymbolFilter +{ +public: + ClangClassesFilter(); +}; + +class ClangFunctionsFilter : public ClangGlobalSymbolFilter +{ +public: + ClangFunctionsFilter(); +}; + +} // namespace Internal +} // namespace ClangCodeModel diff --git a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp index 8b22f6f1456..aab88ea91d3 100644 --- a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp +++ b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp @@ -33,9 +33,12 @@ #include "clangprojectsettings.h" #include "clangrefactoringengine.h" #include "clangcurrentdocumentfilter.h" +#include "clanggloballocatorfilters.h" #include "clangoverviewmodel.h" #include +#include +#include #include #include @@ -44,23 +47,34 @@ #include #include +#include +#include +#include + #include +#include #include +#include #include +#include #include #include #include +#include #include +#include #include #include #include using namespace ClangCodeModel; using namespace ClangCodeModel::Internal; +using namespace LanguageClient; +static Q_LOGGING_CATEGORY(clangdLog, "qtc.clangcodemodel.clangd", QtWarningMsg); static ClangModelManagerSupport *m_instance = nullptr; static CppTools::CppModelManager *cppModelManager() @@ -79,6 +93,9 @@ ClangModelManagerSupport::ClangModelManagerSupport() CppTools::CppModelManager::instance()->setCurrentDocumentFilter( std::make_unique()); + cppModelManager()->setLocatorFilter(std::make_unique()); + cppModelManager()->setClassesFilter(std::make_unique()); + cppModelManager()->setFunctionsFilter(std::make_unique()); Core::EditorManager *editorManager = Core::EditorManager::instance(); connect(editorManager, &Core::EditorManager::editorOpened, @@ -104,9 +121,14 @@ ClangModelManagerSupport::ClangModelManagerSupport() connect(sessionManager, &ProjectExplorer::SessionManager::aboutToRemoveProject, this, &ClangModelManagerSupport::onAboutToRemoveProject); + CppTools::CppCodeModelSettings::setDefaultClangdPath(Utils::FilePath::fromString( + Core::ICore::clangdExecutable(CLANG_BINDIR))); CppTools::CppCodeModelSettings *settings = CppTools::codeModelSettings(); connect(settings, &CppTools::CppCodeModelSettings::clangDiagnosticConfigsInvalidated, this, &ClangModelManagerSupport::onDiagnosticConfigsInvalidated); + + // TODO: Enable this once we do document-level stuff with clangd (highlighting etc) + // createClient(nullptr, {}); } ClangModelManagerSupport::~ClangModelManagerSupport() @@ -220,6 +242,136 @@ void ClangModelManagerSupport::connectToWidgetsMarkContextMenuRequested(QWidget } } +static QString clientName() { return "ccm-clangd"; } + +void ClangModelManagerSupport::updateLanguageClient(ProjectExplorer::Project *project, + const CppTools::ProjectInfo &projectInfo) +{ + if (!CppTools::codeModelSettings()->useClangd()) + return; + const auto getJsonDbDir = [project] { + if (const ProjectExplorer::Target * const target = project->activeTarget()) { + if (const ProjectExplorer::BuildConfiguration * const bc + = target->activeBuildConfiguration()) { + return bc->buildDirectory(); + } + } + return Utils::FilePath(); + }; + + const Utils::FilePath jsonDbDir = getJsonDbDir(); + if (jsonDbDir.isEmpty()) + return; + const auto generatorWatcher = new QFutureWatcher; + connect(generatorWatcher, &QFutureWatcher::finished, + [this, project, projectInfo, getJsonDbDir, jsonDbDir, generatorWatcher] { + generatorWatcher->deleteLater(); + if (!CppTools::codeModelSettings()->useClangd()) + return; + if (!ProjectExplorer::SessionManager::hasProject(project)) + return; + if (cppModelManager()->projectInfo(project) != projectInfo) + return; + if (getJsonDbDir() != jsonDbDir) + return; + const GenerateCompilationDbResult result = generatorWatcher->result(); + if (!result.error.isEmpty()) { + Core::MessageManager::writeDisrupting( + tr("Cannot use clangd: Failed to generate compilation database:\n%1") + .arg(result.error)); + return; + } + if (Client * const oldClient = clientForProject(project)) + LanguageClientManager::shutdownClient(oldClient); + Client * const client = createClient(project, jsonDbDir); + connect(client, &Client::initialized, this, [client, project, projectInfo, jsonDbDir] { + using namespace ProjectExplorer; + if (!CppTools::codeModelSettings()->useClangd()) + return; + if (!SessionManager::hasProject(project)) + return; + if (cppModelManager()->projectInfo(project) != projectInfo) + return; + + // TODO: We'd like to add all open editor documents for the project to the client here, + // but there doesn't seem to be such an interface. + + // clangd oddity: Background indexing only starts after opening a random file. + // TODO: changes to the compilation db do not seem to trigger re-indexing. + // How to force it? + ProjectNode * const rootNode = project->rootProjectNode(); + if (!rootNode) + return; + const Node * const cxxNode = rootNode->findNode([](Node *n) { + const FileNode * const fileNode = n->asFileNode(); + return fileNode && (fileNode->fileType() == FileType::Source + || fileNode->fileType() == FileType::Header) + && fileNode->filePath().exists(); + }); + if (!cxxNode) + return; + + QFile cxxFile(cxxNode->filePath().toString()); + if (!cxxFile.open(QIODevice::ReadOnly)) + return; + using namespace LanguageServerProtocol; + TextDocumentItem item; + item.setLanguageId("text/x-c++src"); + item.setUri(DocumentUri::fromFilePath(cxxNode->filePath())); + item.setText(QString::fromUtf8(cxxFile.readAll())); + item.setVersion(0); + client->sendContent(DidOpenTextDocumentNotification(DidOpenTextDocumentParams(item))); + client->sendContent(DidCloseTextDocumentNotification(DidCloseTextDocumentParams( + TextDocumentIdentifier{item.uri()}))); + }); + + }); + generatorWatcher->setFuture(Utils::runAsync(&Internal::generateCompilationDB, projectInfo, + CompilationDbPurpose::CodeModel)); +} + +LanguageClient::Client *ClangModelManagerSupport::clientForProject( + const ProjectExplorer::Project *project) +{ + const QList clients = Utils::filtered( + LanguageClientManager::clientsForProject(project), + [](const LanguageClient::Client *c) { + return c->name().startsWith(clientName()) + && c->state() != Client::ShutdownRequested + && c->state() != Client::Shutdown; + }); + QTC_CHECK(clients.size() <= 1); + return clients.empty() ? nullptr : clients.first(); +} + +Client *ClangModelManagerSupport::createClient(ProjectExplorer::Project *project, + const Utils::FilePath &jsonDbDir) +{ + QString clangdArgs = "--index --background-index --limit-results=0"; + if (!jsonDbDir.isEmpty()) + clangdArgs += " --compile-commands-dir=" + jsonDbDir.toString(); + if (clangdLog().isDebugEnabled()) + clangdArgs += " --log=verbose --pretty"; + const auto clientInterface = new StdIOClientInterface; + clientInterface->setExecutable(CppTools::codeModelSettings()->clangdFilePath().toString()); + clientInterface->setArguments(clangdArgs); + const auto client = new Client(clientInterface); + client->setName(clientName()); + LanguageFilter langFilter; + langFilter.mimeTypes = QStringList{"text/x-chdr", "text/x-c++hdr", "text/x-c++src", + "text/x-objc++src", "text/x-objcsrc"}; + client->setSupportedLanguage(langFilter); + LanguageServerProtocol::ClientCapabilities caps = Client::defaultClientCapabilities(); + caps.clearExperimental(); + caps.clearTextDocument(); + client->setClientCapabilities(caps); + client->setLocatorsEnabled(false); + client->setDocumentActionsEnabled(false); + client->setCurrentProject(project); + client->start(); + return client; +} + void ClangModelManagerSupport::onEditorOpened(Core::IEditor *editor) { QTC_ASSERT(editor, return); @@ -232,6 +384,11 @@ void ClangModelManagerSupport::onEditorOpened(Core::IEditor *editor) connectToWidgetsMarkContextMenuRequested(editor->widget()); // TODO: Ensure that not fully loaded documents are updated? + + ProjectExplorer::Project * const project + = ProjectExplorer::SessionManager::projectForFile(document->filePath()); + if (Client * const client = clientForProject(project)) + client->openDocument(textDocument); } } @@ -420,6 +577,8 @@ void ClangModelManagerSupport::onProjectPartsUpdated(ProjectExplorer::Project *p const CppTools::ProjectInfo projectInfo = cppModelManager()->projectInfo(project); QTC_ASSERT(projectInfo.isValid(), return); + updateLanguageClient(project, projectInfo); + QStringList projectPartIds; for (const CppTools::ProjectPart::Ptr &projectPart : projectInfo.projectParts()) projectPartIds.append(projectPart->id()); diff --git a/src/plugins/clangcodemodel/clangmodelmanagersupport.h b/src/plugins/clangcodemodel/clangmodelmanagersupport.h index 40348f850cf..4f0e5543572 100644 --- a/src/plugins/clangcodemodel/clangmodelmanagersupport.h +++ b/src/plugins/clangcodemodel/clangmodelmanagersupport.h @@ -47,6 +47,8 @@ class FollowSymbolInterface; class RefactoringEngineInterface; } // namespace CppTools +namespace LanguageClient { class Client; } + namespace ClangCodeModel { namespace Internal { @@ -77,6 +79,8 @@ public: ClangProjectSettings &projectSettings(ProjectExplorer::Project *project) const; + LanguageClient::Client *clientForProject(const ProjectExplorer::Project *project); + static ClangModelManagerSupport *instance(); private: @@ -118,6 +122,11 @@ private: void connectToTextDocumentContentsChangedForUnsavedFile(TextEditor::TextDocument *textDocument); void connectToWidgetsMarkContextMenuRequested(QWidget *editorWidget); + void updateLanguageClient(ProjectExplorer::Project *project, + const CppTools::ProjectInfo &projectInfo); + LanguageClient::Client *createClient(ProjectExplorer::Project *project, + const Utils::FilePath &jsonDbDir); + private: UiHeaderOnDiskManager m_uiHeaderOnDiskManager; BackendCommunicator m_communicator; diff --git a/src/plugins/clangcodemodel/clangrefactoringengine.cpp b/src/plugins/clangcodemodel/clangrefactoringengine.cpp index ed9af84d660..aad2e650ebc 100644 --- a/src/plugins/clangcodemodel/clangrefactoringengine.cpp +++ b/src/plugins/clangcodemodel/clangrefactoringengine.cpp @@ -26,6 +26,12 @@ #include "clangrefactoringengine.h" #include "clangeditordocumentprocessor.h" +#include "clangmodelmanagersupport.h" + +#include +#include +#include +#include #include #include @@ -80,5 +86,27 @@ void RefactoringEngine::startLocalRenaming(const CppTools::CursorInEditor &data, m_watcher->setFuture(cursorFuture); } +void RefactoringEngine::findUsages(const CppTools::CursorInEditor &cursor, + CppTools::UsagesCallback &&callback) const +{ + ProjectExplorer::Project * const project + = ProjectExplorer::SessionManager::projectForFile(cursor.filePath()); + LanguageClient::Client * const client + = ClangModelManagerSupport::instance()->clientForProject(project); + if (!client || client->state() != LanguageClient::Client::Initialized) { + // TODO: Also forward to built-in if index is not ready. + // This requires us to keep track of workDone status in the client. + // Related: Also allow to override the server string for progress info + CppTools::CppModelManager::builtinRefactoringEngine() + ->findUsages(cursor, std::move(callback)); + return; + } + // TODO: We want to keep our "access type info" feature. + // Check whether we can support it using clang 12's textDocument/ast request + if (!client->documentOpen(cursor.textDocument())) + client->openDocument(cursor.textDocument()); // TODO: Just a workaround + client->symbolSupport().findUsages(cursor.textDocument(), cursor.cursor()); +} + } // namespace Internal } // namespace ClangCodeModel diff --git a/src/plugins/clangcodemodel/clangrefactoringengine.h b/src/plugins/clangcodemodel/clangrefactoringengine.h index 297c8ff2387..ca5f04c8008 100644 --- a/src/plugins/clangcodemodel/clangrefactoringengine.h +++ b/src/plugins/clangcodemodel/clangrefactoringengine.h @@ -46,7 +46,8 @@ public: RenameCallback &&renameSymbolsCallback) override; void globalRename(const CppTools::CursorInEditor &, CppTools::UsagesCallback &&, const QString &) override {} - void findUsages(const CppTools::CursorInEditor &, CppTools::UsagesCallback &&) const override {} + void findUsages(const CppTools::CursorInEditor &cursor, + CppTools::UsagesCallback &&callback) const override; void globalFollowSymbol(const CppTools::CursorInEditor &, ::Utils::ProcessLinkCallback &&, const CPlusPlus::Snapshot &, diff --git a/src/plugins/clangcodemodel/clangutils.cpp b/src/plugins/clangcodemodel/clangutils.cpp index f46b455197d..95b9364e0c3 100644 --- a/src/plugins/clangcodemodel/clangutils.cpp +++ b/src/plugins/clangcodemodel/clangutils.cpp @@ -27,6 +27,7 @@ #include "clangeditordocumentprocessor.h" #include "clangmodelmanagersupport.h" +#include "clangprojectsettings.h" #include @@ -104,14 +105,6 @@ private: } }; -QStringList createClangOptions(const ProjectPart &projectPart, - UseBuildSystemWarnings useBuildSystemWarnings, - ProjectFile::Kind fileKind) -{ - return LibClangOptionsBuilder(projectPart, useBuildSystemWarnings) - .build(fileKind, UsePrecompiledHeaders::No); -} - ProjectPart::Ptr projectPartForFile(const QString &filePath) { if (const auto parser = CppTools::BaseEditorDocumentParser::get(filePath)) @@ -354,34 +347,45 @@ static QStringList projectPartArguments(const ProjectPart &projectPart) static QJsonObject createFileObject(const FilePath &buildDir, const QStringList &arguments, const ProjectPart &projectPart, - const ProjectFile &projFile) + const ProjectFile &projFile, + CompilationDbPurpose purpose) { QJsonObject fileObject; fileObject["file"] = projFile.path; - QJsonArray args = QJsonArray::fromStringList(arguments); + QJsonArray args; - const ProjectFile::Kind kind = ProjectFile::classify(projFile.path); - if (projectPart.toolchainType == ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID - || projectPart.toolchainType == ProjectExplorer::Constants::CLANG_CL_TOOLCHAIN_TYPEID) { - if (ProjectFile::isC(kind)) - args.append("/TC"); - else if (ProjectFile::isCxx(kind)) - args.append("/TP"); + if (purpose == CompilationDbPurpose::Project) { + args = QJsonArray::fromStringList(arguments); + + const ProjectFile::Kind kind = ProjectFile::classify(projFile.path); + if (projectPart.toolchainType == ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID + || projectPart.toolchainType == ProjectExplorer::Constants::CLANG_CL_TOOLCHAIN_TYPEID) { + if (ProjectFile::isC(kind)) + args.append("/TC"); + else if (ProjectFile::isCxx(kind)) + args.append("/TP"); + } else { + QStringList langOption + = createLanguageOptionGcc(kind, + projectPart.languageExtensions + & LanguageExtension::ObjectiveC); + for (const QString &langOptionPart : langOption) + args.append(langOptionPart); + } } else { - QStringList langOption - = createLanguageOptionGcc(kind, - projectPart.languageExtensions - & LanguageExtension::ObjectiveC); - for (const QString &langOptionPart : langOption) - args.append(langOptionPart); + // TODO: Do we really need to re-calculate the project part options per source file? + args = QJsonArray::fromStringList(createClangOptions(projectPart, projFile.path).second); + args.prepend("clang"); // TODO: clang-cl for MSVC targets? Does it matter at all what we put here? } + args.append(QDir::toNativeSeparators(projFile.path)); fileObject["arguments"] = args; fileObject["directory"] = buildDir.toString(); return fileObject; } -GenerateCompilationDbResult generateCompilationDB(CppTools::ProjectInfo projectInfo) +GenerateCompilationDbResult generateCompilationDB(CppTools::ProjectInfo projectInfo, + CompilationDbPurpose purpose) { const FilePath buildDir = buildDirectory(*projectInfo.project()); QTC_ASSERT(!buildDir.isEmpty(), return GenerateCompilationDbResult(QString(), @@ -400,9 +404,12 @@ GenerateCompilationDbResult generateCompilationDB(CppTools::ProjectInfo projectI compileCommandsFile.write("["); for (ProjectPart::Ptr projectPart : projectInfo.projectParts()) { - const QStringList args = projectPartArguments(*projectPart); + QStringList args; + if (purpose == CompilationDbPurpose::Project) + args = projectPartArguments(*projectPart); for (const ProjectFile &projFile : projectPart->files) { - const QJsonObject json = createFileObject(buildDir, args, *projectPart, projFile); + const QJsonObject json = createFileObject(buildDir, args, *projectPart, projFile, + purpose); if (compileCommandsFile.size() > 1) compileCommandsFile.write(","); compileCommandsFile.write('\n' + QJsonDocument(json).toJson().trimmed()); @@ -474,5 +481,142 @@ QString DiagnosticTextInfo::clazyCheckName(const QString &option) return option; } + +namespace { +static ClangProjectSettings &getProjectSettings(ProjectExplorer::Project *project) +{ + QTC_CHECK(project); + return ClangModelManagerSupport::instance()->projectSettings(project); +} + +// TODO: Can we marry this with CompilerOptionsBuilder? +class FileOptionsBuilder +{ +public: + FileOptionsBuilder(const QString &filePath, const CppTools::ProjectPart &projectPart) + : m_filePath(filePath) + , m_projectPart(projectPart) + , m_builder(projectPart) + { + // Determine the driver mode from toolchain and flags. + m_builder.evaluateCompilerFlags(); + m_isClMode = m_builder.isClStyle(); + + addLanguageOptions(); + addGlobalDiagnosticOptions(); // Before addDiagnosticOptions() so users still can overwrite. + addDiagnosticOptions(); + addGlobalOptions(); + addPrecompiledHeaderOptions(); + } + + const QStringList &options() const { return m_options; } + const ::Utils::Id &diagnosticConfigId() const { return m_diagnosticConfigId; } + CppTools::UseBuildSystemWarnings useBuildSystemWarnings() const + { + return m_useBuildSystemWarnings; + } + +private: + void addLanguageOptions() + { + // Determine file kind with respect to ambiguous headers. + CppTools::ProjectFile::Kind fileKind = CppTools::ProjectFile::classify(m_filePath); + if (fileKind == CppTools::ProjectFile::AmbiguousHeader) { + fileKind = m_projectPart.languageVersion <= ::Utils::LanguageVersion::LatestC + ? CppTools::ProjectFile::CHeader + : CppTools::ProjectFile::CXXHeader; + } + + m_builder.reset(); + m_builder.updateFileLanguage(fileKind); + + m_options.append(m_builder.options()); + } + + void addDiagnosticOptions() + { + if (m_projectPart.project) { + ClangProjectSettings &projectSettings = getProjectSettings(m_projectPart.project); + if (!projectSettings.useGlobalConfig()) { + const ::Utils::Id warningConfigId = projectSettings.warningConfigId(); + const CppTools::ClangDiagnosticConfigsModel configsModel + = CppTools::diagnosticConfigsModel(); + if (configsModel.hasConfigWithId(warningConfigId)) { + addDiagnosticOptionsForConfig(configsModel.configWithId(warningConfigId)); + return; + } + } + } + + addDiagnosticOptionsForConfig(CppTools::codeModelSettings()->clangDiagnosticConfig()); + } + + void addDiagnosticOptionsForConfig(const CppTools::ClangDiagnosticConfig &diagnosticConfig) + { + m_diagnosticConfigId = diagnosticConfig.id(); + m_useBuildSystemWarnings = diagnosticConfig.useBuildSystemWarnings() + ? CppTools::UseBuildSystemWarnings::Yes + : CppTools::UseBuildSystemWarnings::No; + + const QStringList options = m_isClMode + ? CppTools::clangArgsForCl(diagnosticConfig.clangOptions()) + : diagnosticConfig.clangOptions(); + m_options.append(options); + } + + void addGlobalDiagnosticOptions() + { + m_options += CppTools::ClangDiagnosticConfigsModel::globalDiagnosticOptions(); + } + + void addGlobalOptions() + { + if (!m_projectPart.project) + m_options.append(ClangProjectSettings::globalCommandLineOptions()); + else + m_options.append(getProjectSettings(m_projectPart.project).commandLineOptions()); + } + + void addPrecompiledHeaderOptions() + { + using namespace CppTools; + + if (getPchUsage() == UsePrecompiledHeaders::No) + return; + + if (m_projectPart.precompiledHeaders.contains(m_filePath)) + return; + + m_builder.reset(); + m_builder.addPrecompiledHeaderOptions(UsePrecompiledHeaders::Yes); + + m_options.append(m_builder.options()); + } + +private: + const QString &m_filePath; + const CppTools::ProjectPart &m_projectPart; + + ::Utils::Id m_diagnosticConfigId; + CppTools::UseBuildSystemWarnings m_useBuildSystemWarnings = CppTools::UseBuildSystemWarnings::No; + CppTools::CompilerOptionsBuilder m_builder; + bool m_isClMode = false; + QStringList m_options; +}; +} // namespace + +QPair createClangOptions(const CppTools::ProjectPart &projectPart, + const QString &filePath) +{ + QPair value; + const FileOptionsBuilder fileOptions(filePath, projectPart); + value.first = fileOptions.diagnosticConfigId(); + LibClangOptionsBuilder optionsBuilder(projectPart, fileOptions.useBuildSystemWarnings()); + const QStringList projectPartOptions = optionsBuilder.build(CppTools::ProjectFile::Unsupported, + UsePrecompiledHeaders::No); + value.second = projectPartOptions + fileOptions.options(); + return value; +} + } // namespace Internal } // namespace Clang diff --git a/src/plugins/clangcodemodel/clangutils.h b/src/plugins/clangcodemodel/clangutils.h index 43d4de22240..7e59eb886e5 100644 --- a/src/plugins/clangcodemodel/clangutils.h +++ b/src/plugins/clangcodemodel/clangutils.h @@ -30,6 +30,7 @@ #include #include +#include #include QT_BEGIN_NAMESPACE @@ -49,9 +50,8 @@ namespace Internal { CppTools::CppEditorDocumentHandle *cppDocument(const QString &filePath); void setLastSentDocumentRevision(const QString &filePath, uint revision); -QStringList createClangOptions(const CppTools::ProjectPart &projectPart, - CppTools::UseBuildSystemWarnings useBuildSystemWarnings, - CppTools::ProjectFile::Kind fileKind); +QPair createClangOptions(const CppTools::ProjectPart &projectPart, + const QString &filePath); CppTools::ProjectPart::Ptr projectPartForFile(const QString &filePath); CppTools::ProjectPart::Ptr projectPartForFileBasedOnProcessor(const QString &filePath); @@ -78,7 +78,9 @@ public: QString error; }; -GenerateCompilationDbResult generateCompilationDB(CppTools::ProjectInfo projectInfo); +enum CompilationDbPurpose { Project, CodeModel }; +GenerateCompilationDbResult generateCompilationDB(CppTools::ProjectInfo projectInfo, + CompilationDbPurpose purpose); class DiagnosticTextInfo { diff --git a/src/plugins/coreplugin/icore.cpp b/src/plugins/coreplugin/icore.cpp index 2adf220b91c..bdedc5404c2 100644 --- a/src/plugins/coreplugin/icore.cpp +++ b/src/plugins/coreplugin/icore.cpp @@ -531,6 +531,14 @@ QString ICore::clangExecutable(const QString &clangBinDirectory) return clangBinary("clang", clangBinDirectory); } +/*! + \internal +*/ +QString ICore::clangdExecutable(const QString &clangBinDirectory) +{ + return clangBinary("clangd", clangBinDirectory); +} + /*! \internal */ diff --git a/src/plugins/coreplugin/icore.h b/src/plugins/coreplugin/icore.h index 565e0b8faa2..93827be0472 100644 --- a/src/plugins/coreplugin/icore.h +++ b/src/plugins/coreplugin/icore.h @@ -164,6 +164,7 @@ public: static QString pluginPath(); static QString userPluginPath(); static QString clangExecutable(const QString &clangBinDirectory); + static QString clangdExecutable(const QString &clangBinDirectory); static QString clangTidyExecutable(const QString &clangBinDirectory); static QString clazyStandaloneExecutable(const QString &clangBinDirectory); static QString clangIncludeDirectory(const QString &clangVersion, diff --git a/src/plugins/cppeditor/cppeditorwidget.cpp b/src/plugins/cppeditor/cppeditorwidget.cpp index 7643b1976b2..6f858dae26c 100644 --- a/src/plugins/cppeditor/cppeditorwidget.cpp +++ b/src/plugins/cppeditor/cppeditorwidget.cpp @@ -472,7 +472,8 @@ void CppEditorWidget::findUsages() void CppEditorWidget::findUsages(QTextCursor cursor) { // 'this' in cursorInEditor is never used (and must never be used) asynchronously. - const CppTools::CursorInEditor cursorInEditor{cursor, textDocument()->filePath(), this}; + const CppTools::CursorInEditor cursorInEditor{cursor, textDocument()->filePath(), this, + textDocument()}; QPointer cppEditorWidget = this; d->m_modelManager->findUsages(cursorInEditor, [=](const CppTools::Usages &usages) { diff --git a/src/plugins/cppeditor/cppquickfixes.cpp b/src/plugins/cppeditor/cppquickfixes.cpp index 433b2416b0f..1be833ad671 100644 --- a/src/plugins/cppeditor/cppquickfixes.cpp +++ b/src/plugins/cppeditor/cppquickfixes.cpp @@ -2068,7 +2068,7 @@ void AddIncludeForUndefinedIdentifier::match(const CppQuickFixInterface &interfa const Snapshot forwardHeaders = forwardingHeaders(interface); foreach (const Core::LocatorFilterEntry &entry, matches) { IndexItem::Ptr info = entry.internalData.value(); - if (info->symbolName() != className) + if (!info || info->symbolName() != className) continue; indexItems << info; diff --git a/src/plugins/cpptools/cppclassesfilter.h b/src/plugins/cpptools/cppclassesfilter.h index 638d9a1867f..a89cae256e3 100644 --- a/src/plugins/cpptools/cppclassesfilter.h +++ b/src/plugins/cpptools/cppclassesfilter.h @@ -32,7 +32,7 @@ namespace CppTools { class CppLocatorData; -class CppClassesFilter : public Internal::CppLocatorFilter +class CPPTOOLS_EXPORT CppClassesFilter : public CppLocatorFilter { Q_OBJECT diff --git a/src/plugins/cpptools/cppcodemodelsettings.cpp b/src/plugins/cpptools/cppcodemodelsettings.cpp index feb1c7f09ad..5c5113d004d 100644 --- a/src/plugins/cpptools/cppcodemodelsettings.cpp +++ b/src/plugins/cpptools/cppcodemodelsettings.cpp @@ -35,6 +35,7 @@ #include using namespace CppTools; +using namespace Utils; static Utils::Id initialClangDiagnosticConfigId() { return Constants::CPP_CLANG_DIAG_CONFIG_BUILDSYSTEM; } @@ -60,6 +61,17 @@ static QString skipIndexingBigFilesKey() static QString indexerFileSizeLimitKey() { return QLatin1String(Constants::CPPTOOLS_INDEXER_FILE_SIZE_LIMIT); } +static QString useClangdKey() { return QLatin1String("UseClangd"); } +static QString clangdPathKey() { return QLatin1String("ClangdPath"); } + +static FilePath g_defaultClangdFilePath; +static FilePath fallbackClangdFilePath() +{ + if (g_defaultClangdFilePath.exists()) + return g_defaultClangdFilePath; + return FilePath::fromString("clangd"); +} + static Utils::Id clangDiagnosticConfigIdFromSettings(QSettings *s) { QTC_ASSERT(s->group() == QLatin1String(Constants::CPPTOOLS_SETTINGSGROUP), return Utils::Id()); @@ -160,6 +172,9 @@ void CppCodeModelSettings::fromSettings(QSettings *s) const QVariant indexerFileSizeLimit = s->value(indexerFileSizeLimitKey(), 5); setIndexerFileSizeLimitInMb(indexerFileSizeLimit.toInt()); + setUseClangd(s->value(useClangdKey(), false).toBool()); + setClangdFilePath(FilePath::fromString(s->value(clangdPathKey()).toString())); + s->endGroup(); if (write) @@ -183,6 +198,8 @@ void CppCodeModelSettings::toSettings(QSettings *s) s->setValue(interpretAmbiguousHeadersAsCHeadersKey(), interpretAmbigiousHeadersAsCHeaders()); s->setValue(skipIndexingBigFilesKey(), skipIndexingBigFiles()); s->setValue(indexerFileSizeLimitKey(), indexerFileSizeLimitInMb()); + s->setValue(useClangdKey(), useClangd()); + s->setValue(clangdPathKey(), m_clangdFilePath.toString()); s->endGroup(); @@ -282,3 +299,15 @@ void CppCodeModelSettings::setEnableLowerClazyLevels(bool yesno) { m_enableLowerClazyLevels = yesno; } + +void CppCodeModelSettings::setDefaultClangdPath(const Utils::FilePath &filePath) +{ + g_defaultClangdFilePath = filePath; +} + +FilePath CppCodeModelSettings::clangdFilePath() const +{ + if (!m_clangdFilePath.isEmpty()) + return m_clangdFilePath; + return fallbackClangdFilePath(); +} diff --git a/src/plugins/cpptools/cppcodemodelsettings.h b/src/plugins/cpptools/cppcodemodelsettings.h index afc326dedc4..475c7dda9e7 100644 --- a/src/plugins/cpptools/cppcodemodelsettings.h +++ b/src/plugins/cpptools/cppcodemodelsettings.h @@ -29,6 +29,8 @@ #include "clangdiagnosticconfigsmodel.h" +#include + #include #include @@ -76,6 +78,13 @@ public: int indexerFileSizeLimitInMb() const; void setIndexerFileSizeLimitInMb(int sizeInMB); + void setUseClangd(bool use) { m_useClangd = use; } + bool useClangd() const { return m_useClangd; } + + static void setDefaultClangdPath(const Utils::FilePath &filePath); + void setClangdFilePath(const Utils::FilePath &filePath) { m_clangdFilePath = filePath; } + Utils::FilePath clangdFilePath() const; + signals: void clangDiagnosticConfigsInvalidated(const QVector &configId); void changed(); @@ -88,6 +97,8 @@ private: ClangDiagnosticConfigs m_clangCustomDiagnosticConfigs; Utils::Id m_clangDiagnosticConfigId; bool m_enableLowerClazyLevels = true; // For UI behavior only + Utils::FilePath m_clangdFilePath; + bool m_useClangd = false; }; } // namespace CppTools diff --git a/src/plugins/cpptools/cppcodemodelsettingspage.cpp b/src/plugins/cpptools/cppcodemodelsettingspage.cpp index 2c1b0ddc02b..00e7b8081ad 100644 --- a/src/plugins/cpptools/cppcodemodelsettingspage.cpp +++ b/src/plugins/cpptools/cppcodemodelsettingspage.cpp @@ -100,6 +100,9 @@ void CppCodeModelSettingsWidget::setupClangCodeModelWidgets() const bool isClangActive = CppModelManager::instance()->isClangCodeModelActive(); m_ui->clangCodeModelIsDisabledHint->setVisible(!isClangActive); m_ui->clangCodeModelIsEnabledHint->setVisible(isClangActive); + m_ui->clangdCheckBox->setVisible(isClangActive); + m_ui->clangdChooser->setVisible(isClangActive); + for (int i = 0; i < m_ui->clangDiagnosticConfigsSelectionWidget->layout()->count(); ++i) { QWidget *widget = m_ui->clangDiagnosticConfigsSelectionWidget->layout()->itemAt(i)->widget(); if (widget) @@ -117,6 +120,16 @@ void CppCodeModelSettingsWidget::setupGeneralWidgets() const bool ignorePch = m_settings->pchUsage() == CppCodeModelSettings::PchUse_None; m_ui->ignorePCHCheckBox->setChecked(ignorePch); + + m_ui->clangdCheckBox->setChecked(m_settings->useClangd()); + m_ui->clangdCheckBox->setToolTip(tr("Use clangd for locators and \"Find References\".\n" + "Changing this option does not affect projects that are already open.")); + m_ui->clangdChooser->setExpectedKind(Utils::PathChooser::ExistingCommand); + m_ui->clangdChooser->setFilePath(codeModelSettings()->clangdFilePath()); + m_ui->clangdChooser->setEnabled(m_ui->clangdCheckBox->isChecked()); + connect(m_ui->clangdCheckBox, &QCheckBox::toggled, m_ui->clangdChooser, [this](bool checked) { + m_ui->clangdChooser->setEnabled(checked); + }); } bool CppCodeModelSettingsWidget::applyClangCodeModelWidgetsToSettings() const @@ -163,6 +176,16 @@ bool CppCodeModelSettingsWidget::applyGeneralWidgetsToSettings() const m_settings->setIndexerFileSizeLimitInMb(newFileSizeLimit); settingsChanged = true; } + const bool newUseClangd = m_ui->clangdCheckBox->isChecked(); + if (m_settings->useClangd() != newUseClangd) { + m_settings->setUseClangd(newUseClangd); + settingsChanged = true; + } + const Utils::FilePath newClangdPath = m_ui->clangdChooser->rawFilePath(); + if (m_settings->clangdFilePath() != newClangdPath) { + m_settings->setClangdFilePath(newClangdPath); + settingsChanged = true; + } const bool newIgnorePch = m_ui->ignorePCHCheckBox->isChecked(); const bool previousIgnorePch = m_settings->pchUsage() == CppCodeModelSettings::PchUse_None; diff --git a/src/plugins/cpptools/cppcodemodelsettingspage.ui b/src/plugins/cpptools/cppcodemodelsettingspage.ui index 996ddc9e755..2362131cbe9 100644 --- a/src/plugins/cpptools/cppcodemodelsettingspage.ui +++ b/src/plugins/cpptools/cppcodemodelsettingspage.ui @@ -6,7 +6,7 @@ 0 0 - 647 + 697 440 @@ -80,6 +80,33 @@ + + + + + + Use clangd (EXPERIMENTAL) + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + @@ -147,6 +174,11 @@ QWidget
cpptools/clangdiagnosticconfigsselectionwidget.h
+ + Utils::PathChooser + QLineEdit +
utils/pathchooser.h
+
diff --git a/src/plugins/cpptools/cppfunctionsfilter.cpp b/src/plugins/cpptools/cppfunctionsfilter.cpp index 8b83508e421..3f3773580fc 100644 --- a/src/plugins/cpptools/cppfunctionsfilter.cpp +++ b/src/plugins/cpptools/cppfunctionsfilter.cpp @@ -30,7 +30,6 @@ #include using namespace CppTools; -using namespace CppTools::Internal; CppFunctionsFilter::CppFunctionsFilter(CppLocatorData *locatorData) : CppLocatorFilter(locatorData) diff --git a/src/plugins/cpptools/cppfunctionsfilter.h b/src/plugins/cpptools/cppfunctionsfilter.h index 04ba9b86122..0eee85c6d8b 100644 --- a/src/plugins/cpptools/cppfunctionsfilter.h +++ b/src/plugins/cpptools/cppfunctionsfilter.h @@ -30,9 +30,8 @@ namespace CppTools { -namespace Internal { -class CppFunctionsFilter : public CppLocatorFilter +class CPPTOOLS_EXPORT CppFunctionsFilter : public CppLocatorFilter { Q_OBJECT @@ -45,5 +44,4 @@ protected: Core::LocatorFilterEntry filterEntryFromIndexItem(IndexItem::Ptr info) override; }; -} // namespace Internal } // namespace CppTools diff --git a/src/plugins/cpptools/cpplocatordata.h b/src/plugins/cpptools/cpplocatordata.h index f8c853471f6..61cfda81691 100644 --- a/src/plugins/cpptools/cpplocatordata.h +++ b/src/plugins/cpptools/cpplocatordata.h @@ -35,7 +35,7 @@ namespace CppTools { -class CppLocatorData : public QObject +class CPPTOOLS_EXPORT CppLocatorData : public QObject { Q_OBJECT diff --git a/src/plugins/cpptools/cpplocatorfilter.cpp b/src/plugins/cpptools/cpplocatorfilter.cpp index 0d2ae76b801..73403a5e6b5 100644 --- a/src/plugins/cpptools/cpplocatorfilter.cpp +++ b/src/plugins/cpptools/cpplocatorfilter.cpp @@ -35,8 +35,7 @@ #include #include -using namespace CppTools; -using namespace CppTools::Internal; +namespace CppTools { CppLocatorFilter::CppLocatorFilter(CppLocatorData *locatorData) : m_data(locatorData) @@ -145,3 +144,5 @@ void CppLocatorFilter::accept(Core::LocatorFilterEntry selection, IndexItem::Ptr info = qvariant_cast(selection.internalData); Core::EditorManager::openEditorAt(info->fileName(), info->line(), info->column()); } + +} // namespace CppTools diff --git a/src/plugins/cpptools/cpplocatorfilter.h b/src/plugins/cpptools/cpplocatorfilter.h index 63df9551170..5c4c2bfaf1d 100644 --- a/src/plugins/cpptools/cpplocatorfilter.h +++ b/src/plugins/cpptools/cpplocatorfilter.h @@ -25,15 +25,15 @@ #pragma once +#include "cpptools_global.h" #include "cpplocatordata.h" #include "searchsymbols.h" #include namespace CppTools { -namespace Internal { -class CppLocatorFilter : public Core::ILocatorFilter +class CPPTOOLS_EXPORT CppLocatorFilter : public Core::ILocatorFilter { Q_OBJECT @@ -54,5 +54,4 @@ protected: CppLocatorData *m_data = nullptr; }; -} // namespace Internal } // namespace CppTools diff --git a/src/plugins/cpptools/cppmodelmanager.cpp b/src/plugins/cpptools/cppmodelmanager.cpp index 9c2de6bbc22..971276f70aa 100644 --- a/src/plugins/cpptools/cppmodelmanager.cpp +++ b/src/plugins/cpptools/cppmodelmanager.cpp @@ -328,7 +328,7 @@ void CppModelManager::globalRename(const CursorInEditor &data, UsagesCallback && void CppModelManager::findUsages(const CppTools::CursorInEditor &data, UsagesCallback &&showUsagesCallback) const { - RefactoringEngineInterface *engine = getRefactoringEngine(d->m_refactoringEngines); + RefactoringEngineInterface *engine = getRefactoringEngine(d->m_refactoringEngines, false); QTC_ASSERT(engine, return;); engine->findUsages(data, std::move(showUsagesCallback)); } @@ -466,6 +466,11 @@ void CppModelManager::removeRefactoringEngine(RefactoringEngineType type) instance()->d->m_refactoringEngines.remove(type); } +RefactoringEngineInterface *CppModelManager::builtinRefactoringEngine() +{ + return instance()->d->m_refactoringEngines.value(RefactoringEngineType::BuiltIn); +} + template static void setFilter(std::unique_ptr &filter, std::unique_ptr &&newFilter) @@ -917,6 +922,11 @@ QByteArray CppModelManager::codeModelConfiguration() const return QByteArray::fromRawData(pp_configuration, qstrlen(pp_configuration)); } +CppLocatorData *CppModelManager::locatorData() const +{ + return &d->m_locatorData; +} + static QSet tooBigFilesRemoved(const QSet &files, int fileSizeLimitInMb) { if (fileSizeLimitInMb <= 0) diff --git a/src/plugins/cpptools/cppmodelmanager.h b/src/plugins/cpptools/cppmodelmanager.h index 48c8ceabb06..1f09f9d8edd 100644 --- a/src/plugins/cpptools/cppmodelmanager.h +++ b/src/plugins/cpptools/cppmodelmanager.h @@ -60,6 +60,7 @@ class BaseEditorDocumentProcessor; class CppCompletionAssistProvider; class CppEditorDocumentHandle; class CppIndexingSupport; +class CppLocatorData; class ModelManagerSupportProvider; class FollowSymbolInterface; class SymbolFinder; @@ -111,6 +112,7 @@ public: void updateCppEditorDocuments(bool projectsUpdated = false) const; WorkingCopy workingCopy() const; QByteArray codeModelConfiguration() const; + CppLocatorData *locatorData() const; QList projectInfos() const; ProjectInfo projectInfo(ProjectExplorer::Project *project) const; @@ -215,6 +217,7 @@ public: static void addRefactoringEngine(RefactoringEngineType type, RefactoringEngineInterface *refactoringEngine); static void removeRefactoringEngine(RefactoringEngineType type); + static RefactoringEngineInterface *builtinRefactoringEngine(); void setLocatorFilter(std::unique_ptr &&filter); void setClassesFilter(std::unique_ptr &&filter); diff --git a/src/plugins/cpptools/cursorineditor.h b/src/plugins/cpptools/cursorineditor.h index b84633d9bc4..8e9a4b70a17 100644 --- a/src/plugins/cpptools/cursorineditor.h +++ b/src/plugins/cpptools/cursorineditor.h @@ -31,24 +31,30 @@ #include +namespace TextEditor { class TextDocument; } + namespace CppTools { class CursorInEditor { public: CursorInEditor(const QTextCursor &cursor, const Utils::FilePath &filePath, - CppEditorWidgetInterface *editorWidget = nullptr) + CppEditorWidgetInterface *editorWidget = nullptr, + TextEditor::TextDocument *textDocument = nullptr) : m_cursor(cursor) , m_filePath(filePath) , m_editorWidget(editorWidget) + , m_textDocument(textDocument) {} CppEditorWidgetInterface *editorWidget() const { return m_editorWidget; } + TextEditor::TextDocument *textDocument() const { return m_textDocument; } const QTextCursor &cursor() const { return m_cursor; } const Utils::FilePath &filePath() const { return m_filePath; } private: QTextCursor m_cursor; Utils::FilePath m_filePath; CppEditorWidgetInterface *m_editorWidget = nullptr; + TextEditor::TextDocument * const m_textDocument; }; } // namespace CppTools diff --git a/src/plugins/languageclient/client.cpp b/src/plugins/languageclient/client.cpp index 10bf1540c9d..046592348f4 100644 --- a/src/plugins/languageclient/client.cpp +++ b/src/plugins/languageclient/client.cpp @@ -292,7 +292,7 @@ void Client::initialize() QTC_ASSERT(m_state == Uninitialized, return); qCDebug(LOGLSPCLIENT) << "initializing language server " << m_displayName; InitializeParams params; - params.setCapabilities(generateClientCapabilities()); + params.setCapabilities(m_clientCapabilities); params.setInitializationOptions(m_initializationOptions); if (m_project) { params.setRootUri(DocumentUri::fromFilePath(m_project->projectDirectory())); @@ -333,6 +333,16 @@ Client::State Client::state() const return m_state; } +ClientCapabilities Client::defaultClientCapabilities() +{ + return generateClientCapabilities(); +} + +void Client::setClientCapabilities(const LanguageServerProtocol::ClientCapabilities &caps) +{ + m_clientCapabilities = caps; +} + void Client::openDocument(TextEditor::TextDocument *document) { using namespace TextEditor; @@ -532,6 +542,9 @@ void Client::requestDocumentHighlights(TextEditor::TextEditorWidget *widget) void Client::activateDocument(TextEditor::TextDocument *document) { + if (!m_documentActionsEnabled) + return; + auto uri = DocumentUri::fromFilePath(document->filePath()); m_diagnosticManager.showDiagnostics(uri); SemanticHighligtingSupport::applyHighlight(document, m_highlights.value(uri), capabilities()); @@ -558,6 +571,9 @@ void Client::activateDocument(TextEditor::TextDocument *document) void Client::deactivateDocument(TextEditor::TextDocument *document) { + if (!m_documentActionsEnabled) + return; + m_diagnosticManager.hideDiagnostics(document); resetAssistProviders(document); document->setFormatter(nullptr); @@ -1241,6 +1257,9 @@ void Client::handleMethod(const QString &method, const MessageId &id, const ICon void Client::handleDiagnostics(const PublishDiagnosticsParams ¶ms) { + if (!m_documentActionsEnabled) + return; + const DocumentUri &uri = params.uri(); const QList &diagnostics = params.diagnostics(); @@ -1253,6 +1272,9 @@ void Client::handleDiagnostics(const PublishDiagnosticsParams ¶ms) void Client::handleSemanticHighlight(const SemanticHighlightingParams ¶ms) { + if (!m_documentActionsEnabled) + return; + DocumentUri uri; LanguageClientValue version; auto textDocument = params.textDocument(); @@ -1283,6 +1305,9 @@ void Client::handleSemanticHighlight(const SemanticHighlightingParams ¶ms) void Client::rehighlight() { + if (!m_documentActionsEnabled) + return; + using namespace TextEditor; for (auto it = m_highlights.begin(), end = m_highlights.end(); it != end; ++it) { if (TextDocument *doc = TextDocument::textDocumentForFilePath(it.key().toFilePath())) { diff --git a/src/plugins/languageclient/client.h b/src/plugins/languageclient/client.h index 734ffd4e96a..e62c33a62f5 100644 --- a/src/plugins/languageclient/client.h +++ b/src/plugins/languageclient/client.h @@ -113,11 +113,17 @@ public: bool reachable() const { return m_state == Initialized; } // capabilities + static LanguageServerProtocol::ClientCapabilities defaultClientCapabilities(); + void setClientCapabilities(const LanguageServerProtocol::ClientCapabilities &caps); const LanguageServerProtocol::ServerCapabilities &capabilities() const; const DynamicCapabilities &dynamicCapabilities() const; void registerCapabilities(const QList ®istrations); void unregisterCapabilities(const QList &unregistrations); + void setLocatorsEnabled(bool enabled) { m_locatorsEnabled = enabled; } + bool locatorsEnabled() const { return m_locatorsEnabled; } + void setDocumentActionsEnabled(bool enabled) { m_documentActionsEnabled = enabled; } + // document synchronization void setSupportedLanguage(const LanguageFilter &filter); void setActivateDocumentAutomatically(bool enabled); @@ -226,6 +232,7 @@ private: QMap m_documentHighlightsTimer; QTimer m_documentUpdateTimer; Utils::Id m_id; + LanguageServerProtocol::ClientCapabilities m_clientCapabilities = defaultClientCapabilities(); LanguageServerProtocol::ServerCapabilities m_serverCapabilities; DynamicCapabilities m_dynamicCapabilities; struct AssistProviders @@ -250,6 +257,8 @@ private: ProgressManager m_progressManager; bool m_activateDocAutomatically = false; SemanticTokenSupport m_tokentSupport; + bool m_locatorsEnabled = true; + bool m_documentActionsEnabled = true; }; } // namespace LanguageClient diff --git a/src/plugins/languageclient/languageclient.qbs b/src/plugins/languageclient/languageclient.qbs index 24036dcd042..a5dbc641f65 100644 --- a/src/plugins/languageclient/languageclient.qbs +++ b/src/plugins/languageclient/languageclient.qbs @@ -57,4 +57,6 @@ QtcPlugin { "semantichighlightsupport.cpp", "semantichighlightsupport.h", ] + + Export { Depends { name: "LanguageServerProtocol" } } } diff --git a/src/plugins/languageclient/languageclientmanager.cpp b/src/plugins/languageclient/languageclientmanager.cpp index 6a1b8ff5a8e..24afdaac643 100644 --- a/src/plugins/languageclient/languageclientmanager.cpp +++ b/src/plugins/languageclient/languageclientmanager.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -375,6 +376,14 @@ Client *LanguageClientManager::clientForUri(const DocumentUri &uri) return clientForFilePath(uri.toFilePath()); } +const QList LanguageClientManager::clientsForProject( + const ProjectExplorer::Project *project) +{ + return Utils::filtered(managerInstance->m_clients, [project](const Client *c) { + return c->project() == project; + }).toList(); +} + void LanguageClientManager::openDocumentWithClient(TextEditor::TextDocument *document, Client *client) { Client *currentClient = clientForDocument(document); diff --git a/src/plugins/languageclient/languageclientmanager.h b/src/plugins/languageclient/languageclientmanager.h index 0867104e9c8..9800db8a130 100644 --- a/src/plugins/languageclient/languageclientmanager.h +++ b/src/plugins/languageclient/languageclientmanager.h @@ -84,6 +84,7 @@ public: static Client *clientForDocument(TextEditor::TextDocument *document); static Client *clientForFilePath(const Utils::FilePath &filePath); static Client *clientForUri(const LanguageServerProtocol::DocumentUri &uri); + static const QList clientsForProject(const ProjectExplorer::Project *project); /// /// \brief openDocumentWithClient diff --git a/src/plugins/languageclient/languageclientsymbolsupport.h b/src/plugins/languageclient/languageclientsymbolsupport.h index 61dd8e3d053..582b5b95f6e 100644 --- a/src/plugins/languageclient/languageclientsymbolsupport.h +++ b/src/plugins/languageclient/languageclientsymbolsupport.h @@ -25,6 +25,8 @@ #pragma once +#include "languageclient_global.h" + #include #include @@ -38,7 +40,7 @@ namespace LanguageClient { class Client; -class SymbolSupport +class LANGUAGECLIENT_EXPORT SymbolSupport { Q_DECLARE_TR_FUNCTIONS(SymbolSupport) public: diff --git a/src/plugins/languageclient/locatorfilter.cpp b/src/plugins/languageclient/locatorfilter.cpp index 9a0c304b0b0..204738b9bfe 100644 --- a/src/plugins/languageclient/locatorfilter.cpp +++ b/src/plugins/languageclient/locatorfilter.cpp @@ -214,6 +214,18 @@ WorkspaceLocatorFilter::WorkspaceLocatorFilter(const QVector &filter } void WorkspaceLocatorFilter::prepareSearch(const QString &entry) +{ + prepareSearch(entry, LanguageClientManager::clients(), false); +} + +void WorkspaceLocatorFilter::prepareSearch(const QString &entry, const QVector &clients) +{ + prepareSearch(entry, clients, true); +} + +void WorkspaceLocatorFilter::prepareSearch(const QString &entry, + const QVector &clients, + bool force) { m_pendingRequests.clear(); m_results.clear(); @@ -222,7 +234,11 @@ void WorkspaceLocatorFilter::prepareSearch(const QString &entry) params.setQuery(entry); QMutexLocker locker(&m_mutex); - for (auto client : Utils::filtered(LanguageClientManager::clients(), &Client::reachable)) { + for (auto client : qAsConst(clients)) { + if (!client->reachable()) + continue; + if (!(force || client->locatorsEnabled())) + continue; Utils::optional> capability = client->capabilities().workspaceSymbolProvider(); if (!capability.has_value()) diff --git a/src/plugins/languageclient/locatorfilter.h b/src/plugins/languageclient/locatorfilter.h index 24e7ff54c50..58da2ddf9c2 100644 --- a/src/plugins/languageclient/locatorfilter.h +++ b/src/plugins/languageclient/locatorfilter.h @@ -74,13 +74,16 @@ private: Utils::optional m_currentSymbols; }; -class WorkspaceLocatorFilter : public Core::ILocatorFilter +class LANGUAGECLIENT_EXPORT WorkspaceLocatorFilter : public Core::ILocatorFilter { Q_OBJECT public: WorkspaceLocatorFilter(); + /// request workspace symbols for all clients with enabled locator void prepareSearch(const QString &entry) override; + /// force request workspace symbols for all given clients + void prepareSearch(const QString &entry, const QVector &clients); QList matchesFor(QFutureInterface &future, const QString &entry) override; void accept(Core::LocatorFilterEntry selection, @@ -95,6 +98,7 @@ protected: explicit WorkspaceLocatorFilter(const QVector &filter); private: + void prepareSearch(const QString &entry, const QVector &clients, bool force); void handleResponse(Client *client, const LanguageServerProtocol::WorkspaceSymbolRequest::Response &response); @@ -104,13 +108,13 @@ private: QVector m_filterKinds; }; -class WorkspaceClassLocatorFilter : public WorkspaceLocatorFilter +class LANGUAGECLIENT_EXPORT WorkspaceClassLocatorFilter : public WorkspaceLocatorFilter { public: WorkspaceClassLocatorFilter(); }; -class WorkspaceMethodLocatorFilter : public WorkspaceLocatorFilter +class LANGUAGECLIENT_EXPORT WorkspaceMethodLocatorFilter : public WorkspaceLocatorFilter { public: WorkspaceMethodLocatorFilter();