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 <david.schulz@qt.io>
This commit is contained in:
Christian Kandeler
2021-02-23 13:51:41 +01:00
parent 8bacd9bdc4
commit ecafdb7543
40 changed files with 881 additions and 190 deletions

View File

@@ -138,6 +138,10 @@
edit the value for the \uicontrol {Do not index files greater than} edit the value for the \uicontrol {Do not index files greater than}
check box. To index all files, deselect the check box. 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 \li The \uicontrol {Diagnostic Configuration} field shows the Clang
checks to perform. Click the value of the field to open the checks to perform. Click the value of the field to open the
\uicontrol {Diagnostic Configurations} dialog, where you can \uicontrol {Diagnostic Configurations} dialog, where you can

View File

@@ -1,7 +1,7 @@
add_qtc_plugin(ClangCodeModel add_qtc_plugin(ClangCodeModel
CONDITION TARGET libclang CONDITION TARGET libclang
DEPENDS ClangSupport CPlusPlus DEPENDS ClangSupport CPlusPlus
PLUGIN_DEPENDS Core CppTools TextEditor PLUGIN_DEPENDS Core CppTools LanguageClient TextEditor
PLUGIN_TEST_DEPENDS CppEditor QmakeProjectManager PLUGIN_TEST_DEPENDS CppEditor QmakeProjectManager
SOURCES SOURCES
clangactivationsequencecontextprocessor.cpp clangactivationsequencecontextprocessor.h clangactivationsequencecontextprocessor.cpp clangactivationsequencecontextprocessor.h
@@ -29,6 +29,7 @@ add_qtc_plugin(ClangCodeModel
clangfixitoperationsextractor.cpp clangfixitoperationsextractor.h clangfixitoperationsextractor.cpp clangfixitoperationsextractor.h
clangfollowsymbol.cpp clangfollowsymbol.h clangfollowsymbol.cpp clangfollowsymbol.h
clangfunctionhintmodel.cpp clangfunctionhintmodel.h clangfunctionhintmodel.cpp clangfunctionhintmodel.h
clanggloballocatorfilters.cpp clanggloballocatorfilters.h
clanghighlightingresultreporter.cpp clanghighlightingresultreporter.h clanghighlightingresultreporter.cpp clanghighlightingresultreporter.h
clanghoverhandler.cpp clanghoverhandler.h clanghoverhandler.cpp clanghoverhandler.h
clangisdiagnosticrelatedtolocation.h clangisdiagnosticrelatedtolocation.h

View File

@@ -40,7 +40,8 @@ SOURCES += \
clangtextmark.cpp \ clangtextmark.cpp \
clanguiheaderondiskmanager.cpp \ clanguiheaderondiskmanager.cpp \
clangutils.cpp \ clangutils.cpp \
clangoverviewmodel.cpp clangoverviewmodel.cpp \
clanggloballocatorfilters.cpp
HEADERS += \ HEADERS += \
clangactivationsequencecontextprocessor.h \ clangactivationsequencecontextprocessor.h \
@@ -79,7 +80,8 @@ HEADERS += \
clangtextmark.h \ clangtextmark.h \
clanguiheaderondiskmanager.h \ clanguiheaderondiskmanager.h \
clangutils.h \ clangutils.h \
clangoverviewmodel.h clangoverviewmodel.h \
clanggloballocatorfilters.h
FORMS += clangprojectsettingswidget.ui FORMS += clangprojectsettingswidget.ui

View File

@@ -11,6 +11,7 @@ QtcPlugin {
Depends { name: "TextEditor" } Depends { name: "TextEditor" }
Depends { name: "Utils" } Depends { name: "Utils" }
Depends { name: "ClangSupport" } Depends { name: "ClangSupport" }
Depends { name: "LanguageClient" }
Depends { name: "libclang"; required: false } Depends { name: "libclang"; required: false }
Depends { name: "clang_defines" } Depends { name: "clang_defines" }
@@ -72,6 +73,8 @@ QtcPlugin {
"clangfollowsymbol.h", "clangfollowsymbol.h",
"clangfunctionhintmodel.cpp", "clangfunctionhintmodel.cpp",
"clangfunctionhintmodel.h", "clangfunctionhintmodel.h",
"clanggloballocatorfilters.cpp",
"clanggloballocatorfilters.h",
"clanghighlightingresultreporter.cpp", "clanghighlightingresultreporter.cpp",
"clanghighlightingresultreporter.h", "clanghighlightingresultreporter.h",
"clanghoverhandler.cpp", "clanghoverhandler.cpp",

View File

@@ -5,6 +5,7 @@ QTC_LIB_DEPENDS += \
QTC_PLUGIN_DEPENDS += \ QTC_PLUGIN_DEPENDS += \
coreplugin \ coreplugin \
cpptools \ cpptools \
languageclient \
texteditor texteditor
QTC_TEST_DEPENDS += \ QTC_TEST_DEPENDS += \
cppeditor \ cppeditor \

View File

@@ -77,7 +77,8 @@ void ClangCodeModelPlugin::generateCompilationDB()
QFuture<GenerateCompilationDbResult> task QFuture<GenerateCompilationDbResult> task
= QtConcurrent::run(&Internal::generateCompilationDB, = 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"); Core::ProgressManager::addTask(task, tr("Generating Compilation DB"), "generate compilation db");
m_generatorWatcher.setFuture(task); m_generatorWatcher.setFuture(task);
} }

View File

@@ -31,7 +31,6 @@
#include "clangfixitoperationsextractor.h" #include "clangfixitoperationsextractor.h"
#include "clangmodelmanagersupport.h" #include "clangmodelmanagersupport.h"
#include "clanghighlightingresultreporter.h" #include "clanghighlightingresultreporter.h"
#include "clangprojectsettings.h"
#include "clangutils.h" #include "clangutils.h"
#include <diagnosticcontainer.h> #include <diagnosticcontainer.h>
@@ -66,12 +65,6 @@
namespace ClangCodeModel { namespace ClangCodeModel {
namespace Internal { namespace Internal {
static ClangProjectSettings &getProjectSettings(ProjectExplorer::Project *project)
{
QTC_CHECK(project);
return ClangModelManagerSupport::instance()->projectSettings(project);
}
ClangEditorDocumentProcessor::ClangEditorDocumentProcessor( ClangEditorDocumentProcessor::ClangEditorDocumentProcessor(
BackendCommunicator &communicator, BackendCommunicator &communicator,
TextEditor::TextDocument *document) TextEditor::TextDocument *document)
@@ -439,123 +432,6 @@ void ClangEditorDocumentProcessor::onParserFinished()
updateBackendProjectPartAndDocument(); 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) void ClangEditorDocumentProcessor::updateBackendDocument(CppTools::ProjectPart &projectPart)
{ {
// On registration we send the document content immediately as an unsaved // On registration we send the document content immediately as an unsaved
@@ -572,17 +448,11 @@ void ClangEditorDocumentProcessor::updateBackendDocument(CppTools::ProjectPart &
return; return;
} }
const FileOptionsBuilder fileOptions(filePath(), projectPart); const auto clangOptions = createClangOptions(projectPart, filePath());
m_diagnosticConfigId = fileOptions.diagnosticConfigId(); m_diagnosticConfigId = clangOptions.first;
const QStringList projectPartOptions = createClangOptions(
projectPart, fileOptions.useBuildSystemWarnings(),
CppTools::ProjectFile::Unsupported); // No language option as FileOptionsBuilder adds it.
const QStringList compilationArguments = projectPartOptions + fileOptions.options();
m_communicator.documentsOpened( m_communicator.documentsOpened(
{fileContainerWithOptionsAndDocumentContent(compilationArguments, projectPart.headerPaths)}); {fileContainerWithOptionsAndDocumentContent(clangOptions.second, projectPart.headerPaths)});
setLastSentDocumentRevision(filePath(), revision()); setLastSentDocumentRevision(filePath(), revision());
} }

View File

@@ -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 <cpptools/cppclassesfilter.h>
#include <cpptools/cppfunctionsfilter.h>
#include <cpptools/cppmodelmanager.h>
#include <cpptools/cpptoolsconstants.h>
#include <cpptools/indexitem.h>
#include <languageclient/locatorfilter.h>
#include <projectexplorer/session.h>
#include <utils/link.h>
#include <set>
#include <tuple>
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<LanguageClient::Client *> 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<LanguageClient::WorkspaceLocatorFilter *>(m_lspFilter)
->prepareSearch(entry, clients);
}
}
QList<Core::LocatorFilterEntry> ClangGlobalSymbolFilter::matchesFor(
QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry)
{
QList<Core::LocatorFilterEntry> matches = m_cppFilter->matchesFor(future, entry);
const QList<Core::LocatorFilterEntry> lspMatches = m_lspFilter->matchesFor(future, entry);
if (!lspMatches.isEmpty()) {
std::set<std::tuple<QString, int, int>> locations;
for (const auto &entry : qAsConst(matches)) {
const CppTools::IndexItem::Ptr item
= qvariant_cast<CppTools::IndexItem::Ptr>(entry.internalData);
locations.insert(std::make_tuple(item->fileName(), item->line(), item->column()));
}
for (const auto &entry : lspMatches) {
if (!entry.internalData.canConvert<Utils::Link>())
continue;
const auto link = qvariant_cast<Utils::Link>(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<CppTools::IndexItem::Ptr>(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

View File

@@ -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 <coreplugin/locator/ilocatorfilter.h>
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<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &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

View File

@@ -33,9 +33,12 @@
#include "clangprojectsettings.h" #include "clangprojectsettings.h"
#include "clangrefactoringengine.h" #include "clangrefactoringengine.h"
#include "clangcurrentdocumentfilter.h" #include "clangcurrentdocumentfilter.h"
#include "clanggloballocatorfilters.h"
#include "clangoverviewmodel.h" #include "clangoverviewmodel.h"
#include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h>
#include <coreplugin/messagemanager.h>
#include <cpptools/cppcodemodelsettings.h> #include <cpptools/cppcodemodelsettings.h>
#include <cpptools/cppfollowsymbolundercursor.h> #include <cpptools/cppfollowsymbolundercursor.h>
@@ -44,23 +47,34 @@
#include <cpptools/editordocumenthandle.h> #include <cpptools/editordocumenthandle.h>
#include <cpptools/projectinfo.h> #include <cpptools/projectinfo.h>
#include <languageclient/client.h>
#include <languageclient/languageclientinterface.h>
#include <languageclient/languageclientmanager.h>
#include <texteditor/quickfix.h> #include <texteditor/quickfix.h>
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/project.h> #include <projectexplorer/project.h>
#include <projectexplorer/projectnodes.h>
#include <projectexplorer/session.h> #include <projectexplorer/session.h>
#include <projectexplorer/target.h>
#include <clangsupport/filecontainer.h> #include <clangsupport/filecontainer.h>
#include <utils/algorithm.h> #include <utils/algorithm.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <utils/runextensions.h>
#include <QApplication> #include <QApplication>
#include <QFile>
#include <QMenu> #include <QMenu>
#include <QTextBlock> #include <QTextBlock>
#include <QTimer> #include <QTimer>
using namespace ClangCodeModel; using namespace ClangCodeModel;
using namespace ClangCodeModel::Internal; using namespace ClangCodeModel::Internal;
using namespace LanguageClient;
static Q_LOGGING_CATEGORY(clangdLog, "qtc.clangcodemodel.clangd", QtWarningMsg);
static ClangModelManagerSupport *m_instance = nullptr; static ClangModelManagerSupport *m_instance = nullptr;
static CppTools::CppModelManager *cppModelManager() static CppTools::CppModelManager *cppModelManager()
@@ -79,6 +93,9 @@ ClangModelManagerSupport::ClangModelManagerSupport()
CppTools::CppModelManager::instance()->setCurrentDocumentFilter( CppTools::CppModelManager::instance()->setCurrentDocumentFilter(
std::make_unique<ClangCurrentDocumentFilter>()); std::make_unique<ClangCurrentDocumentFilter>());
cppModelManager()->setLocatorFilter(std::make_unique<ClangGlobalSymbolFilter>());
cppModelManager()->setClassesFilter(std::make_unique<ClangClassesFilter>());
cppModelManager()->setFunctionsFilter(std::make_unique<ClangFunctionsFilter>());
Core::EditorManager *editorManager = Core::EditorManager::instance(); Core::EditorManager *editorManager = Core::EditorManager::instance();
connect(editorManager, &Core::EditorManager::editorOpened, connect(editorManager, &Core::EditorManager::editorOpened,
@@ -104,9 +121,14 @@ ClangModelManagerSupport::ClangModelManagerSupport()
connect(sessionManager, &ProjectExplorer::SessionManager::aboutToRemoveProject, connect(sessionManager, &ProjectExplorer::SessionManager::aboutToRemoveProject,
this, &ClangModelManagerSupport::onAboutToRemoveProject); this, &ClangModelManagerSupport::onAboutToRemoveProject);
CppTools::CppCodeModelSettings::setDefaultClangdPath(Utils::FilePath::fromString(
Core::ICore::clangdExecutable(CLANG_BINDIR)));
CppTools::CppCodeModelSettings *settings = CppTools::codeModelSettings(); CppTools::CppCodeModelSettings *settings = CppTools::codeModelSettings();
connect(settings, &CppTools::CppCodeModelSettings::clangDiagnosticConfigsInvalidated, connect(settings, &CppTools::CppCodeModelSettings::clangDiagnosticConfigsInvalidated,
this, &ClangModelManagerSupport::onDiagnosticConfigsInvalidated); this, &ClangModelManagerSupport::onDiagnosticConfigsInvalidated);
// TODO: Enable this once we do document-level stuff with clangd (highlighting etc)
// createClient(nullptr, {});
} }
ClangModelManagerSupport::~ClangModelManagerSupport() 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<GenerateCompilationDbResult>;
connect(generatorWatcher, &QFutureWatcher<GenerateCompilationDbResult>::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<Client *> 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) void ClangModelManagerSupport::onEditorOpened(Core::IEditor *editor)
{ {
QTC_ASSERT(editor, return); QTC_ASSERT(editor, return);
@@ -232,6 +384,11 @@ void ClangModelManagerSupport::onEditorOpened(Core::IEditor *editor)
connectToWidgetsMarkContextMenuRequested(editor->widget()); connectToWidgetsMarkContextMenuRequested(editor->widget());
// TODO: Ensure that not fully loaded documents are updated? // 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); const CppTools::ProjectInfo projectInfo = cppModelManager()->projectInfo(project);
QTC_ASSERT(projectInfo.isValid(), return); QTC_ASSERT(projectInfo.isValid(), return);
updateLanguageClient(project, projectInfo);
QStringList projectPartIds; QStringList projectPartIds;
for (const CppTools::ProjectPart::Ptr &projectPart : projectInfo.projectParts()) for (const CppTools::ProjectPart::Ptr &projectPart : projectInfo.projectParts())
projectPartIds.append(projectPart->id()); projectPartIds.append(projectPart->id());

View File

@@ -47,6 +47,8 @@ class FollowSymbolInterface;
class RefactoringEngineInterface; class RefactoringEngineInterface;
} // namespace CppTools } // namespace CppTools
namespace LanguageClient { class Client; }
namespace ClangCodeModel { namespace ClangCodeModel {
namespace Internal { namespace Internal {
@@ -77,6 +79,8 @@ public:
ClangProjectSettings &projectSettings(ProjectExplorer::Project *project) const; ClangProjectSettings &projectSettings(ProjectExplorer::Project *project) const;
LanguageClient::Client *clientForProject(const ProjectExplorer::Project *project);
static ClangModelManagerSupport *instance(); static ClangModelManagerSupport *instance();
private: private:
@@ -118,6 +122,11 @@ private:
void connectToTextDocumentContentsChangedForUnsavedFile(TextEditor::TextDocument *textDocument); void connectToTextDocumentContentsChangedForUnsavedFile(TextEditor::TextDocument *textDocument);
void connectToWidgetsMarkContextMenuRequested(QWidget *editorWidget); void connectToWidgetsMarkContextMenuRequested(QWidget *editorWidget);
void updateLanguageClient(ProjectExplorer::Project *project,
const CppTools::ProjectInfo &projectInfo);
LanguageClient::Client *createClient(ProjectExplorer::Project *project,
const Utils::FilePath &jsonDbDir);
private: private:
UiHeaderOnDiskManager m_uiHeaderOnDiskManager; UiHeaderOnDiskManager m_uiHeaderOnDiskManager;
BackendCommunicator m_communicator; BackendCommunicator m_communicator;

View File

@@ -26,6 +26,12 @@
#include "clangrefactoringengine.h" #include "clangrefactoringengine.h"
#include "clangeditordocumentprocessor.h" #include "clangeditordocumentprocessor.h"
#include "clangmodelmanagersupport.h"
#include <cpptools/cppmodelmanager.h>
#include <languageclient/client.h>
#include <languageclient/languageclientsymbolsupport.h>
#include <projectexplorer/session.h>
#include <utils/textutils.h> #include <utils/textutils.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
@@ -80,5 +86,27 @@ void RefactoringEngine::startLocalRenaming(const CppTools::CursorInEditor &data,
m_watcher->setFuture(cursorFuture); 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 Internal
} // namespace ClangCodeModel } // namespace ClangCodeModel

View File

@@ -46,7 +46,8 @@ public:
RenameCallback &&renameSymbolsCallback) override; RenameCallback &&renameSymbolsCallback) override;
void globalRename(const CppTools::CursorInEditor &, CppTools::UsagesCallback &&, void globalRename(const CppTools::CursorInEditor &, CppTools::UsagesCallback &&,
const QString &) override {} 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 &, void globalFollowSymbol(const CppTools::CursorInEditor &,
::Utils::ProcessLinkCallback &&, ::Utils::ProcessLinkCallback &&,
const CPlusPlus::Snapshot &, const CPlusPlus::Snapshot &,

View File

@@ -27,6 +27,7 @@
#include "clangeditordocumentprocessor.h" #include "clangeditordocumentprocessor.h"
#include "clangmodelmanagersupport.h" #include "clangmodelmanagersupport.h"
#include "clangprojectsettings.h"
#include <clangsupport/tokeninfocontainer.h> #include <clangsupport/tokeninfocontainer.h>
@@ -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) ProjectPart::Ptr projectPartForFile(const QString &filePath)
{ {
if (const auto parser = CppTools::BaseEditorDocumentParser::get(filePath)) if (const auto parser = CppTools::BaseEditorDocumentParser::get(filePath))
@@ -354,11 +347,15 @@ static QStringList projectPartArguments(const ProjectPart &projectPart)
static QJsonObject createFileObject(const FilePath &buildDir, static QJsonObject createFileObject(const FilePath &buildDir,
const QStringList &arguments, const QStringList &arguments,
const ProjectPart &projectPart, const ProjectPart &projectPart,
const ProjectFile &projFile) const ProjectFile &projFile,
CompilationDbPurpose purpose)
{ {
QJsonObject fileObject; QJsonObject fileObject;
fileObject["file"] = projFile.path; fileObject["file"] = projFile.path;
QJsonArray args = QJsonArray::fromStringList(arguments); QJsonArray args;
if (purpose == CompilationDbPurpose::Project) {
args = QJsonArray::fromStringList(arguments);
const ProjectFile::Kind kind = ProjectFile::classify(projFile.path); const ProjectFile::Kind kind = ProjectFile::classify(projFile.path);
if (projectPart.toolchainType == ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID if (projectPart.toolchainType == ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID
@@ -375,13 +372,20 @@ static QJsonObject createFileObject(const FilePath &buildDir,
for (const QString &langOptionPart : langOption) for (const QString &langOptionPart : langOption)
args.append(langOptionPart); args.append(langOptionPart);
} }
} else {
// 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)); args.append(QDir::toNativeSeparators(projFile.path));
fileObject["arguments"] = args; fileObject["arguments"] = args;
fileObject["directory"] = buildDir.toString(); fileObject["directory"] = buildDir.toString();
return fileObject; return fileObject;
} }
GenerateCompilationDbResult generateCompilationDB(CppTools::ProjectInfo projectInfo) GenerateCompilationDbResult generateCompilationDB(CppTools::ProjectInfo projectInfo,
CompilationDbPurpose purpose)
{ {
const FilePath buildDir = buildDirectory(*projectInfo.project()); const FilePath buildDir = buildDirectory(*projectInfo.project());
QTC_ASSERT(!buildDir.isEmpty(), return GenerateCompilationDbResult(QString(), QTC_ASSERT(!buildDir.isEmpty(), return GenerateCompilationDbResult(QString(),
@@ -400,9 +404,12 @@ GenerateCompilationDbResult generateCompilationDB(CppTools::ProjectInfo projectI
compileCommandsFile.write("["); compileCommandsFile.write("[");
for (ProjectPart::Ptr projectPart : projectInfo.projectParts()) { 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) { 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) if (compileCommandsFile.size() > 1)
compileCommandsFile.write(","); compileCommandsFile.write(",");
compileCommandsFile.write('\n' + QJsonDocument(json).toJson().trimmed()); compileCommandsFile.write('\n' + QJsonDocument(json).toJson().trimmed());
@@ -474,5 +481,142 @@ QString DiagnosticTextInfo::clazyCheckName(const QString &option)
return 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<Utils::Id, QStringList> createClangOptions(const CppTools::ProjectPart &projectPart,
const QString &filePath)
{
QPair<Utils::Id, QStringList> 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 Internal
} // namespace Clang } // namespace Clang

View File

@@ -30,6 +30,7 @@
#include <cpptools/projectpart.h> #include <cpptools/projectpart.h>
#include <cpptools/compileroptionsbuilder.h> #include <cpptools/compileroptionsbuilder.h>
#include <QPair>
#include <QTextCursor> #include <QTextCursor>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@@ -49,9 +50,8 @@ namespace Internal {
CppTools::CppEditorDocumentHandle *cppDocument(const QString &filePath); CppTools::CppEditorDocumentHandle *cppDocument(const QString &filePath);
void setLastSentDocumentRevision(const QString &filePath, uint revision); void setLastSentDocumentRevision(const QString &filePath, uint revision);
QStringList createClangOptions(const CppTools::ProjectPart &projectPart, QPair<Utils::Id, QStringList> createClangOptions(const CppTools::ProjectPart &projectPart,
CppTools::UseBuildSystemWarnings useBuildSystemWarnings, const QString &filePath);
CppTools::ProjectFile::Kind fileKind);
CppTools::ProjectPart::Ptr projectPartForFile(const QString &filePath); CppTools::ProjectPart::Ptr projectPartForFile(const QString &filePath);
CppTools::ProjectPart::Ptr projectPartForFileBasedOnProcessor(const QString &filePath); CppTools::ProjectPart::Ptr projectPartForFileBasedOnProcessor(const QString &filePath);
@@ -78,7 +78,9 @@ public:
QString error; QString error;
}; };
GenerateCompilationDbResult generateCompilationDB(CppTools::ProjectInfo projectInfo); enum CompilationDbPurpose { Project, CodeModel };
GenerateCompilationDbResult generateCompilationDB(CppTools::ProjectInfo projectInfo,
CompilationDbPurpose purpose);
class DiagnosticTextInfo class DiagnosticTextInfo
{ {

View File

@@ -531,6 +531,14 @@ QString ICore::clangExecutable(const QString &clangBinDirectory)
return clangBinary("clang", clangBinDirectory); return clangBinary("clang", clangBinDirectory);
} }
/*!
\internal
*/
QString ICore::clangdExecutable(const QString &clangBinDirectory)
{
return clangBinary("clangd", clangBinDirectory);
}
/*! /*!
\internal \internal
*/ */

View File

@@ -164,6 +164,7 @@ public:
static QString pluginPath(); static QString pluginPath();
static QString userPluginPath(); static QString userPluginPath();
static QString clangExecutable(const QString &clangBinDirectory); static QString clangExecutable(const QString &clangBinDirectory);
static QString clangdExecutable(const QString &clangBinDirectory);
static QString clangTidyExecutable(const QString &clangBinDirectory); static QString clangTidyExecutable(const QString &clangBinDirectory);
static QString clazyStandaloneExecutable(const QString &clangBinDirectory); static QString clazyStandaloneExecutable(const QString &clangBinDirectory);
static QString clangIncludeDirectory(const QString &clangVersion, static QString clangIncludeDirectory(const QString &clangVersion,

View File

@@ -472,7 +472,8 @@ void CppEditorWidget::findUsages()
void CppEditorWidget::findUsages(QTextCursor cursor) void CppEditorWidget::findUsages(QTextCursor cursor)
{ {
// 'this' in cursorInEditor is never used (and must never be used) asynchronously. // '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> cppEditorWidget = this; QPointer<CppEditorWidget> cppEditorWidget = this;
d->m_modelManager->findUsages(cursorInEditor, d->m_modelManager->findUsages(cursorInEditor,
[=](const CppTools::Usages &usages) { [=](const CppTools::Usages &usages) {

View File

@@ -2068,7 +2068,7 @@ void AddIncludeForUndefinedIdentifier::match(const CppQuickFixInterface &interfa
const Snapshot forwardHeaders = forwardingHeaders(interface); const Snapshot forwardHeaders = forwardingHeaders(interface);
foreach (const Core::LocatorFilterEntry &entry, matches) { foreach (const Core::LocatorFilterEntry &entry, matches) {
IndexItem::Ptr info = entry.internalData.value<IndexItem::Ptr>(); IndexItem::Ptr info = entry.internalData.value<IndexItem::Ptr>();
if (info->symbolName() != className) if (!info || info->symbolName() != className)
continue; continue;
indexItems << info; indexItems << info;

View File

@@ -32,7 +32,7 @@
namespace CppTools { namespace CppTools {
class CppLocatorData; class CppLocatorData;
class CppClassesFilter : public Internal::CppLocatorFilter class CPPTOOLS_EXPORT CppClassesFilter : public CppLocatorFilter
{ {
Q_OBJECT Q_OBJECT

View File

@@ -35,6 +35,7 @@
#include <QSettings> #include <QSettings>
using namespace CppTools; using namespace CppTools;
using namespace Utils;
static Utils::Id initialClangDiagnosticConfigId() static Utils::Id initialClangDiagnosticConfigId()
{ return Constants::CPP_CLANG_DIAG_CONFIG_BUILDSYSTEM; } { return Constants::CPP_CLANG_DIAG_CONFIG_BUILDSYSTEM; }
@@ -60,6 +61,17 @@ static QString skipIndexingBigFilesKey()
static QString indexerFileSizeLimitKey() static QString indexerFileSizeLimitKey()
{ return QLatin1String(Constants::CPPTOOLS_INDEXER_FILE_SIZE_LIMIT); } { 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) static Utils::Id clangDiagnosticConfigIdFromSettings(QSettings *s)
{ {
QTC_ASSERT(s->group() == QLatin1String(Constants::CPPTOOLS_SETTINGSGROUP), return Utils::Id()); 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); const QVariant indexerFileSizeLimit = s->value(indexerFileSizeLimitKey(), 5);
setIndexerFileSizeLimitInMb(indexerFileSizeLimit.toInt()); setIndexerFileSizeLimitInMb(indexerFileSizeLimit.toInt());
setUseClangd(s->value(useClangdKey(), false).toBool());
setClangdFilePath(FilePath::fromString(s->value(clangdPathKey()).toString()));
s->endGroup(); s->endGroup();
if (write) if (write)
@@ -183,6 +198,8 @@ void CppCodeModelSettings::toSettings(QSettings *s)
s->setValue(interpretAmbiguousHeadersAsCHeadersKey(), interpretAmbigiousHeadersAsCHeaders()); s->setValue(interpretAmbiguousHeadersAsCHeadersKey(), interpretAmbigiousHeadersAsCHeaders());
s->setValue(skipIndexingBigFilesKey(), skipIndexingBigFiles()); s->setValue(skipIndexingBigFilesKey(), skipIndexingBigFiles());
s->setValue(indexerFileSizeLimitKey(), indexerFileSizeLimitInMb()); s->setValue(indexerFileSizeLimitKey(), indexerFileSizeLimitInMb());
s->setValue(useClangdKey(), useClangd());
s->setValue(clangdPathKey(), m_clangdFilePath.toString());
s->endGroup(); s->endGroup();
@@ -282,3 +299,15 @@ void CppCodeModelSettings::setEnableLowerClazyLevels(bool yesno)
{ {
m_enableLowerClazyLevels = 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();
}

View File

@@ -29,6 +29,8 @@
#include "clangdiagnosticconfigsmodel.h" #include "clangdiagnosticconfigsmodel.h"
#include <utils/fileutils.h>
#include <QObject> #include <QObject>
#include <QStringList> #include <QStringList>
@@ -76,6 +78,13 @@ public:
int indexerFileSizeLimitInMb() const; int indexerFileSizeLimitInMb() const;
void setIndexerFileSizeLimitInMb(int sizeInMB); 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: signals:
void clangDiagnosticConfigsInvalidated(const QVector<Utils::Id> &configId); void clangDiagnosticConfigsInvalidated(const QVector<Utils::Id> &configId);
void changed(); void changed();
@@ -88,6 +97,8 @@ private:
ClangDiagnosticConfigs m_clangCustomDiagnosticConfigs; ClangDiagnosticConfigs m_clangCustomDiagnosticConfigs;
Utils::Id m_clangDiagnosticConfigId; Utils::Id m_clangDiagnosticConfigId;
bool m_enableLowerClazyLevels = true; // For UI behavior only bool m_enableLowerClazyLevels = true; // For UI behavior only
Utils::FilePath m_clangdFilePath;
bool m_useClangd = false;
}; };
} // namespace CppTools } // namespace CppTools

View File

@@ -100,6 +100,9 @@ void CppCodeModelSettingsWidget::setupClangCodeModelWidgets()
const bool isClangActive = CppModelManager::instance()->isClangCodeModelActive(); const bool isClangActive = CppModelManager::instance()->isClangCodeModelActive();
m_ui->clangCodeModelIsDisabledHint->setVisible(!isClangActive); m_ui->clangCodeModelIsDisabledHint->setVisible(!isClangActive);
m_ui->clangCodeModelIsEnabledHint->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) { for (int i = 0; i < m_ui->clangDiagnosticConfigsSelectionWidget->layout()->count(); ++i) {
QWidget *widget = m_ui->clangDiagnosticConfigsSelectionWidget->layout()->itemAt(i)->widget(); QWidget *widget = m_ui->clangDiagnosticConfigsSelectionWidget->layout()->itemAt(i)->widget();
if (widget) if (widget)
@@ -117,6 +120,16 @@ void CppCodeModelSettingsWidget::setupGeneralWidgets()
const bool ignorePch = m_settings->pchUsage() == CppCodeModelSettings::PchUse_None; const bool ignorePch = m_settings->pchUsage() == CppCodeModelSettings::PchUse_None;
m_ui->ignorePCHCheckBox->setChecked(ignorePch); 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 bool CppCodeModelSettingsWidget::applyClangCodeModelWidgetsToSettings() const
@@ -163,6 +176,16 @@ bool CppCodeModelSettingsWidget::applyGeneralWidgetsToSettings() const
m_settings->setIndexerFileSizeLimitInMb(newFileSizeLimit); m_settings->setIndexerFileSizeLimitInMb(newFileSizeLimit);
settingsChanged = true; 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 newIgnorePch = m_ui->ignorePCHCheckBox->isChecked();
const bool previousIgnorePch = m_settings->pchUsage() == CppCodeModelSettings::PchUse_None; const bool previousIgnorePch = m_settings->pchUsage() == CppCodeModelSettings::PchUse_None;

View File

@@ -6,7 +6,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>647</width> <width>697</width>
<height>440</height> <height>440</height>
</rect> </rect>
</property> </property>
@@ -80,6 +80,33 @@
</item> </item>
</layout> </layout>
</item> </item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QCheckBox" name="clangdCheckBox">
<property name="text">
<string>Use clangd (EXPERIMENTAL)</string>
</property>
</widget>
</item>
<item>
<widget class="Utils::PathChooser" name="clangdChooser"/>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>
@@ -147,6 +174,11 @@
<extends>QWidget</extends> <extends>QWidget</extends>
<header>cpptools/clangdiagnosticconfigsselectionwidget.h</header> <header>cpptools/clangdiagnosticconfigsselectionwidget.h</header>
</customwidget> </customwidget>
<customwidget>
<class>Utils::PathChooser</class>
<extends>QLineEdit</extends>
<header location="global">utils/pathchooser.h</header>
</customwidget>
</customwidgets> </customwidgets>
<resources/> <resources/>
<connections/> <connections/>

View File

@@ -30,7 +30,6 @@
#include <utils/fileutils.h> #include <utils/fileutils.h>
using namespace CppTools; using namespace CppTools;
using namespace CppTools::Internal;
CppFunctionsFilter::CppFunctionsFilter(CppLocatorData *locatorData) CppFunctionsFilter::CppFunctionsFilter(CppLocatorData *locatorData)
: CppLocatorFilter(locatorData) : CppLocatorFilter(locatorData)

View File

@@ -30,9 +30,8 @@
namespace CppTools { namespace CppTools {
namespace Internal {
class CppFunctionsFilter : public CppLocatorFilter class CPPTOOLS_EXPORT CppFunctionsFilter : public CppLocatorFilter
{ {
Q_OBJECT Q_OBJECT
@@ -45,5 +44,4 @@ protected:
Core::LocatorFilterEntry filterEntryFromIndexItem(IndexItem::Ptr info) override; Core::LocatorFilterEntry filterEntryFromIndexItem(IndexItem::Ptr info) override;
}; };
} // namespace Internal
} // namespace CppTools } // namespace CppTools

View File

@@ -35,7 +35,7 @@
namespace CppTools { namespace CppTools {
class CppLocatorData : public QObject class CPPTOOLS_EXPORT CppLocatorData : public QObject
{ {
Q_OBJECT Q_OBJECT

View File

@@ -35,8 +35,7 @@
#include <algorithm> #include <algorithm>
#include <numeric> #include <numeric>
using namespace CppTools; namespace CppTools {
using namespace CppTools::Internal;
CppLocatorFilter::CppLocatorFilter(CppLocatorData *locatorData) CppLocatorFilter::CppLocatorFilter(CppLocatorData *locatorData)
: m_data(locatorData) : m_data(locatorData)
@@ -145,3 +144,5 @@ void CppLocatorFilter::accept(Core::LocatorFilterEntry selection,
IndexItem::Ptr info = qvariant_cast<IndexItem::Ptr>(selection.internalData); IndexItem::Ptr info = qvariant_cast<IndexItem::Ptr>(selection.internalData);
Core::EditorManager::openEditorAt(info->fileName(), info->line(), info->column()); Core::EditorManager::openEditorAt(info->fileName(), info->line(), info->column());
} }
} // namespace CppTools

View File

@@ -25,15 +25,15 @@
#pragma once #pragma once
#include "cpptools_global.h"
#include "cpplocatordata.h" #include "cpplocatordata.h"
#include "searchsymbols.h" #include "searchsymbols.h"
#include <coreplugin/locator/ilocatorfilter.h> #include <coreplugin/locator/ilocatorfilter.h>
namespace CppTools { namespace CppTools {
namespace Internal {
class CppLocatorFilter : public Core::ILocatorFilter class CPPTOOLS_EXPORT CppLocatorFilter : public Core::ILocatorFilter
{ {
Q_OBJECT Q_OBJECT
@@ -54,5 +54,4 @@ protected:
CppLocatorData *m_data = nullptr; CppLocatorData *m_data = nullptr;
}; };
} // namespace Internal
} // namespace CppTools } // namespace CppTools

View File

@@ -328,7 +328,7 @@ void CppModelManager::globalRename(const CursorInEditor &data, UsagesCallback &&
void CppModelManager::findUsages(const CppTools::CursorInEditor &data, void CppModelManager::findUsages(const CppTools::CursorInEditor &data,
UsagesCallback &&showUsagesCallback) const UsagesCallback &&showUsagesCallback) const
{ {
RefactoringEngineInterface *engine = getRefactoringEngine(d->m_refactoringEngines); RefactoringEngineInterface *engine = getRefactoringEngine(d->m_refactoringEngines, false);
QTC_ASSERT(engine, return;); QTC_ASSERT(engine, return;);
engine->findUsages(data, std::move(showUsagesCallback)); engine->findUsages(data, std::move(showUsagesCallback));
} }
@@ -466,6 +466,11 @@ void CppModelManager::removeRefactoringEngine(RefactoringEngineType type)
instance()->d->m_refactoringEngines.remove(type); instance()->d->m_refactoringEngines.remove(type);
} }
RefactoringEngineInterface *CppModelManager::builtinRefactoringEngine()
{
return instance()->d->m_refactoringEngines.value(RefactoringEngineType::BuiltIn);
}
template<class FilterClass> template<class FilterClass>
static void setFilter(std::unique_ptr<FilterClass> &filter, static void setFilter(std::unique_ptr<FilterClass> &filter,
std::unique_ptr<FilterClass> &&newFilter) std::unique_ptr<FilterClass> &&newFilter)
@@ -917,6 +922,11 @@ QByteArray CppModelManager::codeModelConfiguration() const
return QByteArray::fromRawData(pp_configuration, qstrlen(pp_configuration)); return QByteArray::fromRawData(pp_configuration, qstrlen(pp_configuration));
} }
CppLocatorData *CppModelManager::locatorData() const
{
return &d->m_locatorData;
}
static QSet<QString> tooBigFilesRemoved(const QSet<QString> &files, int fileSizeLimitInMb) static QSet<QString> tooBigFilesRemoved(const QSet<QString> &files, int fileSizeLimitInMb)
{ {
if (fileSizeLimitInMb <= 0) if (fileSizeLimitInMb <= 0)

View File

@@ -60,6 +60,7 @@ class BaseEditorDocumentProcessor;
class CppCompletionAssistProvider; class CppCompletionAssistProvider;
class CppEditorDocumentHandle; class CppEditorDocumentHandle;
class CppIndexingSupport; class CppIndexingSupport;
class CppLocatorData;
class ModelManagerSupportProvider; class ModelManagerSupportProvider;
class FollowSymbolInterface; class FollowSymbolInterface;
class SymbolFinder; class SymbolFinder;
@@ -111,6 +112,7 @@ public:
void updateCppEditorDocuments(bool projectsUpdated = false) const; void updateCppEditorDocuments(bool projectsUpdated = false) const;
WorkingCopy workingCopy() const; WorkingCopy workingCopy() const;
QByteArray codeModelConfiguration() const; QByteArray codeModelConfiguration() const;
CppLocatorData *locatorData() const;
QList<ProjectInfo> projectInfos() const; QList<ProjectInfo> projectInfos() const;
ProjectInfo projectInfo(ProjectExplorer::Project *project) const; ProjectInfo projectInfo(ProjectExplorer::Project *project) const;
@@ -215,6 +217,7 @@ public:
static void addRefactoringEngine(RefactoringEngineType type, static void addRefactoringEngine(RefactoringEngineType type,
RefactoringEngineInterface *refactoringEngine); RefactoringEngineInterface *refactoringEngine);
static void removeRefactoringEngine(RefactoringEngineType type); static void removeRefactoringEngine(RefactoringEngineType type);
static RefactoringEngineInterface *builtinRefactoringEngine();
void setLocatorFilter(std::unique_ptr<Core::ILocatorFilter> &&filter); void setLocatorFilter(std::unique_ptr<Core::ILocatorFilter> &&filter);
void setClassesFilter(std::unique_ptr<Core::ILocatorFilter> &&filter); void setClassesFilter(std::unique_ptr<Core::ILocatorFilter> &&filter);

View File

@@ -31,24 +31,30 @@
#include <QTextCursor> #include <QTextCursor>
namespace TextEditor { class TextDocument; }
namespace CppTools { namespace CppTools {
class CursorInEditor class CursorInEditor
{ {
public: public:
CursorInEditor(const QTextCursor &cursor, const Utils::FilePath &filePath, CursorInEditor(const QTextCursor &cursor, const Utils::FilePath &filePath,
CppEditorWidgetInterface *editorWidget = nullptr) CppEditorWidgetInterface *editorWidget = nullptr,
TextEditor::TextDocument *textDocument = nullptr)
: m_cursor(cursor) : m_cursor(cursor)
, m_filePath(filePath) , m_filePath(filePath)
, m_editorWidget(editorWidget) , m_editorWidget(editorWidget)
, m_textDocument(textDocument)
{} {}
CppEditorWidgetInterface *editorWidget() const { return m_editorWidget; } CppEditorWidgetInterface *editorWidget() const { return m_editorWidget; }
TextEditor::TextDocument *textDocument() const { return m_textDocument; }
const QTextCursor &cursor() const { return m_cursor; } const QTextCursor &cursor() const { return m_cursor; }
const Utils::FilePath &filePath() const { return m_filePath; } const Utils::FilePath &filePath() const { return m_filePath; }
private: private:
QTextCursor m_cursor; QTextCursor m_cursor;
Utils::FilePath m_filePath; Utils::FilePath m_filePath;
CppEditorWidgetInterface *m_editorWidget = nullptr; CppEditorWidgetInterface *m_editorWidget = nullptr;
TextEditor::TextDocument * const m_textDocument;
}; };
} // namespace CppTools } // namespace CppTools

View File

@@ -292,7 +292,7 @@ void Client::initialize()
QTC_ASSERT(m_state == Uninitialized, return); QTC_ASSERT(m_state == Uninitialized, return);
qCDebug(LOGLSPCLIENT) << "initializing language server " << m_displayName; qCDebug(LOGLSPCLIENT) << "initializing language server " << m_displayName;
InitializeParams params; InitializeParams params;
params.setCapabilities(generateClientCapabilities()); params.setCapabilities(m_clientCapabilities);
params.setInitializationOptions(m_initializationOptions); params.setInitializationOptions(m_initializationOptions);
if (m_project) { if (m_project) {
params.setRootUri(DocumentUri::fromFilePath(m_project->projectDirectory())); params.setRootUri(DocumentUri::fromFilePath(m_project->projectDirectory()));
@@ -333,6 +333,16 @@ Client::State Client::state() const
return m_state; return m_state;
} }
ClientCapabilities Client::defaultClientCapabilities()
{
return generateClientCapabilities();
}
void Client::setClientCapabilities(const LanguageServerProtocol::ClientCapabilities &caps)
{
m_clientCapabilities = caps;
}
void Client::openDocument(TextEditor::TextDocument *document) void Client::openDocument(TextEditor::TextDocument *document)
{ {
using namespace TextEditor; using namespace TextEditor;
@@ -532,6 +542,9 @@ void Client::requestDocumentHighlights(TextEditor::TextEditorWidget *widget)
void Client::activateDocument(TextEditor::TextDocument *document) void Client::activateDocument(TextEditor::TextDocument *document)
{ {
if (!m_documentActionsEnabled)
return;
auto uri = DocumentUri::fromFilePath(document->filePath()); auto uri = DocumentUri::fromFilePath(document->filePath());
m_diagnosticManager.showDiagnostics(uri); m_diagnosticManager.showDiagnostics(uri);
SemanticHighligtingSupport::applyHighlight(document, m_highlights.value(uri), capabilities()); SemanticHighligtingSupport::applyHighlight(document, m_highlights.value(uri), capabilities());
@@ -558,6 +571,9 @@ void Client::activateDocument(TextEditor::TextDocument *document)
void Client::deactivateDocument(TextEditor::TextDocument *document) void Client::deactivateDocument(TextEditor::TextDocument *document)
{ {
if (!m_documentActionsEnabled)
return;
m_diagnosticManager.hideDiagnostics(document); m_diagnosticManager.hideDiagnostics(document);
resetAssistProviders(document); resetAssistProviders(document);
document->setFormatter(nullptr); document->setFormatter(nullptr);
@@ -1241,6 +1257,9 @@ void Client::handleMethod(const QString &method, const MessageId &id, const ICon
void Client::handleDiagnostics(const PublishDiagnosticsParams &params) void Client::handleDiagnostics(const PublishDiagnosticsParams &params)
{ {
if (!m_documentActionsEnabled)
return;
const DocumentUri &uri = params.uri(); const DocumentUri &uri = params.uri();
const QList<Diagnostic> &diagnostics = params.diagnostics(); const QList<Diagnostic> &diagnostics = params.diagnostics();
@@ -1253,6 +1272,9 @@ void Client::handleDiagnostics(const PublishDiagnosticsParams &params)
void Client::handleSemanticHighlight(const SemanticHighlightingParams &params) void Client::handleSemanticHighlight(const SemanticHighlightingParams &params)
{ {
if (!m_documentActionsEnabled)
return;
DocumentUri uri; DocumentUri uri;
LanguageClientValue<int> version; LanguageClientValue<int> version;
auto textDocument = params.textDocument(); auto textDocument = params.textDocument();
@@ -1283,6 +1305,9 @@ void Client::handleSemanticHighlight(const SemanticHighlightingParams &params)
void Client::rehighlight() void Client::rehighlight()
{ {
if (!m_documentActionsEnabled)
return;
using namespace TextEditor; using namespace TextEditor;
for (auto it = m_highlights.begin(), end = m_highlights.end(); it != end; ++it) { for (auto it = m_highlights.begin(), end = m_highlights.end(); it != end; ++it) {
if (TextDocument *doc = TextDocument::textDocumentForFilePath(it.key().toFilePath())) { if (TextDocument *doc = TextDocument::textDocumentForFilePath(it.key().toFilePath())) {

View File

@@ -113,11 +113,17 @@ public:
bool reachable() const { return m_state == Initialized; } bool reachable() const { return m_state == Initialized; }
// capabilities // capabilities
static LanguageServerProtocol::ClientCapabilities defaultClientCapabilities();
void setClientCapabilities(const LanguageServerProtocol::ClientCapabilities &caps);
const LanguageServerProtocol::ServerCapabilities &capabilities() const; const LanguageServerProtocol::ServerCapabilities &capabilities() const;
const DynamicCapabilities &dynamicCapabilities() const; const DynamicCapabilities &dynamicCapabilities() const;
void registerCapabilities(const QList<LanguageServerProtocol::Registration> &registrations); void registerCapabilities(const QList<LanguageServerProtocol::Registration> &registrations);
void unregisterCapabilities(const QList<LanguageServerProtocol::Unregistration> &unregistrations); void unregisterCapabilities(const QList<LanguageServerProtocol::Unregistration> &unregistrations);
void setLocatorsEnabled(bool enabled) { m_locatorsEnabled = enabled; }
bool locatorsEnabled() const { return m_locatorsEnabled; }
void setDocumentActionsEnabled(bool enabled) { m_documentActionsEnabled = enabled; }
// document synchronization // document synchronization
void setSupportedLanguage(const LanguageFilter &filter); void setSupportedLanguage(const LanguageFilter &filter);
void setActivateDocumentAutomatically(bool enabled); void setActivateDocumentAutomatically(bool enabled);
@@ -226,6 +232,7 @@ private:
QMap<TextEditor::TextEditorWidget *, QTimer *> m_documentHighlightsTimer; QMap<TextEditor::TextEditorWidget *, QTimer *> m_documentHighlightsTimer;
QTimer m_documentUpdateTimer; QTimer m_documentUpdateTimer;
Utils::Id m_id; Utils::Id m_id;
LanguageServerProtocol::ClientCapabilities m_clientCapabilities = defaultClientCapabilities();
LanguageServerProtocol::ServerCapabilities m_serverCapabilities; LanguageServerProtocol::ServerCapabilities m_serverCapabilities;
DynamicCapabilities m_dynamicCapabilities; DynamicCapabilities m_dynamicCapabilities;
struct AssistProviders struct AssistProviders
@@ -250,6 +257,8 @@ private:
ProgressManager m_progressManager; ProgressManager m_progressManager;
bool m_activateDocAutomatically = false; bool m_activateDocAutomatically = false;
SemanticTokenSupport m_tokentSupport; SemanticTokenSupport m_tokentSupport;
bool m_locatorsEnabled = true;
bool m_documentActionsEnabled = true;
}; };
} // namespace LanguageClient } // namespace LanguageClient

View File

@@ -57,4 +57,6 @@ QtcPlugin {
"semantichighlightsupport.cpp", "semantichighlightsupport.cpp",
"semantichighlightsupport.h", "semantichighlightsupport.h",
] ]
Export { Depends { name: "LanguageServerProtocol" } }
} }

View File

@@ -40,6 +40,7 @@
#include <texteditor/textdocument.h> #include <texteditor/textdocument.h>
#include <texteditor/texteditor.h> #include <texteditor/texteditor.h>
#include <texteditor/textmark.h> #include <texteditor/textmark.h>
#include <utils/algorithm.h>
#include <utils/executeondestruction.h> #include <utils/executeondestruction.h>
#include <utils/mimetypes/mimedatabase.h> #include <utils/mimetypes/mimedatabase.h>
#include <utils/theme/theme.h> #include <utils/theme/theme.h>
@@ -375,6 +376,14 @@ Client *LanguageClientManager::clientForUri(const DocumentUri &uri)
return clientForFilePath(uri.toFilePath()); return clientForFilePath(uri.toFilePath());
} }
const QList<Client *> 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) void LanguageClientManager::openDocumentWithClient(TextEditor::TextDocument *document, Client *client)
{ {
Client *currentClient = clientForDocument(document); Client *currentClient = clientForDocument(document);

View File

@@ -84,6 +84,7 @@ public:
static Client *clientForDocument(TextEditor::TextDocument *document); static Client *clientForDocument(TextEditor::TextDocument *document);
static Client *clientForFilePath(const Utils::FilePath &filePath); static Client *clientForFilePath(const Utils::FilePath &filePath);
static Client *clientForUri(const LanguageServerProtocol::DocumentUri &uri); static Client *clientForUri(const LanguageServerProtocol::DocumentUri &uri);
static const QList<Client *> clientsForProject(const ProjectExplorer::Project *project);
/// ///
/// \brief openDocumentWithClient /// \brief openDocumentWithClient

View File

@@ -25,6 +25,8 @@
#pragma once #pragma once
#include "languageclient_global.h"
#include <texteditor/textdocument.h> #include <texteditor/textdocument.h>
#include <languageserverprotocol/languagefeatures.h> #include <languageserverprotocol/languagefeatures.h>
@@ -38,7 +40,7 @@ namespace LanguageClient {
class Client; class Client;
class SymbolSupport class LANGUAGECLIENT_EXPORT SymbolSupport
{ {
Q_DECLARE_TR_FUNCTIONS(SymbolSupport) Q_DECLARE_TR_FUNCTIONS(SymbolSupport)
public: public:

View File

@@ -214,6 +214,18 @@ WorkspaceLocatorFilter::WorkspaceLocatorFilter(const QVector<SymbolKind> &filter
} }
void WorkspaceLocatorFilter::prepareSearch(const QString &entry) void WorkspaceLocatorFilter::prepareSearch(const QString &entry)
{
prepareSearch(entry, LanguageClientManager::clients(), false);
}
void WorkspaceLocatorFilter::prepareSearch(const QString &entry, const QVector<Client *> &clients)
{
prepareSearch(entry, clients, true);
}
void WorkspaceLocatorFilter::prepareSearch(const QString &entry,
const QVector<Client *> &clients,
bool force)
{ {
m_pendingRequests.clear(); m_pendingRequests.clear();
m_results.clear(); m_results.clear();
@@ -222,7 +234,11 @@ void WorkspaceLocatorFilter::prepareSearch(const QString &entry)
params.setQuery(entry); params.setQuery(entry);
QMutexLocker locker(&m_mutex); 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<Utils::variant<bool, WorkDoneProgressOptions>> capability Utils::optional<Utils::variant<bool, WorkDoneProgressOptions>> capability
= client->capabilities().workspaceSymbolProvider(); = client->capabilities().workspaceSymbolProvider();
if (!capability.has_value()) if (!capability.has_value())

View File

@@ -74,13 +74,16 @@ private:
Utils::optional<LanguageServerProtocol::DocumentSymbolsResult> m_currentSymbols; Utils::optional<LanguageServerProtocol::DocumentSymbolsResult> m_currentSymbols;
}; };
class WorkspaceLocatorFilter : public Core::ILocatorFilter class LANGUAGECLIENT_EXPORT WorkspaceLocatorFilter : public Core::ILocatorFilter
{ {
Q_OBJECT Q_OBJECT
public: public:
WorkspaceLocatorFilter(); WorkspaceLocatorFilter();
/// request workspace symbols for all clients with enabled locator
void prepareSearch(const QString &entry) override; void prepareSearch(const QString &entry) override;
/// force request workspace symbols for all given clients
void prepareSearch(const QString &entry, const QVector<Client *> &clients);
QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future, QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future,
const QString &entry) override; const QString &entry) override;
void accept(Core::LocatorFilterEntry selection, void accept(Core::LocatorFilterEntry selection,
@@ -95,6 +98,7 @@ protected:
explicit WorkspaceLocatorFilter(const QVector<LanguageServerProtocol::SymbolKind> &filter); explicit WorkspaceLocatorFilter(const QVector<LanguageServerProtocol::SymbolKind> &filter);
private: private:
void prepareSearch(const QString &entry, const QVector<Client *> &clients, bool force);
void handleResponse(Client *client, void handleResponse(Client *client,
const LanguageServerProtocol::WorkspaceSymbolRequest::Response &response); const LanguageServerProtocol::WorkspaceSymbolRequest::Response &response);
@@ -104,13 +108,13 @@ private:
QVector<LanguageServerProtocol::SymbolKind> m_filterKinds; QVector<LanguageServerProtocol::SymbolKind> m_filterKinds;
}; };
class WorkspaceClassLocatorFilter : public WorkspaceLocatorFilter class LANGUAGECLIENT_EXPORT WorkspaceClassLocatorFilter : public WorkspaceLocatorFilter
{ {
public: public:
WorkspaceClassLocatorFilter(); WorkspaceClassLocatorFilter();
}; };
class WorkspaceMethodLocatorFilter : public WorkspaceLocatorFilter class LANGUAGECLIENT_EXPORT WorkspaceMethodLocatorFilter : public WorkspaceLocatorFilter
{ {
public: public:
WorkspaceMethodLocatorFilter(); WorkspaceMethodLocatorFilter();