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

View File

@@ -732,6 +732,31 @@ ClangdClient::ClangdClient(Project *project, const Utils::FilePath &jsonDbDir)
const auto hideDiagsHandler = []{ ClangDiagnosticManager::clearTaskHubIssues(); };
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,
const DocumentUri &uri) {
gatherHelpItemForTooltip(response, uri);

View File

@@ -171,6 +171,11 @@ std::unique_ptr<CppTools::AbstractOverviewModel> ClangModelManagerSupport::creat
return std::make_unique<OverviewModel>();
}
bool ClangModelManagerSupport::supportsOutline(const TextEditor::TextDocument *document) const
{
return !clientForFile(document->filePath());
}
CppTools::BaseEditorDocumentProcessor *ClangModelManagerSupport::createEditorDocumentProcessor(
TextEditor::TextDocument *baseTextDocument)
{
@@ -344,7 +349,7 @@ void ClangModelManagerSupport::updateLanguageClient(ProjectExplorer::Project *pr
}
ClangdClient *ClangModelManagerSupport::clientForProject(
const ProjectExplorer::Project *project)
const ProjectExplorer::Project *project) const
{
const QList<Client *> clients = Utils::filtered(
LanguageClientManager::clientsForProject(project),
@@ -357,7 +362,7 @@ ClangdClient *ClangModelManagerSupport::clientForProject(
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));
}

View File

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

View File

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

View File

@@ -28,6 +28,7 @@
#include "cppeditor.h"
#include <cpptools/cppeditoroutline.h>
#include <cpptools/cppmodelmanager.h>
#include <cpptools/cppoverviewmodel.h>
#include <texteditor/textdocument.h>
@@ -212,7 +213,10 @@ bool CppOutlineWidget::syncCursor()
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)

View File

@@ -1310,6 +1310,11 @@ bool CppModelManager::isCppEditor(Core::IEditor *editor)
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
{
return d->m_activeModelManagerSupport != d->m_builtinModelManagerSupport;

View File

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

View File

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

View File

@@ -1003,6 +1003,16 @@ void Client::setDiagnosticsHandlers(const TextMarkCreator &textMarkCreator,
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()
{
if (m_clientInterface->start())

View File

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

View File

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