forked from qt-creator/qt-creator
ClangCodeModel: Provide diagnostics via clangd
Change-Id: Ib45a62ebe200c2b56a1bb1a66f8a92103e60d092 Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
@@ -41,8 +41,6 @@
|
||||
|
||||
#define qCDebugIpc() qCDebug(ipcLog) << "<===="
|
||||
|
||||
using namespace ClangBackEnd;
|
||||
|
||||
namespace ClangCodeModel {
|
||||
namespace Internal {
|
||||
|
||||
@@ -189,12 +187,12 @@ void BackendReceiver::alive()
|
||||
m_aliveHandler();
|
||||
}
|
||||
|
||||
void BackendReceiver::echo(const EchoMessage &message)
|
||||
void BackendReceiver::echo(const ClangBackEnd::EchoMessage &message)
|
||||
{
|
||||
qCDebugIpc() << message;
|
||||
}
|
||||
|
||||
void BackendReceiver::completions(const CompletionsMessage &message)
|
||||
void BackendReceiver::completions(const ClangBackEnd::CompletionsMessage &message)
|
||||
{
|
||||
qCDebugIpc() << "CompletionsMessage with" << message.codeCompletions.size()
|
||||
<< "items";
|
||||
@@ -204,7 +202,7 @@ void BackendReceiver::completions(const CompletionsMessage &message)
|
||||
processor->handleAvailableCompletions(message.codeCompletions);
|
||||
}
|
||||
|
||||
void BackendReceiver::annotations(const AnnotationsMessage &message)
|
||||
void BackendReceiver::annotations(const ClangBackEnd::AnnotationsMessage &message)
|
||||
{
|
||||
qCDebugIpc() << "AnnotationsMessage"
|
||||
<< "for" << QFileInfo(message.fileContainer.filePath).fileName() << "with"
|
||||
@@ -230,10 +228,10 @@ void BackendReceiver::annotations(const AnnotationsMessage &message)
|
||||
}
|
||||
|
||||
static
|
||||
CppTools::CursorInfo::Range toCursorInfoRange(const SourceRangeContainer &sourceRange)
|
||||
CppTools::CursorInfo::Range toCursorInfoRange(const ClangBackEnd::SourceRangeContainer &sourceRange)
|
||||
{
|
||||
const SourceLocationContainer &start = sourceRange.start;
|
||||
const SourceLocationContainer &end = sourceRange.end;
|
||||
const ClangBackEnd::SourceLocationContainer &start = sourceRange.start;
|
||||
const ClangBackEnd::SourceLocationContainer &end = sourceRange.end;
|
||||
const int length = end.column - start.column;
|
||||
|
||||
return {start.line, start.column, length};
|
||||
@@ -241,13 +239,13 @@ CppTools::CursorInfo::Range toCursorInfoRange(const SourceRangeContainer &source
|
||||
|
||||
static
|
||||
CppTools::CursorInfo toCursorInfo(const CppTools::SemanticInfo::LocalUseMap &localUses,
|
||||
const ReferencesMessage &message)
|
||||
const ClangBackEnd::ReferencesMessage &message)
|
||||
{
|
||||
CppTools::CursorInfo result;
|
||||
const QVector<SourceRangeContainer> &references = message.references;
|
||||
const QVector<ClangBackEnd::SourceRangeContainer> &references = message.references;
|
||||
|
||||
result.areUseRangesForLocalVariable = message.isLocalVariable;
|
||||
for (const SourceRangeContainer &reference : references)
|
||||
for (const ClangBackEnd::SourceRangeContainer &reference : references)
|
||||
result.useRanges.append(toCursorInfoRange(reference));
|
||||
|
||||
result.useRanges.reserve(references.size());
|
||||
@@ -257,13 +255,13 @@ CppTools::CursorInfo toCursorInfo(const CppTools::SemanticInfo::LocalUseMap &loc
|
||||
}
|
||||
|
||||
static
|
||||
CppTools::SymbolInfo toSymbolInfo(const FollowSymbolMessage &message)
|
||||
CppTools::SymbolInfo toSymbolInfo(const ClangBackEnd::FollowSymbolMessage &message)
|
||||
{
|
||||
CppTools::SymbolInfo result;
|
||||
const SourceRangeContainer &range = message.result.range;
|
||||
const ClangBackEnd::SourceRangeContainer &range = message.result.range;
|
||||
|
||||
const SourceLocationContainer &start = range.start;
|
||||
const SourceLocationContainer &end = range.end;
|
||||
const ClangBackEnd::SourceLocationContainer &start = range.start;
|
||||
const ClangBackEnd::SourceLocationContainer &end = range.end;
|
||||
result.startLine = start.line;
|
||||
result.startColumn = start.column;
|
||||
result.endLine = end.line;
|
||||
@@ -275,7 +273,7 @@ CppTools::SymbolInfo toSymbolInfo(const FollowSymbolMessage &message)
|
||||
return result;
|
||||
}
|
||||
|
||||
void BackendReceiver::references(const ReferencesMessage &message)
|
||||
void BackendReceiver::references(const ClangBackEnd::ReferencesMessage &message)
|
||||
{
|
||||
qCDebugIpc() << "ReferencesMessage with"
|
||||
<< message.references.size() << "references";
|
||||
@@ -292,22 +290,22 @@ void BackendReceiver::references(const ReferencesMessage &message)
|
||||
futureInterface.reportFinished();
|
||||
}
|
||||
|
||||
static Core::HelpItem::Category toHelpItemCategory(ToolTipInfo::QdocCategory category)
|
||||
static Core::HelpItem::Category toHelpItemCategory(ClangBackEnd::ToolTipInfo::QdocCategory category)
|
||||
{
|
||||
switch (category) {
|
||||
case ToolTipInfo::Unknown:
|
||||
case ClangBackEnd::ToolTipInfo::Unknown:
|
||||
return Core::HelpItem::Unknown;
|
||||
case ToolTipInfo::ClassOrNamespace:
|
||||
case ClangBackEnd::ToolTipInfo::ClassOrNamespace:
|
||||
return Core::HelpItem::ClassOrNamespace;
|
||||
case ToolTipInfo::Enum:
|
||||
case ClangBackEnd::ToolTipInfo::Enum:
|
||||
return Core::HelpItem::Enum;
|
||||
case ToolTipInfo::Typedef:
|
||||
case ClangBackEnd::ToolTipInfo::Typedef:
|
||||
return Core::HelpItem::Typedef;
|
||||
case ToolTipInfo::Macro:
|
||||
case ClangBackEnd::ToolTipInfo::Macro:
|
||||
return Core::HelpItem::Macro;
|
||||
case ToolTipInfo::Brief:
|
||||
case ClangBackEnd::ToolTipInfo::Brief:
|
||||
return Core::HelpItem::Brief;
|
||||
case ToolTipInfo::Function:
|
||||
case ClangBackEnd::ToolTipInfo::Function:
|
||||
return Core::HelpItem::Function;
|
||||
}
|
||||
|
||||
@@ -325,11 +323,11 @@ static QStringList toStringList(const Utf8StringVector &utf8StringVector)
|
||||
return list;
|
||||
}
|
||||
|
||||
static CppTools::ToolTipInfo toToolTipInfo(const ToolTipMessage &message)
|
||||
static CppTools::ToolTipInfo toToolTipInfo(const ClangBackEnd::ToolTipMessage &message)
|
||||
{
|
||||
CppTools::ToolTipInfo info;
|
||||
|
||||
const ToolTipInfo &backendInfo = message.toolTipInfo;
|
||||
const ClangBackEnd::ToolTipInfo &backendInfo = message.toolTipInfo;
|
||||
|
||||
info.text = backendInfo.text;
|
||||
info.briefComment = backendInfo.briefComment;
|
||||
@@ -343,7 +341,7 @@ static CppTools::ToolTipInfo toToolTipInfo(const ToolTipMessage &message)
|
||||
return info;
|
||||
}
|
||||
|
||||
void BackendReceiver::tooltip(const ToolTipMessage &message)
|
||||
void BackendReceiver::tooltip(const ClangBackEnd::ToolTipMessage &message)
|
||||
{
|
||||
qCDebugIpc() << "ToolTipMessage" << message.toolTipInfo.text;
|
||||
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
|
||||
#include "clangdclient.h"
|
||||
|
||||
#include "clangdiagnosticmanager.h"
|
||||
#include "clangtextmark.h"
|
||||
|
||||
#include <clangsupport/sourcelocationscontainer.h>
|
||||
#include <coreplugin/editormanager/editormanager.h>
|
||||
#include <coreplugin/find/searchresultitem.h>
|
||||
@@ -37,6 +40,7 @@
|
||||
#include <cpptools/cppvirtualfunctionassistprovider.h>
|
||||
#include <cpptools/cppvirtualfunctionproposalitem.h>
|
||||
#include <languageclient/languageclientinterface.h>
|
||||
#include <languageclient/languageclientutils.h>
|
||||
#include <projectexplorer/project.h>
|
||||
#include <projectexplorer/projecttree.h>
|
||||
#include <projectexplorer/session.h>
|
||||
@@ -604,6 +608,23 @@ public:
|
||||
const int revision;
|
||||
};
|
||||
|
||||
class DiagnosticsCapabilities : public JsonObject
|
||||
{
|
||||
public:
|
||||
using JsonObject::JsonObject;
|
||||
void enableCategorySupport() { insert("categorySupport", true); }
|
||||
void enableCodeActionsInline() {insert("codeActionsInline", true);}
|
||||
};
|
||||
|
||||
class ClangdTextDocumentClientCapabilities : public TextDocumentClientCapabilities
|
||||
{
|
||||
public:
|
||||
using TextDocumentClientCapabilities::TextDocumentClientCapabilities;
|
||||
|
||||
|
||||
void setPublishDiagnostics(const DiagnosticsCapabilities &caps)
|
||||
{ insert("publishDiagnostics", caps); }
|
||||
};
|
||||
|
||||
class ClangdClient::Private
|
||||
{
|
||||
@@ -650,12 +671,30 @@ ClangdClient::ClangdClient(Project *project, const Utils::FilePath &jsonDbDir)
|
||||
langFilter.mimeTypes = QStringList{"text/x-chdr", "text/x-csrc",
|
||||
"text/x-c++hdr", "text/x-c++src", "text/x-objc++src", "text/x-objcsrc"};
|
||||
setSupportedLanguage(langFilter);
|
||||
LanguageServerProtocol::ClientCapabilities caps = Client::defaultClientCapabilities();
|
||||
setActivateDocumentAutomatically(true);
|
||||
ClientCapabilities caps = Client::defaultClientCapabilities();
|
||||
Utils::optional<TextDocumentClientCapabilities> textCaps = caps.textDocument();
|
||||
if (textCaps) {
|
||||
ClangdTextDocumentClientCapabilities clangdTextCaps(*textCaps);
|
||||
clangdTextCaps.clearCompletion();
|
||||
clangdTextCaps.clearDocumentHighlight();
|
||||
DiagnosticsCapabilities diagnostics;
|
||||
diagnostics.enableCategorySupport();
|
||||
diagnostics.enableCodeActionsInline();
|
||||
clangdTextCaps.setPublishDiagnostics(diagnostics);
|
||||
caps.setTextDocument(clangdTextCaps);
|
||||
}
|
||||
caps.clearExperimental();
|
||||
setClientCapabilities(caps);
|
||||
setLocatorsEnabled(false);
|
||||
setProgressTitleForToken(indexingToken(), tr("Parsing C/C++ Files (clangd)"));
|
||||
setCurrentProject(project);
|
||||
|
||||
const auto textMarkCreator = [this](const Utils::FilePath &filePath,
|
||||
const Diagnostic &diag) { return new ClangdTextMark(filePath, diag, this); };
|
||||
const auto hideDiagsHandler = []{ ClangDiagnosticManager::clearTaskHubIssues(); };
|
||||
setDiagnosticsHandlers(textMarkCreator, hideDiagsHandler);
|
||||
|
||||
connect(this, &Client::workDone, this, [this, project](const ProgressToken &token) {
|
||||
const QString * const val = Utils::get_if<QString>(&token);
|
||||
if (val && *val == indexingToken()) {
|
||||
@@ -791,6 +830,17 @@ void ClangdClient::findUsages(TextEditor::TextDocument *document, const QTextCur
|
||||
|
||||
void ClangdClient::enableTesting() { d->isTesting = true; }
|
||||
|
||||
void ClangdClient::handleDiagnostics(const PublishDiagnosticsParams ¶ms)
|
||||
{
|
||||
const DocumentUri &uri = params.uri();
|
||||
Client::handleDiagnostics(params);
|
||||
for (const Diagnostic &diagnostic : params.diagnostics()) {
|
||||
const ClangdDiagnostic clangdDiagnostic(diagnostic);
|
||||
for (const CodeAction &action : clangdDiagnostic.codeActions().value_or(QList<CodeAction>{}))
|
||||
LanguageClient::updateCodeActionRefactoringMarker(this, action, uri);
|
||||
}
|
||||
}
|
||||
|
||||
QVersionNumber ClangdClient::versionNumber() const
|
||||
{
|
||||
if (d->versionNumber)
|
||||
@@ -1506,6 +1556,16 @@ TextEditor::IAssistProcessor *ClangdClient::VirtualFunctionAssistProvider::creat
|
||||
= new VirtualFunctionAssistProcessor(m_data);
|
||||
}
|
||||
|
||||
Utils::optional<QList<CodeAction> > ClangdDiagnostic::codeActions() const
|
||||
{
|
||||
return optionalArray<LanguageServerProtocol::CodeAction>("codeActions");
|
||||
}
|
||||
|
||||
QString ClangdDiagnostic::category() const
|
||||
{
|
||||
return typedValue<QString>("category");
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace ClangCodeModel
|
||||
|
||||
|
||||
@@ -78,6 +78,8 @@ signals:
|
||||
void findUsagesDone();
|
||||
|
||||
private:
|
||||
void handleDiagnostics(const LanguageServerProtocol::PublishDiagnosticsParams ¶ms) override;
|
||||
|
||||
class Private;
|
||||
class FollowSymbolData;
|
||||
class VirtualFunctionAssistProcessor;
|
||||
@@ -85,5 +87,13 @@ private:
|
||||
Private * const d;
|
||||
};
|
||||
|
||||
class ClangdDiagnostic : public LanguageServerProtocol::Diagnostic
|
||||
{
|
||||
public:
|
||||
using Diagnostic::Diagnostic;
|
||||
Utils::optional<QList<LanguageServerProtocol::CodeAction>> codeActions() const;
|
||||
QString category() const;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace ClangCodeModel
|
||||
|
||||
@@ -298,7 +298,8 @@ void ClangDiagnosticManager::generateFixItAvailableMarkers()
|
||||
addFixItAvailableMarker(m_errorDiagnostics, lineNumbersWithFixItMarker);
|
||||
}
|
||||
|
||||
static void addTask(const ClangBackEnd::DiagnosticContainer &diagnostic, bool isChild = false)
|
||||
void ClangDiagnosticManager::addTask(const ClangBackEnd::DiagnosticContainer &diagnostic,
|
||||
bool isChild)
|
||||
{
|
||||
using namespace ProjectExplorer;
|
||||
using ::Utils::FilePath;
|
||||
|
||||
@@ -66,6 +66,8 @@ public:
|
||||
static void clearTaskHubIssues();
|
||||
void generateTaskHubIssues();
|
||||
|
||||
static void addTask(const ClangBackEnd::DiagnosticContainer &diagnostic, bool isChild = false);
|
||||
|
||||
private:
|
||||
void cleanMarks();
|
||||
QString filePath() const;
|
||||
|
||||
@@ -104,7 +104,7 @@ public:
|
||||
}
|
||||
|
||||
QWidget *createWidget(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics,
|
||||
const ClangDiagnosticManager *diagMgr)
|
||||
const std::function<bool()> &canApplyFixIt)
|
||||
{
|
||||
const QString text = htmlText(diagnostics);
|
||||
|
||||
@@ -133,7 +133,7 @@ public:
|
||||
const TargetIdToDiagnosticTable table = m_targetIdsToDiagnostics;
|
||||
const bool hideToolTipAfterLinkActivation = m_displayHints.hideTooltipAfterLinkActivation;
|
||||
QObject::connect(label, &QLabel::linkActivated, [table, hideToolTipAfterLinkActivation,
|
||||
diagMgr](const QString &action) {
|
||||
canApplyFixIt](const QString &action) {
|
||||
const ClangBackEnd::DiagnosticContainer diagnostic = table.value(action);
|
||||
|
||||
if (diagnostic == ClangBackEnd::DiagnosticContainer())
|
||||
@@ -141,10 +141,8 @@ public:
|
||||
else if (action.startsWith(LINK_ACTION_GOTO_LOCATION)) {
|
||||
openEditorAt(diagnostic);
|
||||
} else if (action.startsWith(LINK_ACTION_APPLY_FIX)) {
|
||||
if (diagMgr && !diagMgr->diagnosticsInvalidated()
|
||||
&& diagMgr->diagnosticsWithFixIts().contains(diagnostic)) {
|
||||
if (canApplyFixIt && canApplyFixIt())
|
||||
applyFixit(diagnostic);
|
||||
}
|
||||
} else {
|
||||
QTC_CHECK(!"Link target cannot be handled.");
|
||||
}
|
||||
@@ -368,14 +366,14 @@ private:
|
||||
};
|
||||
|
||||
WidgetFromDiagnostics::DisplayHints toHints(const ClangDiagnosticWidget::Destination &destination,
|
||||
const ClangDiagnosticManager *diagMgr = nullptr)
|
||||
const std::function<bool()> &canApplyFixIt)
|
||||
{
|
||||
WidgetFromDiagnostics::DisplayHints hints;
|
||||
|
||||
if (destination == ClangDiagnosticWidget::ToolTip) {
|
||||
hints.showCategoryAndEnableOption = true;
|
||||
hints.showFileNameInMainDiagnostic = false;
|
||||
hints.enableClickableFixits = diagMgr && !diagMgr->diagnosticsInvalidated();
|
||||
hints.enableClickableFixits = canApplyFixIt && canApplyFixIt();
|
||||
hints.limitWidth = true;
|
||||
hints.hideTooltipAfterLinkActivation = true;
|
||||
hints.allowTextSelection = false;
|
||||
@@ -398,7 +396,7 @@ QString ClangDiagnosticWidget::createText(
|
||||
const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics,
|
||||
const ClangDiagnosticWidget::Destination &destination)
|
||||
{
|
||||
const QString htmlText = WidgetFromDiagnostics(toHints(destination)).htmlText(diagnostics);
|
||||
const QString htmlText = WidgetFromDiagnostics(toHints(destination, {})).htmlText(diagnostics);
|
||||
|
||||
QTextDocument document;
|
||||
document.setHtml(htmlText);
|
||||
@@ -413,9 +411,10 @@ QString ClangDiagnosticWidget::createText(
|
||||
}
|
||||
|
||||
QWidget *ClangDiagnosticWidget::createWidget(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics,
|
||||
const Destination &destination, const ClangDiagnosticManager *diagMgr)
|
||||
const Destination &destination, const std::function<bool()> &canApplyFixIt)
|
||||
{
|
||||
return WidgetFromDiagnostics(toHints(destination, diagMgr)).createWidget(diagnostics, diagMgr);
|
||||
return WidgetFromDiagnostics(toHints(destination, canApplyFixIt))
|
||||
.createWidget(diagnostics, canApplyFixIt);
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
|
||||
#include <clangsupport/diagnosticcontainer.h>
|
||||
|
||||
#include <functional>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QLayout;
|
||||
class QWidget;
|
||||
@@ -43,9 +45,10 @@ public:
|
||||
static QString createText(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics,
|
||||
const Destination &destination);
|
||||
|
||||
|
||||
static QWidget *createWidget(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics,
|
||||
const Destination &destination,
|
||||
const ClangDiagnosticManager *diagMgr = nullptr);
|
||||
const std::function<bool()> &canApplyFixIt);
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
@@ -188,6 +188,9 @@ void ClangEditorDocumentProcessor::updateCodeWarnings(
|
||||
const ClangBackEnd::DiagnosticContainer &firstHeaderErrorDiagnostic,
|
||||
uint documentRevision)
|
||||
{
|
||||
if (ClangModelManagerSupport::instance()->clientForFile(m_document.filePath()))
|
||||
return;
|
||||
|
||||
if (documentRevision == revision()) {
|
||||
if (m_invalidationState == InvalidationState::Scheduled)
|
||||
m_invalidationState = InvalidationState::Canceled;
|
||||
@@ -489,7 +492,7 @@ ClangEditorDocumentProcessor::creatorForHeaderErrorDiagnosticWidget(
|
||||
vbox->setSpacing(2);
|
||||
|
||||
vbox->addWidget(ClangDiagnosticWidget::createWidget({firstHeaderErrorDiagnostic},
|
||||
ClangDiagnosticWidget::InfoBar));
|
||||
ClangDiagnosticWidget::InfoBar, {}));
|
||||
|
||||
auto widget = new QWidget;
|
||||
widget->setLayout(vbox);
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "clangtextmark.h"
|
||||
|
||||
#include "clangconstants.h"
|
||||
#include "clangdclient.h"
|
||||
#include "clangdiagnostictooltipwidget.h"
|
||||
#include "clangeditordocumentprocessor.h"
|
||||
#include "clangmodelmanagersupport.h"
|
||||
@@ -47,10 +48,13 @@
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
#include <QLayout>
|
||||
#include <QPointer>
|
||||
#include <QString>
|
||||
|
||||
using namespace CppTools;
|
||||
using namespace ClangCodeModel::Internal;
|
||||
using namespace LanguageClient;
|
||||
using namespace LanguageServerProtocol;
|
||||
using namespace Utils;
|
||||
|
||||
namespace ClangCodeModel {
|
||||
@@ -272,10 +276,13 @@ void ClangTextMark::updateIcon(bool valid)
|
||||
|
||||
bool ClangTextMark::addToolTipContent(QLayout *target) const
|
||||
{
|
||||
|
||||
const auto canApplyFixIt = [diag = m_diagnostic, diagMgr = m_diagMgr, c = color()] {
|
||||
return c != Utils::Theme::Color::IconsDisabledColor
|
||||
&& !diagMgr->diagnosticsInvalidated()
|
||||
&& diagMgr->diagnosticsWithFixIts().contains(diag);
|
||||
};
|
||||
QWidget *widget = ClangDiagnosticWidget::createWidget(
|
||||
{m_diagnostic}, ClangDiagnosticWidget::ToolTip,
|
||||
color() == Utils::Theme::Color::IconsDisabledColor ? nullptr : m_diagMgr);
|
||||
{m_diagnostic}, ClangDiagnosticWidget::ToolTip, canApplyFixIt);
|
||||
target->addWidget(widget);
|
||||
|
||||
return true;
|
||||
@@ -287,5 +294,113 @@ void ClangTextMark::removedFromEditor()
|
||||
m_removedFromEditorHandler(this);
|
||||
}
|
||||
|
||||
ClangBackEnd::DiagnosticSeverity convertSeverity(DiagnosticSeverity src)
|
||||
{
|
||||
if (src == DiagnosticSeverity::Error)
|
||||
return ClangBackEnd::DiagnosticSeverity::Error;
|
||||
if (src == DiagnosticSeverity::Warning)
|
||||
return ClangBackEnd::DiagnosticSeverity::Warning;
|
||||
return ClangBackEnd::DiagnosticSeverity::Note;
|
||||
}
|
||||
|
||||
ClangBackEnd::SourceRangeContainer convertRange(const FilePath &filePath, const Range &src)
|
||||
{
|
||||
const ClangBackEnd::SourceLocationContainer start(filePath.toString(), src.start().line() + 1,
|
||||
src.start().character() + 1);
|
||||
const ClangBackEnd::SourceLocationContainer end(filePath.toString(), src.end().line() + 1,
|
||||
src.end().character() + 1);
|
||||
return ClangBackEnd::SourceRangeContainer(start, end);
|
||||
}
|
||||
|
||||
ClangBackEnd::DiagnosticContainer convertDiagnostic(const ClangdDiagnostic &src,
|
||||
const FilePath &filePath)
|
||||
{
|
||||
ClangBackEnd::DiagnosticContainer target;
|
||||
target.ranges.append(convertRange(filePath, src.range()));
|
||||
target.location = target.ranges.first().start;
|
||||
target.text = src.message();
|
||||
target.category = src.category();
|
||||
if (src.severity())
|
||||
target.severity = convertSeverity(*src.severity());
|
||||
const Diagnostic::Code code = src.code().value_or(Diagnostic::Code());
|
||||
const QString * const codeString = Utils::get_if<QString>(&code);
|
||||
if (codeString && codeString->startsWith("-W"))
|
||||
target.enableOption = *codeString;
|
||||
for (const CodeAction &codeAction : src.codeActions().value_or(QList<CodeAction>())) {
|
||||
const Utils::optional<WorkspaceEdit> edit = codeAction.edit();
|
||||
if (!edit)
|
||||
continue;
|
||||
const Utils::optional<WorkspaceEdit::Changes> changes = edit->changes();
|
||||
if (!changes)
|
||||
continue;
|
||||
for (auto it = changes->cbegin(); it != changes->cend(); ++it) {
|
||||
for (const TextEdit &textEdit : it.value()) {
|
||||
target.fixIts << ClangBackEnd::FixItContainer(textEdit.newText(),
|
||||
convertRange(it.key().toFilePath(), textEdit.range()));
|
||||
}
|
||||
}
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
ClangdTextMark::ClangdTextMark(const FilePath &filePath,
|
||||
const Diagnostic &diagnostic,
|
||||
const Client *client)
|
||||
: TextEditor::TextMark(filePath, int(diagnostic.range().start().line() + 1), client->id())
|
||||
, m_lspDiagnostic(diagnostic)
|
||||
, m_diagnostic(convertDiagnostic(ClangdDiagnostic(diagnostic), filePath))
|
||||
, m_client(client)
|
||||
{
|
||||
setSettingsPage(CppTools::Constants::CPP_CODE_MODEL_SETTINGS_ID);
|
||||
|
||||
const bool isError = diagnostic.severity()
|
||||
&& *diagnostic.severity() == DiagnosticSeverity::Error;
|
||||
setDefaultToolTip(isError ? tr("Code Model Error") : tr("Code Model Warning"));
|
||||
setPriority(isError ? TextEditor::TextMark::HighPriority
|
||||
: TextEditor::TextMark::NormalPriority);
|
||||
setIcon(isError ? Icons::CODEMODEL_ERROR.icon() : Icons::CODEMODEL_WARNING.icon());
|
||||
setLineAnnotation(diagnostic.message());
|
||||
setColor(isError ? Theme::CodeModel_Error_TextMarkColor
|
||||
: Theme::CodeModel_Warning_TextMarkColor);
|
||||
|
||||
// Copy to clipboard action
|
||||
QVector<QAction *> actions;
|
||||
QAction *action = new QAction();
|
||||
action->setIcon(QIcon::fromTheme("edit-copy", Icons::COPY.icon()));
|
||||
action->setToolTip(tr("Clang Code Model Marks", "Copy to Clipboard"));
|
||||
QObject::connect(action, &QAction::triggered, [diag = m_diagnostic]() {
|
||||
const QString text = ClangDiagnosticWidget::createText({diag},
|
||||
ClangDiagnosticWidget::InfoBar);
|
||||
QApplication::clipboard()->setText(text, QClipboard::Clipboard);
|
||||
});
|
||||
actions << action;
|
||||
|
||||
// Remove diagnostic warning action
|
||||
ProjectExplorer::Project *project = projectForCurrentEditor();
|
||||
if (project && isDiagnosticConfigChangable(project, m_diagnostic)) {
|
||||
action = new QAction();
|
||||
action->setIcon(Icons::BROKEN.icon());
|
||||
action->setToolTip(tr("Disable Diagnostic in Current Project"));
|
||||
QObject::connect(action, &QAction::triggered, [diag = m_diagnostic]() {
|
||||
disableDiagnosticInCurrentProjectConfig(diag);
|
||||
});
|
||||
actions << action;
|
||||
}
|
||||
|
||||
setActions(actions);
|
||||
|
||||
ClangDiagnosticManager::addTask(m_diagnostic);
|
||||
}
|
||||
|
||||
bool ClangdTextMark::addToolTipContent(QLayout *target) const
|
||||
{
|
||||
const auto canApplyFixIt = [c = QPointer(m_client), diag = m_lspDiagnostic, fp = fileName()] {
|
||||
return c && c->reachable() && c->hasDiagnostic(DocumentUri::fromFilePath(fp), diag);
|
||||
};
|
||||
target->addWidget(ClangDiagnosticWidget::createWidget({m_diagnostic},
|
||||
ClangDiagnosticWidget::ToolTip, canApplyFixIt));
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace ClangCodeModel
|
||||
|
||||
@@ -28,10 +28,14 @@
|
||||
#include <clangsupport_global.h>
|
||||
#include <clangsupport/diagnosticcontainer.h>
|
||||
|
||||
#include <languageserverprotocol/lsptypes.h>
|
||||
|
||||
#include <texteditor/textmark.h>
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace LanguageClient { class Client; }
|
||||
|
||||
namespace ClangCodeModel {
|
||||
namespace Internal {
|
||||
class ClangDiagnosticManager;
|
||||
@@ -60,5 +64,21 @@ private:
|
||||
const ClangDiagnosticManager * const m_diagMgr;
|
||||
};
|
||||
|
||||
class ClangdTextMark : public TextEditor::TextMark
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(ClangdTextMark)
|
||||
public:
|
||||
ClangdTextMark(const ::Utils::FilePath &filePath,
|
||||
const LanguageServerProtocol::Diagnostic &diagnostic,
|
||||
const LanguageClient::Client *client);
|
||||
|
||||
private:
|
||||
bool addToolTipContent(QLayout *target) const override;
|
||||
|
||||
const LanguageServerProtocol::Diagnostic m_lspDiagnostic;
|
||||
const ClangBackEnd::DiagnosticContainer m_diagnostic;
|
||||
const LanguageClient::Client * const m_client;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace ClangCodeModel
|
||||
|
||||
@@ -52,7 +52,6 @@
|
||||
#include <QDebug>
|
||||
#include <QtTest>
|
||||
|
||||
using namespace ClangBackEnd;
|
||||
using namespace ClangCodeModel;
|
||||
using namespace ClangCodeModel::Internal;
|
||||
|
||||
|
||||
@@ -991,6 +991,18 @@ QList<Diagnostic> Client::diagnosticsAt(const DocumentUri &uri, const QTextCurso
|
||||
return m_diagnosticManager.diagnosticsAt(uri, cursor);
|
||||
}
|
||||
|
||||
bool Client::hasDiagnostic(const LanguageServerProtocol::DocumentUri &uri,
|
||||
const LanguageServerProtocol::Diagnostic &diag) const
|
||||
{
|
||||
return m_diagnosticManager.hasDiagnostic(uri, documentForFilePath(uri.toFilePath()), diag);
|
||||
}
|
||||
|
||||
void Client::setDiagnosticsHandlers(const TextMarkCreator &textMarkCreator,
|
||||
const HideDiagnosticsHandler &hideHandler)
|
||||
{
|
||||
m_diagnosticManager.setDiagnosticsHandlers(textMarkCreator, hideHandler);
|
||||
}
|
||||
|
||||
void Client::start()
|
||||
{
|
||||
if (m_clientInterface->start())
|
||||
|
||||
@@ -170,6 +170,10 @@ public:
|
||||
QList<LanguageServerProtocol::Diagnostic> diagnosticsAt(
|
||||
const LanguageServerProtocol::DocumentUri &uri,
|
||||
const QTextCursor &cursor) const;
|
||||
bool hasDiagnostic(const LanguageServerProtocol::DocumentUri &uri,
|
||||
const LanguageServerProtocol::Diagnostic &diag) const;
|
||||
void setDiagnosticsHandlers(const TextMarkCreator &textMarkCreator,
|
||||
const HideDiagnosticsHandler &hideHandler);
|
||||
|
||||
// logging
|
||||
void log(const QString &message) const;
|
||||
@@ -189,6 +193,7 @@ protected:
|
||||
void setProgressTitleForToken(const LanguageServerProtocol::ProgressToken &token,
|
||||
const QString &message);
|
||||
void handleMessage(const LanguageServerProtocol::BaseMessage &message);
|
||||
virtual void handleDiagnostics(const LanguageServerProtocol::PublishDiagnosticsParams ¶ms);
|
||||
|
||||
private:
|
||||
void handleResponse(const LanguageServerProtocol::MessageId &id, const QByteArray &content,
|
||||
@@ -196,7 +201,6 @@ private:
|
||||
void handleMethod(const QString &method, const LanguageServerProtocol::MessageId &id,
|
||||
const LanguageServerProtocol::IContent *content);
|
||||
|
||||
void handleDiagnostics(const LanguageServerProtocol::PublishDiagnosticsParams ¶ms);
|
||||
void handleSemanticHighlight(const LanguageServerProtocol::SemanticHighlightingParams ¶ms);
|
||||
|
||||
void initializeCallback(const LanguageServerProtocol::InitializeRequest::Response &initResponse);
|
||||
|
||||
@@ -91,6 +91,8 @@ void DiagnosticManager::hideDiagnostics(TextDocument *doc)
|
||||
if (!doc)
|
||||
return;
|
||||
|
||||
if (m_hideHandler)
|
||||
m_hideHandler();
|
||||
for (BaseTextEditor *editor : BaseTextEditor::textEditorsForDocument(doc))
|
||||
editor->editorWidget()->setExtraSelections(TextEditorWidget::CodeWarningsSelection, {});
|
||||
qDeleteAll(Utils::filtered(doc->marks(), Utils::equal(&TextMark::category, m_clientId)));
|
||||
@@ -127,6 +129,12 @@ void DiagnosticManager::showDiagnostics(const DocumentUri &uri, int version)
|
||||
const auto icon = QIcon::fromTheme("edit-copy", Utils::Icons::COPY.icon());
|
||||
const QString tooltip = tr("Copy to Clipboard");
|
||||
for (const Diagnostic &diagnostic : versionedDiagnostics.diagnostics) {
|
||||
extraSelections << toDiagnosticsSelections(diagnostic, doc->document());
|
||||
if (m_textMarkCreator) {
|
||||
doc->addMark(m_textMarkCreator(filePath, diagnostic));
|
||||
continue;
|
||||
}
|
||||
|
||||
QAction *action = new QAction();
|
||||
action->setIcon(icon);
|
||||
action->setToolTip(tooltip);
|
||||
@@ -137,7 +145,6 @@ void DiagnosticManager::showDiagnostics(const DocumentUri &uri, int version)
|
||||
mark->setActions({action});
|
||||
|
||||
doc->addMark(mark);
|
||||
extraSelections << toDiagnosticsSelections(diagnostic, doc->document());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,4 +175,26 @@ QList<Diagnostic> DiagnosticManager::diagnosticsAt(const DocumentUri &uri,
|
||||
});
|
||||
}
|
||||
|
||||
bool DiagnosticManager::hasDiagnostic(const LanguageServerProtocol::DocumentUri &uri,
|
||||
const TextDocument *doc,
|
||||
const LanguageServerProtocol::Diagnostic &diag) const
|
||||
{
|
||||
if (!doc)
|
||||
return false;
|
||||
const auto it = m_diagnostics.find(uri);
|
||||
if (it == m_diagnostics.end())
|
||||
return {};
|
||||
const int revision = doc->document()->revision();
|
||||
if (revision != it->version.value_or(revision))
|
||||
return false;
|
||||
return it->diagnostics.contains(diag);
|
||||
}
|
||||
|
||||
void DiagnosticManager::setDiagnosticsHandlers(const TextMarkCreator &textMarkCreator,
|
||||
const HideDiagnosticsHandler &removalHandler)
|
||||
{
|
||||
m_textMarkCreator = textMarkCreator;
|
||||
m_hideHandler = removalHandler;
|
||||
}
|
||||
|
||||
} // namespace LanguageClient
|
||||
|
||||
@@ -31,10 +31,19 @@
|
||||
|
||||
#include <QMap>
|
||||
|
||||
namespace TextEditor { class TextDocument; }
|
||||
#include <functional>
|
||||
|
||||
namespace TextEditor {
|
||||
class TextDocument;
|
||||
class TextMark;
|
||||
}
|
||||
|
||||
namespace LanguageClient {
|
||||
|
||||
using TextMarkCreator = std::function<TextEditor::TextMark *(const Utils::FilePath &,
|
||||
const LanguageServerProtocol::Diagnostic &)>;
|
||||
using HideDiagnosticsHandler = std::function<void()>;
|
||||
|
||||
class DiagnosticManager
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(LanguageClient::DiagnosticManager)
|
||||
@@ -55,6 +64,12 @@ public:
|
||||
QList<LanguageServerProtocol::Diagnostic> diagnosticsAt(
|
||||
const LanguageServerProtocol::DocumentUri &uri,
|
||||
const QTextCursor &cursor) const;
|
||||
bool hasDiagnostic(const LanguageServerProtocol::DocumentUri &uri,
|
||||
const TextEditor::TextDocument *doc,
|
||||
const LanguageServerProtocol::Diagnostic &diag) const;
|
||||
|
||||
void setDiagnosticsHandlers(const TextMarkCreator &shownHandler,
|
||||
const HideDiagnosticsHandler &removalHandler);
|
||||
|
||||
private:
|
||||
struct VersionedDiagnostics {
|
||||
@@ -63,6 +78,8 @@ private:
|
||||
};
|
||||
QMap<LanguageServerProtocol::DocumentUri, VersionedDiagnostics> m_diagnostics;
|
||||
Utils::Id m_clientId;
|
||||
TextMarkCreator m_textMarkCreator;
|
||||
HideDiagnosticsHandler m_hideHandler;
|
||||
};
|
||||
|
||||
} // namespace LanguageClient
|
||||
|
||||
Reference in New Issue
Block a user