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()