From 6e6d25d11707dd835f3572f341617ea85fdcf47e Mon Sep 17 00:00:00 2001 From: David Schulz Date: Fri, 22 Mar 2024 12:40:25 +0100 Subject: [PATCH] LanguageClient: generate issue pane entries for diagnostics The clang code model already generated issue pane entries for received diagnostics. Move the tracking of these issue pane entries to the generic language client support and also generate entries for other language servers. Fixes: QTCREATORBUG-30549 Change-Id: I29f2ffbf199c3fdc5ca59a670f5033c833b96a49 Reviewed-by: Christian Kandeler --- src/plugins/clangcodemodel/clangdclient.cpp | 60 ++++--------- src/plugins/clangcodemodel/clangdclient.h | 3 - .../clangmodelmanagersupport.cpp | 4 +- src/plugins/clangcodemodel/clangtextmark.cpp | 31 ------- .../languageclient/diagnosticmanager.cpp | 90 ++++++++++++++++++- .../languageclient/diagnosticmanager.h | 14 ++- .../languageclient/languageclient_global.h | 1 + .../languageclient/languageclientplugin.cpp | 7 ++ 8 files changed, 126 insertions(+), 84 deletions(-) diff --git a/src/plugins/clangcodemodel/clangdclient.cpp b/src/plugins/clangcodemodel/clangdclient.cpp index 248ef69b364..d3ede30b10a 100644 --- a/src/plugins/clangcodemodel/clangdclient.cpp +++ b/src/plugins/clangcodemodel/clangdclient.cpp @@ -356,7 +356,6 @@ public: QHash highlightingData; QHash parserConfigs; - QHash issuePaneEntries; QHash openedExtraFiles; VersionedDataCache astCache; @@ -690,30 +689,14 @@ const LanguageClient::Client::CustomInspectorTabs ClangdClient::createCustomInsp class ClangdDiagnosticManager : public LanguageClient::DiagnosticManager { - using LanguageClient::DiagnosticManager::DiagnosticManager; - - ClangdClient *getClient() const { return qobject_cast(client()); } - - bool isCurrentDocument(const Utils::FilePath &filePath) const +public: + ClangdDiagnosticManager(LanguageClient::Client *client) + : LanguageClient::DiagnosticManager(client) { - const IDocument * const doc = EditorManager::currentDocument(); - return doc && doc->filePath() == filePath; - } - - void showDiagnostics(const Utils::FilePath &filePath, int version) override - { - getClient()->clearTasks(filePath); - DiagnosticManager::showDiagnostics(filePath, version); - if (isCurrentDocument(filePath)) - getClient()->switchIssuePaneEntries(filePath); - } - - void hideDiagnostics(const Utils::FilePath &filePath) override - { - DiagnosticManager::hideDiagnostics(filePath); - if (isCurrentDocument(filePath)) - TaskHub::clearTasks(Constants::TASK_CATEGORY_DIAGNOSTICS); + setTaskCategory(Constants::TASK_CATEGORY_DIAGNOSTICS); + setForceCreateTasks(false); } +private: QList filteredDiagnostics(const QList &diagnostics) const override { @@ -729,7 +712,18 @@ class ClangdDiagnosticManager : public LanguageClient::DiagnosticManager const Diagnostic &diagnostic, bool isProjectFile) const override { - return new ClangdTextMark(doc, diagnostic, isProjectFile, getClient()); + return new ClangdTextMark( + doc, diagnostic, isProjectFile, qobject_cast(client())); + } + + QString taskText(const Diagnostic &diagnostic) const override + { + QString text = diagnostic.message(); + auto splitIndex = text.indexOf("\n\n"); + if (splitIndex >= 0) + text.truncate(splitIndex); + + return diagnosticCategoryPrefixRemoved(text); } }; @@ -938,24 +932,6 @@ void ClangdClient::updateParserConfig(const Utils::FilePath &filePath, emit configChanged(); } -void ClangdClient::switchIssuePaneEntries(const FilePath &filePath) -{ - TaskHub::clearTasks(Constants::TASK_CATEGORY_DIAGNOSTICS); - const Tasks tasks = d->issuePaneEntries.value(filePath); - for (const Task &t : tasks) - TaskHub::addTask(t); -} - -void ClangdClient::addTask(const ProjectExplorer::Task &task) -{ - d->issuePaneEntries[task.file] << task; -} - -void ClangdClient::clearTasks(const Utils::FilePath &filePath) -{ - d->issuePaneEntries[filePath].clear(); -} - std::optional ClangdClient::hasVirtualFunctionAt(TextDocument *doc, int revision, const Range &range) { diff --git a/src/plugins/clangcodemodel/clangdclient.h b/src/plugins/clangcodemodel/clangdclient.h index 0274bffc24f..c0f687ea1fc 100644 --- a/src/plugins/clangcodemodel/clangdclient.h +++ b/src/plugins/clangcodemodel/clangdclient.h @@ -97,9 +97,6 @@ public: void updateParserConfig(const Utils::FilePath &filePath, const CppEditor::BaseEditorDocumentParser::Configuration &config); - void switchIssuePaneEntries(const Utils::FilePath &filePath); - void addTask(const ProjectExplorer::Task &task); - void clearTasks(const Utils::FilePath &filePath); std::optional hasVirtualFunctionAt(TextEditor::TextDocument *doc, int revision, const LanguageServerProtocol::Range &range); diff --git a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp index 79b4273771b..a68f5663090 100644 --- a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp +++ b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp @@ -457,10 +457,8 @@ void ClangModelManagerSupport::onCurrentEditorChanged(IEditor *editor) const FilePath filePath = editor->document()->filePath(); if (auto processor = ClangEditorDocumentProcessor::get(filePath)) { processor->semanticRehighlight(); - if (const auto client = clientForFile(filePath)) { + if (const auto client = clientForFile(filePath)) client->updateParserConfig(filePath, processor->parserConfig()); - client->switchIssuePaneEntries(filePath); - } } } diff --git a/src/plugins/clangcodemodel/clangtextmark.cpp b/src/plugins/clangcodemodel/clangtextmark.cpp index d22127d3dd4..c2c8c88801b 100644 --- a/src/plugins/clangcodemodel/clangtextmark.cpp +++ b/src/plugins/clangcodemodel/clangtextmark.cpp @@ -16,8 +16,6 @@ #include #include -#include - #include #include #include @@ -204,34 +202,6 @@ ClangDiagnostic convertDiagnostic(const ClangdDiagnostic &src, return target; } -Task createTask(const ClangDiagnostic &diagnostic) -{ - Task::TaskType taskType = Task::TaskType::Unknown; - QIcon icon; - - switch (diagnostic.severity) { - case ClangDiagnostic::Severity::Fatal: - case ClangDiagnostic::Severity::Error: - taskType = Task::TaskType::Error; - icon = ::Utils::Icons::CODEMODEL_ERROR.icon(); - break; - case ClangDiagnostic::Severity::Warning: - taskType = Task::TaskType::Warning; - icon = ::Utils::Icons::CODEMODEL_WARNING.icon(); - break; - default: - break; - } - - return Task(taskType, - diagnosticCategoryPrefixRemoved(diagnostic.text), - diagnostic.location.targetFilePath, - diagnostic.location.targetLine, - Constants::TASK_CATEGORY_DIAGNOSTICS, - icon, - Task::NoOptions); -} - } // anonymous namespace ClangdTextMark::ClangdTextMark(TextEditor::TextDocument *doc, @@ -258,7 +228,6 @@ ClangdTextMark::ClangdTextMark(TextEditor::TextDocument *doc, setLineAnnotation(diagnostic.message()); setColor(isError ? Theme::CodeModel_Error_TextMarkColor : Theme::CodeModel_Warning_TextMarkColor); - client->addTask(createTask(m_diagnostic)); } setActionsProvider([diag = m_diagnostic] { diff --git a/src/plugins/languageclient/diagnosticmanager.cpp b/src/plugins/languageclient/diagnosticmanager.cpp index da5a37a5f06..7fc4efb6ddc 100644 --- a/src/plugins/languageclient/diagnosticmanager.cpp +++ b/src/plugins/languageclient/diagnosticmanager.cpp @@ -4,11 +4,13 @@ #include "diagnosticmanager.h" #include "client.h" +#include "languageclientmanager.h" #include "languageclienttr.h" #include #include +#include #include #include @@ -23,8 +25,9 @@ #include using namespace LanguageServerProtocol; -using namespace Utils; +using namespace ProjectExplorer; using namespace TextEditor; +using namespace Utils; namespace LanguageClient { @@ -67,15 +70,35 @@ public: : m_client(client) {} - QMap m_diagnostics; - QMap m_marks; + void showTasks(TextDocument *doc) { + if (!doc || m_client != LanguageClientManager::clientForDocument(doc)) + return; + TaskHub::clearTasks(m_taskCategory); + const Tasks tasks = m_issuePaneEntries.value(doc->filePath()); + for (const Task &t : tasks) + TaskHub::addTask(t); + } + + QMap m_diagnostics; + QMap m_marks; Client *m_client; - Utils::Id m_extraSelectionsId = TextEditorWidget::CodeWarningsSelection; + QHash m_issuePaneEntries; + Id m_extraSelectionsId = TextEditorWidget::CodeWarningsSelection; + bool m_forceCreateTasks = true; + Id m_taskCategory = Constants::TASK_CATEGORY_DIAGNOSTICS; }; DiagnosticManager::DiagnosticManager(Client *client) : d(std::make_unique(client)) { + auto updateCurrentEditor = [this](Core::IEditor *editor) { + if (editor) + d->showTasks(qobject_cast(editor->document())); + }; + connect(Core::EditorManager::instance(), + &Core::EditorManager::currentEditorChanged, + this, + updateCurrentEditor); } DiagnosticManager::~DiagnosticManager() @@ -94,10 +117,13 @@ void DiagnosticManager::setDiagnostics(const FilePath &filePath, void DiagnosticManager::hideDiagnostics(const Utils::FilePath &filePath) { if (auto doc = TextDocument::textDocumentForFilePath(filePath)) { + if (doc == TextDocument::currentTextDocument()) + TaskHub::clearTasks(d->m_taskCategory); for (BaseTextEditor *editor : BaseTextEditor::textEditorsForDocument(doc)) editor->editorWidget()->setExtraSelections(d->m_extraSelectionsId, {}); } d->m_marks.remove(filePath); + d->m_issuePaneEntries.remove(filePath); } QList DiagnosticManager::filteredDiagnostics(const QList &diagnostics) const @@ -118,6 +144,7 @@ void DiagnosticManager::disableDiagnostics(TextEditor::TextDocument *document) void DiagnosticManager::showDiagnostics(const FilePath &filePath, int version) { + d->m_issuePaneEntries.remove(filePath); if (TextDocument *doc = TextDocument::textDocumentForFilePath(filePath)) { QList extraSelections; const VersionedDiagnostics &versionedDiagnostics = d->m_diagnostics.value(filePath); @@ -132,6 +159,8 @@ void DiagnosticManager::showDiagnostics(const FilePath &filePath, int version) extraSelections << selection; if (TextEditor::TextMark *mark = createTextMark(doc, diagnostic, isProjectFile)) marks.marks.append(mark); + if (std::optional task = createTask(doc, diagnostic, isProjectFile)) + d->m_issuePaneEntries[filePath].append(*task); } if (!marks.marks.isEmpty()) emit textMarkCreated(filePath); @@ -139,6 +168,9 @@ void DiagnosticManager::showDiagnostics(const FilePath &filePath, int version) for (BaseTextEditor *editor : BaseTextEditor::textEditorsForDocument(doc)) editor->editorWidget()->setExtraSelections(d->m_extraSelectionsId, extraSelections); + + if (doc == TextDocument::currentTextDocument()) + d->showTasks(doc); } } @@ -166,6 +198,56 @@ TextEditor::TextMark *DiagnosticManager::createTextMark(TextDocument *doc, return mark; } +std::optional DiagnosticManager::createTask( + TextDocument *doc, + const LanguageServerProtocol::Diagnostic &diagnostic, + bool isProjectFile) const +{ + if (!isProjectFile && !d->m_forceCreateTasks) + return {}; + + Task::TaskType taskType = Task::TaskType::Unknown; + QIcon icon; + + if (const std::optional severity = diagnostic.severity()) { + switch (*severity) { + case DiagnosticSeverity::Error: + taskType = Task::TaskType::Error; + icon = Icons::CODEMODEL_ERROR.icon(); + break; + case DiagnosticSeverity::Warning: + taskType = Task::TaskType::Warning; + icon = Icons::CODEMODEL_WARNING.icon(); + break; + default: + break; + } + } + + return Task(taskType, + taskText(diagnostic), + doc->filePath(), + diagnostic.range().start().line(), + d->m_taskCategory, + icon, + Task::NoOptions); +} + +QString DiagnosticManager::taskText(const LanguageServerProtocol::Diagnostic &diagnostic) const +{ + return diagnostic.message(); +} + +void DiagnosticManager::setTaskCategory(const Utils::Id &taskCategory) +{ + d->m_taskCategory = taskCategory; +} + +void DiagnosticManager::setForceCreateTasks(bool forceCreateTasks) +{ + d->m_forceCreateTasks = forceCreateTasks; +} + QTextEdit::ExtraSelection DiagnosticManager::createDiagnosticSelection( const LanguageServerProtocol::Diagnostic &diagnostic, QTextDocument *textDocument) const { diff --git a/src/plugins/languageclient/diagnosticmanager.h b/src/plugins/languageclient/diagnosticmanager.h index 6d1626029b1..cb3798be133 100644 --- a/src/plugins/languageclient/diagnosticmanager.h +++ b/src/plugins/languageclient/diagnosticmanager.h @@ -9,7 +9,6 @@ #include -#include #include #include @@ -19,6 +18,8 @@ class TextDocument; class TextMark; } +namespace ProjectExplorer { class Task; } + namespace LanguageClient { class Client; @@ -58,6 +59,17 @@ protected: virtual TextEditor::TextMark *createTextMark(TextEditor::TextDocument *doc, const LanguageServerProtocol::Diagnostic &diagnostic, bool isProjectFile) const; + + virtual std::optional createTask( + TextEditor::TextDocument *doc, + const LanguageServerProtocol::Diagnostic &diagnostic, + bool isProjectFile) const; + virtual QString taskText(const LanguageServerProtocol::Diagnostic &diagnostic) const; + void setTaskCategory(const Utils::Id &taskCategory); + + // enables task creations for diagnostics outside of the clients project (default: on) + void setForceCreateTasks(bool forceCreateTasks); + virtual QTextEdit::ExtraSelection createDiagnosticSelection( const LanguageServerProtocol::Diagnostic &diagnostic, QTextDocument *textDocument) const; diff --git a/src/plugins/languageclient/languageclient_global.h b/src/plugins/languageclient/languageclient_global.h index 2abb395d86e..cdcd1fb5ddc 100644 --- a/src/plugins/languageclient/languageclient_global.h +++ b/src/plugins/languageclient/languageclient_global.h @@ -41,6 +41,7 @@ const char LANGUAGECLIENT_WORKSPACE_METHOD_FILTER_DESCRIPTION[] "Locates functions and methods in the language server workspace."); const char CALL_HIERARCHY_FACTORY_ID[] = "LanguageClient.CallHierarchy"; +const char TASK_CATEGORY_DIAGNOSTICS[] = "LanguageClient.DiagnosticTask"; } // namespace Constants } // namespace LanguageClient diff --git a/src/plugins/languageclient/languageclientplugin.cpp b/src/plugins/languageclient/languageclientplugin.cpp index 985f00da1ef..a466a5b300a 100644 --- a/src/plugins/languageclient/languageclientplugin.cpp +++ b/src/plugins/languageclient/languageclientplugin.cpp @@ -14,6 +14,8 @@ #include #include +#include + #include #include @@ -59,6 +61,11 @@ void LanguageClientPlugin::initialize() inspectAction.setText(Tr::tr("Inspect Language Clients...")); inspectAction.addToContainer(Core::Constants::M_TOOLS_DEBUG); inspectAction.addOnTriggered(this, &LanguageClientManager::showInspector); + + ProjectExplorer::TaskHub::addCategory( + {Constants::TASK_CATEGORY_DIAGNOSTICS, + Tr::tr("Language Server Diagnostics"), + Tr::tr("Issues provided by the Language Server in the current document.")}); } void LanguageClientPlugin::extensionsInitialized()