ClangCodeModel: Provide outline via clangd

Note that we used to encode the information about symbol visibility and
static-ness in the icons, which we can't do anymore, because clangd does
not provide this information.
On the upside, this change likely fixes a ton of bugs, as our own outline
was rather "quirky".

Change-Id: I099f11ec4e3c6f52cd461fb43080bbdde3bed5e5
Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
Christian Kandeler
2021-06-08 14:28:25 +02:00
parent f4a3310059
commit c0a44be27b
12 changed files with 97 additions and 18 deletions

View File

@@ -40,6 +40,8 @@
#include <QUrl> #include <QUrl>
#include <QList> #include <QList>
#include <functional>
namespace LanguageServerProtocol { namespace LanguageServerProtocol {
class LANGUAGESERVERPROTOCOL_EXPORT DocumentUri : public QUrl class LANGUAGESERVERPROTOCOL_EXPORT DocumentUri : public QUrl
@@ -560,6 +562,7 @@ enum class SymbolKind {
TypeParameter = 26, TypeParameter = 26,
LastSymbolKind = TypeParameter, LastSymbolKind = TypeParameter,
}; };
using SymbolStringifier = std::function<QString(SymbolKind, const QString &, const QString &)>;
namespace CompletionItemKind { namespace CompletionItemKind {
enum Kind { enum Kind {

View File

@@ -732,6 +732,31 @@ ClangdClient::ClangdClient(Project *project, const Utils::FilePath &jsonDbDir)
const auto hideDiagsHandler = []{ ClangDiagnosticManager::clearTaskHubIssues(); }; const auto hideDiagsHandler = []{ ClangDiagnosticManager::clearTaskHubIssues(); };
setDiagnosticsHandlers(textMarkCreator, hideDiagsHandler); setDiagnosticsHandlers(textMarkCreator, hideDiagsHandler);
static const auto symbolStringifier = [](SymbolKind kind, const QString &name,
const QString &detail) -> QString
{
switch (kind) {
case LanguageServerProtocol::SymbolKind::Constructor:
return name + detail;
case LanguageServerProtocol::SymbolKind::Method:
case LanguageServerProtocol::SymbolKind::Function: {
const int parenOffset = detail.indexOf(" (");
if (parenOffset == -1)
return name;
return name + detail.mid(parenOffset + 1) + " -> " + detail.mid(0, parenOffset);
}
case LanguageServerProtocol::SymbolKind::Variable:
case LanguageServerProtocol::SymbolKind::Field:
case LanguageServerProtocol::SymbolKind::Constant:
if (detail.isEmpty())
return name;
return name + " -> " + detail;
default:
return name;
}
};
setSymbolStringifier(symbolStringifier);
hoverHandler()->setHelpItemProvider([this](const HoverRequest::Response &response, hoverHandler()->setHelpItemProvider([this](const HoverRequest::Response &response,
const DocumentUri &uri) { const DocumentUri &uri) {
gatherHelpItemForTooltip(response, uri); gatherHelpItemForTooltip(response, uri);

View File

@@ -171,6 +171,11 @@ std::unique_ptr<CppTools::AbstractOverviewModel> ClangModelManagerSupport::creat
return std::make_unique<OverviewModel>(); return std::make_unique<OverviewModel>();
} }
bool ClangModelManagerSupport::supportsOutline(const TextEditor::TextDocument *document) const
{
return !clientForFile(document->filePath());
}
CppTools::BaseEditorDocumentProcessor *ClangModelManagerSupport::createEditorDocumentProcessor( CppTools::BaseEditorDocumentProcessor *ClangModelManagerSupport::createEditorDocumentProcessor(
TextEditor::TextDocument *baseTextDocument) TextEditor::TextDocument *baseTextDocument)
{ {
@@ -344,7 +349,7 @@ void ClangModelManagerSupport::updateLanguageClient(ProjectExplorer::Project *pr
} }
ClangdClient *ClangModelManagerSupport::clientForProject( ClangdClient *ClangModelManagerSupport::clientForProject(
const ProjectExplorer::Project *project) const ProjectExplorer::Project *project) const
{ {
const QList<Client *> clients = Utils::filtered( const QList<Client *> clients = Utils::filtered(
LanguageClientManager::clientsForProject(project), LanguageClientManager::clientsForProject(project),
@@ -357,7 +362,7 @@ ClangdClient *ClangModelManagerSupport::clientForProject(
return clients.empty() ? nullptr : qobject_cast<ClangdClient *>(clients.first()); return clients.empty() ? nullptr : qobject_cast<ClangdClient *>(clients.first());
} }
ClangdClient *ClangModelManagerSupport::clientForFile(const Utils::FilePath &file) ClangdClient *ClangModelManagerSupport::clientForFile(const Utils::FilePath &file) const
{ {
return clientForProject(ProjectExplorer::SessionManager::projectForFile(file)); return clientForProject(ProjectExplorer::SessionManager::projectForFile(file));
} }

View File

@@ -72,6 +72,7 @@ public:
CppTools::FollowSymbolInterface &followSymbolInterface() override; CppTools::FollowSymbolInterface &followSymbolInterface() override;
CppTools::RefactoringEngineInterface &refactoringEngineInterface() override; CppTools::RefactoringEngineInterface &refactoringEngineInterface() override;
std::unique_ptr<CppTools::AbstractOverviewModel> createOverviewModel() override; std::unique_ptr<CppTools::AbstractOverviewModel> createOverviewModel() override;
bool supportsOutline(const TextEditor::TextDocument *document) const override;
BackendCommunicator &communicator(); BackendCommunicator &communicator();
QString dummyUiHeaderOnDiskDirPath() const; QString dummyUiHeaderOnDiskDirPath() const;
@@ -79,8 +80,8 @@ public:
ClangProjectSettings &projectSettings(ProjectExplorer::Project *project) const; ClangProjectSettings &projectSettings(ProjectExplorer::Project *project) const;
ClangdClient *clientForProject(const ProjectExplorer::Project *project); ClangdClient *clientForProject(const ProjectExplorer::Project *project) const;
ClangdClient *clientForFile(const Utils::FilePath &file); ClangdClient *clientForFile(const Utils::FilePath &file) const;
static ClangModelManagerSupport *instance(); static ClangModelManagerSupport *instance();

View File

@@ -118,7 +118,7 @@ public:
QPointer<CppModelManager> m_modelManager; QPointer<CppModelManager> m_modelManager;
CppEditorDocument *m_cppEditorDocument; CppEditorDocument *m_cppEditorDocument;
CppEditorOutline *m_cppEditorOutline; CppEditorOutline *m_cppEditorOutline = nullptr;
QTimer m_updateFunctionDeclDefLinkTimer; QTimer m_updateFunctionDeclDefLinkTimer;
SemanticInfo m_lastSemanticInfo; SemanticInfo m_lastSemanticInfo;
@@ -139,7 +139,6 @@ public:
CppEditorWidgetPrivate::CppEditorWidgetPrivate(CppEditorWidget *q) CppEditorWidgetPrivate::CppEditorWidgetPrivate(CppEditorWidget *q)
: m_modelManager(CppModelManager::instance()) : m_modelManager(CppModelManager::instance())
, m_cppEditorDocument(qobject_cast<CppEditorDocument *>(q->textDocument())) , m_cppEditorDocument(qobject_cast<CppEditorDocument *>(q->textDocument()))
, m_cppEditorOutline(new CppEditorOutline(q))
, m_declDefLinkFinder(new FunctionDeclDefLinkFinder(q)) , m_declDefLinkFinder(new FunctionDeclDefLinkFinder(q))
, m_localRenaming(q) , m_localRenaming(q)
, m_useSelectionsUpdater(q) , m_useSelectionsUpdater(q)
@@ -160,8 +159,11 @@ void CppEditorWidget::finalizeInitialization()
// clang-format off // clang-format off
// function combo box sorting // function combo box sorting
connect(CppEditorPlugin::instance(), &CppEditorPlugin::outlineSortingChanged, if (CppModelManager::supportsOutline(d->m_cppEditorDocument)) {
outline(), &CppEditorOutline::setSorted); d->m_cppEditorOutline = new CppEditorOutline(this);
connect(CppEditorPlugin::instance(), &CppEditorPlugin::outlineSortingChanged,
outline(), &CppEditorOutline::setSorted);
}
connect(d->m_cppEditorDocument, &CppEditorDocument::codeWarningsUpdated, connect(d->m_cppEditorDocument, &CppEditorDocument::codeWarningsUpdated,
this, &CppEditorWidget::onCodeWarningsUpdated); this, &CppEditorWidget::onCodeWarningsUpdated);
@@ -195,8 +197,10 @@ void CppEditorWidget::finalizeInitialization()
}); });
connect(&d->m_localRenaming, &CppLocalRenaming::processKeyPressNormally, connect(&d->m_localRenaming, &CppLocalRenaming::processKeyPressNormally,
this, &CppEditorWidget::processKeyNormally); this, &CppEditorWidget::processKeyNormally);
connect(this, &QPlainTextEdit::cursorPositionChanged, if (d->m_cppEditorOutline) {
d->m_cppEditorOutline, &CppEditorOutline::updateIndex); connect(this, &QPlainTextEdit::cursorPositionChanged,
d->m_cppEditorOutline, &CppEditorOutline::updateIndex);
}
connect(cppEditorDocument(), &CppEditorDocument::preprocessorSettingsChanged, this, connect(cppEditorDocument(), &CppEditorDocument::preprocessorSettingsChanged, this,
[this](bool customSettings) { [this](bool customSettings) {
@@ -232,7 +236,8 @@ void CppEditorWidget::finalizeInitialization()
}); });
// Toolbar: Outline/Overview combo box // Toolbar: Outline/Overview combo box
insertExtraToolBarWidget(TextEditorWidget::Left, d->m_cppEditorOutline->widget()); if (d->m_cppEditorOutline)
insertExtraToolBarWidget(TextEditorWidget::Left, d->m_cppEditorOutline->widget());
// clang-format on // clang-format on
// Toolbar: '#' Button // Toolbar: '#' Button
@@ -266,7 +271,8 @@ void CppEditorWidget::finalizeInitializationAfterDuplication(TextEditorWidget *o
if (cppEditorWidget->isSemanticInfoValidExceptLocalUses()) if (cppEditorWidget->isSemanticInfoValidExceptLocalUses())
updateSemanticInfo(cppEditorWidget->semanticInfo()); updateSemanticInfo(cppEditorWidget->semanticInfo());
d->m_cppEditorOutline->update(); if (d->m_cppEditorOutline)
d->m_cppEditorOutline->update();
const Id selectionKind = CodeWarningsSelection; const Id selectionKind = CodeWarningsSelection;
setExtraSelections(selectionKind, cppEditorWidget->extraSelections(selectionKind)); setExtraSelections(selectionKind, cppEditorWidget->extraSelections(selectionKind));
@@ -325,7 +331,8 @@ void CppEditorWidget::selectAll()
void CppEditorWidget::onCppDocumentUpdated() void CppEditorWidget::onCppDocumentUpdated()
{ {
d->m_cppEditorOutline->update(); if (d->m_cppEditorOutline)
d->m_cppEditorOutline->update();
} }
void CppEditorWidget::onCodeWarningsUpdated(unsigned revision, void CppEditorWidget::onCodeWarningsUpdated(unsigned revision,

View File

@@ -28,6 +28,7 @@
#include "cppeditor.h" #include "cppeditor.h"
#include <cpptools/cppeditoroutline.h> #include <cpptools/cppeditoroutline.h>
#include <cpptools/cppmodelmanager.h>
#include <cpptools/cppoverviewmodel.h> #include <cpptools/cppoverviewmodel.h>
#include <texteditor/textdocument.h> #include <texteditor/textdocument.h>
@@ -212,7 +213,10 @@ bool CppOutlineWidget::syncCursor()
bool CppOutlineWidgetFactory::supportsEditor(Core::IEditor *editor) const bool CppOutlineWidgetFactory::supportsEditor(Core::IEditor *editor) const
{ {
return qobject_cast<CppEditor*>(editor); const auto cppEditor = qobject_cast<CppEditor*>(editor);
if (!cppEditor)
return false;
return CppTools::CppModelManager::supportsOutline(cppEditor->textDocument());
} }
TextEditor::IOutlineWidget *CppOutlineWidgetFactory::createWidget(Core::IEditor *editor) TextEditor::IOutlineWidget *CppOutlineWidgetFactory::createWidget(Core::IEditor *editor)

View File

@@ -1310,6 +1310,11 @@ bool CppModelManager::isCppEditor(Core::IEditor *editor)
return editor->context().contains(ProjectExplorer::Constants::CXX_LANGUAGE_ID); return editor->context().contains(ProjectExplorer::Constants::CXX_LANGUAGE_ID);
} }
bool CppModelManager::supportsOutline(const TextEditor::TextDocument *document)
{
return instance()->d->m_activeModelManagerSupport->supportsOutline(document);
}
bool CppModelManager::isClangCodeModelActive() const bool CppModelManager::isClangCodeModelActive() const
{ {
return d->m_activeModelManagerSupport != d->m_builtinModelManagerSupport; return d->m_activeModelManagerSupport != d->m_builtinModelManagerSupport;

View File

@@ -142,6 +142,7 @@ public:
void emitAbstractEditorSupportRemoved(const QString &filePath); void emitAbstractEditorSupportRemoved(const QString &filePath);
static bool isCppEditor(Core::IEditor *editor); static bool isCppEditor(Core::IEditor *editor);
static bool supportsOutline(const TextEditor::TextDocument *document);
bool isClangCodeModelActive() const; bool isClangCodeModelActive() const;
QSet<AbstractEditorSupport*> abstractEditorSupports() const; QSet<AbstractEditorSupport*> abstractEditorSupports() const;

View File

@@ -61,6 +61,7 @@ public:
virtual FollowSymbolInterface &followSymbolInterface() = 0; virtual FollowSymbolInterface &followSymbolInterface() = 0;
virtual RefactoringEngineInterface &refactoringEngineInterface() = 0; virtual RefactoringEngineInterface &refactoringEngineInterface() = 0;
virtual std::unique_ptr<AbstractOverviewModel> createOverviewModel() = 0; virtual std::unique_ptr<AbstractOverviewModel> createOverviewModel() = 0;
virtual bool supportsOutline(const TextEditor::TextDocument *) const { return true; }
}; };
class CPPTOOLS_EXPORT ModelManagerSupportProvider class CPPTOOLS_EXPORT ModelManagerSupportProvider

View File

@@ -1003,6 +1003,16 @@ void Client::setDiagnosticsHandlers(const TextMarkCreator &textMarkCreator,
m_diagnosticManager.setDiagnosticsHandlers(textMarkCreator, hideHandler); m_diagnosticManager.setDiagnosticsHandlers(textMarkCreator, hideHandler);
} }
void Client::setSymbolStringifier(const LanguageServerProtocol::SymbolStringifier &stringifier)
{
m_symbolStringifier = stringifier;
}
SymbolStringifier Client::symbolStringifier() const
{
return m_symbolStringifier;
}
void Client::start() void Client::start()
{ {
if (m_clientInterface->start()) if (m_clientInterface->start())

View File

@@ -174,6 +174,8 @@ public:
const LanguageServerProtocol::Diagnostic &diag) const; const LanguageServerProtocol::Diagnostic &diag) const;
void setDiagnosticsHandlers(const TextMarkCreator &textMarkCreator, void setDiagnosticsHandlers(const TextMarkCreator &textMarkCreator,
const HideDiagnosticsHandler &hideHandler); const HideDiagnosticsHandler &hideHandler);
void setSymbolStringifier(const LanguageServerProtocol::SymbolStringifier &stringifier);
LanguageServerProtocol::SymbolStringifier symbolStringifier() const;
// logging // logging
void log(const QString &message) const; void log(const QString &message) const;
@@ -270,6 +272,7 @@ private:
SemanticTokenSupport m_tokentSupport; SemanticTokenSupport m_tokentSupport;
QString m_serverName; QString m_serverName;
QString m_serverVersion; QString m_serverVersion;
LanguageServerProtocol::SymbolStringifier m_symbolStringifier;
bool m_locatorsEnabled = true; bool m_locatorsEnabled = true;
}; };

View File

@@ -55,14 +55,15 @@ public:
, m_type(info.kind()) , m_type(info.kind())
{ } { }
LanguageClientOutlineItem(const DocumentSymbol &info) LanguageClientOutlineItem(const DocumentSymbol &info, const SymbolStringifier &stringifier)
: m_name(info.name()) : m_name(info.name())
, m_detail(info.detail().value_or(QString())) , m_detail(info.detail().value_or(QString()))
, m_range(info.range()) , m_range(info.range())
, m_symbolStringifier(stringifier)
, m_type(info.kind()) , m_type(info.kind())
{ {
for (const DocumentSymbol &child : info.children().value_or(QList<DocumentSymbol>())) for (const DocumentSymbol &child : info.children().value_or(QList<DocumentSymbol>()))
appendChild(new LanguageClientOutlineItem(child)); appendChild(new LanguageClientOutlineItem(child, stringifier));
} }
// TreeItem interface // TreeItem interface
@@ -72,7 +73,9 @@ public:
case Qt::DecorationRole: case Qt::DecorationRole:
return symbolIcon(m_type); return symbolIcon(m_type);
case Qt::DisplayRole: case Qt::DisplayRole:
return m_name; return m_symbolStringifier
? m_symbolStringifier(static_cast<SymbolKind>(m_type), m_name, m_detail)
: m_name;
default: default:
return Utils::TreeItem::data(column, role); return Utils::TreeItem::data(column, role);
} }
@@ -85,6 +88,7 @@ private:
QString m_name; QString m_name;
QString m_detail; QString m_detail;
Range m_range; Range m_range;
SymbolStringifier m_symbolStringifier;
int m_type = -1; int m_type = -1;
}; };
@@ -102,8 +106,16 @@ public:
{ {
clear(); clear();
for (const DocumentSymbol &symbol : info) for (const DocumentSymbol &symbol : info)
rootItem()->appendChild(new LanguageClientOutlineItem(symbol)); rootItem()->appendChild(new LanguageClientOutlineItem(symbol, m_symbolStringifier));
} }
void setSymbolStringifier(const SymbolStringifier &stringifier)
{
m_symbolStringifier = stringifier;
}
private:
SymbolStringifier m_symbolStringifier;
}; };
class LanguageClientOutlineWidget : public TextEditor::IOutlineWidget class LanguageClientOutlineWidget : public TextEditor::IOutlineWidget
@@ -153,6 +165,7 @@ LanguageClientOutlineWidget::LanguageClientOutlineWidget(Client *client,
layout->setSpacing(0); layout->setSpacing(0);
layout->addWidget(Core::ItemViewFind::createSearchableWrapper(&m_view)); layout->addWidget(Core::ItemViewFind::createSearchableWrapper(&m_view));
setLayout(layout); setLayout(layout);
m_model.setSymbolStringifier(m_client->symbolStringifier());
m_view.setModel(&m_model); m_view.setModel(&m_model);
m_view.setHeaderHidden(true); m_view.setHeaderHidden(true);
m_view.setExpandsOnDoubleClick(false); m_view.setExpandsOnDoubleClick(false);
@@ -295,6 +308,7 @@ OutlineComboBox::OutlineComboBox(Client *client, TextEditor::BaseTextEditor *edi
, m_editorWidget(editor->editorWidget()) , m_editorWidget(editor->editorWidget())
, m_uri(DocumentUri::fromFilePath(editor->document()->filePath())) , m_uri(DocumentUri::fromFilePath(editor->document()->filePath()))
{ {
m_model.setSymbolStringifier(client->symbolStringifier());
setModel(&m_model); setModel(&m_model);
setMinimumContentsLength(13); setMinimumContentsLength(13);
QSizePolicy policy = sizePolicy(); QSizePolicy policy = sizePolicy();