LanguageClient: pimpl DiagnosticManager

Change-Id: I4b5a2b0fe9818b676d08fd00f33b70366394539d
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
David Schulz
2024-03-22 09:48:16 +01:00
parent 7561612b3a
commit fc8f1bc47d
2 changed files with 60 additions and 49 deletions

View File

@@ -46,9 +46,35 @@ public:
} }
}; };
DiagnosticManager::DiagnosticManager(Client *client) struct VersionedDiagnostics
{
std::optional<int> version;
QList<LanguageServerProtocol::Diagnostic> diagnostics;
};
class Marks
{
public:
~Marks() { qDeleteAll(marks); }
bool enabled = true;
QList<TextEditor::TextMark *> marks;
};
class DiagnosticManager::DiagnosticManagerPrivate
{
public:
DiagnosticManagerPrivate(Client *client)
: m_client(client) : m_client(client)
, m_extraSelectionsId(TextEditorWidget::CodeWarningsSelection) {}
QMap<Utils::FilePath, VersionedDiagnostics> m_diagnostics;
QMap<Utils::FilePath, Marks> m_marks;
Client *m_client;
Utils::Id m_extraSelectionsId = TextEditorWidget::CodeWarningsSelection;
};
DiagnosticManager::DiagnosticManager(Client *client)
: d(std::make_unique<DiagnosticManagerPrivate>(client))
{ {
} }
@@ -62,16 +88,16 @@ void DiagnosticManager::setDiagnostics(const FilePath &filePath,
const std::optional<int> &version) const std::optional<int> &version)
{ {
hideDiagnostics(filePath); hideDiagnostics(filePath);
m_diagnostics[filePath] = {version, filteredDiagnostics(diagnostics)}; d->m_diagnostics[filePath] = {version, filteredDiagnostics(diagnostics)};
} }
void DiagnosticManager::hideDiagnostics(const Utils::FilePath &filePath) void DiagnosticManager::hideDiagnostics(const Utils::FilePath &filePath)
{ {
if (auto doc = TextDocument::textDocumentForFilePath(filePath)) { if (auto doc = TextDocument::textDocumentForFilePath(filePath)) {
for (BaseTextEditor *editor : BaseTextEditor::textEditorsForDocument(doc)) for (BaseTextEditor *editor : BaseTextEditor::textEditorsForDocument(doc))
editor->editorWidget()->setExtraSelections(m_extraSelectionsId, {}); editor->editorWidget()->setExtraSelections(d->m_extraSelectionsId, {});
} }
m_marks.remove(filePath); d->m_marks.remove(filePath);
} }
QList<Diagnostic> DiagnosticManager::filteredDiagnostics(const QList<Diagnostic> &diagnostics) const QList<Diagnostic> DiagnosticManager::filteredDiagnostics(const QList<Diagnostic> &diagnostics) const
@@ -82,7 +108,7 @@ QList<Diagnostic> DiagnosticManager::filteredDiagnostics(const QList<Diagnostic>
void DiagnosticManager::disableDiagnostics(TextEditor::TextDocument *document) void DiagnosticManager::disableDiagnostics(TextEditor::TextDocument *document)
{ {
Marks &marks = m_marks[document->filePath()]; Marks &marks = d->m_marks[document->filePath()];
if (!marks.enabled) if (!marks.enabled)
return; return;
for (TextEditor::TextMark *mark : marks.marks) for (TextEditor::TextMark *mark : marks.marks)
@@ -94,11 +120,11 @@ void DiagnosticManager::showDiagnostics(const FilePath &filePath, int version)
{ {
if (TextDocument *doc = TextDocument::textDocumentForFilePath(filePath)) { if (TextDocument *doc = TextDocument::textDocumentForFilePath(filePath)) {
QList<QTextEdit::ExtraSelection> extraSelections; QList<QTextEdit::ExtraSelection> extraSelections;
const VersionedDiagnostics &versionedDiagnostics = m_diagnostics.value(filePath); const VersionedDiagnostics &versionedDiagnostics = d->m_diagnostics.value(filePath);
if (versionedDiagnostics.version.value_or(version) == version if (versionedDiagnostics.version.value_or(version) == version
&& !versionedDiagnostics.diagnostics.isEmpty()) { && !versionedDiagnostics.diagnostics.isEmpty()) {
Marks &marks = m_marks[filePath]; Marks &marks = d->m_marks[filePath];
const bool isProjectFile = m_client->fileBelongsToProject(filePath); const bool isProjectFile = d->m_client->fileBelongsToProject(filePath);
for (const Diagnostic &diagnostic : versionedDiagnostics.diagnostics) { for (const Diagnostic &diagnostic : versionedDiagnostics.diagnostics) {
const QTextEdit::ExtraSelection selection const QTextEdit::ExtraSelection selection
= createDiagnosticSelection(diagnostic, doc->document()); = createDiagnosticSelection(diagnostic, doc->document());
@@ -112,17 +138,22 @@ void DiagnosticManager::showDiagnostics(const FilePath &filePath, int version)
} }
for (BaseTextEditor *editor : BaseTextEditor::textEditorsForDocument(doc)) for (BaseTextEditor *editor : BaseTextEditor::textEditorsForDocument(doc))
editor->editorWidget()->setExtraSelections(m_extraSelectionsId, extraSelections); editor->editorWidget()->setExtraSelections(d->m_extraSelectionsId, extraSelections);
} }
} }
Client *DiagnosticManager::client() const
{
return d->m_client;
}
TextEditor::TextMark *DiagnosticManager::createTextMark(TextDocument *doc, TextEditor::TextMark *DiagnosticManager::createTextMark(TextDocument *doc,
const Diagnostic &diagnostic, const Diagnostic &diagnostic,
bool /*isProjectFile*/) const bool /*isProjectFile*/) const
{ {
static const QIcon icon = Icon::fromTheme("edit-copy"); static const QIcon icon = Icon::fromTheme("edit-copy");
static const QString tooltip = Tr::tr("Copy to Clipboard"); static const QString tooltip = Tr::tr("Copy to Clipboard");
auto mark = new TextMark(doc, diagnostic, m_client); auto mark = new TextMark(doc, diagnostic, d->m_client);
mark->setActionsProvider([text = diagnostic.message()] { mark->setActionsProvider([text = diagnostic.message()] {
QAction *action = new QAction(); QAction *action = new QAction();
action->setIcon(icon); action->setIcon(icon);
@@ -153,13 +184,13 @@ QTextEdit::ExtraSelection DiagnosticManager::createDiagnosticSelection(
void DiagnosticManager::setExtraSelectionsId(const Utils::Id &extraSelectionsId) void DiagnosticManager::setExtraSelectionsId(const Utils::Id &extraSelectionsId)
{ {
// this function should be called before any diagnostics are handled // this function should be called before any diagnostics are handled
QTC_CHECK(m_diagnostics.isEmpty()); QTC_CHECK(d->m_diagnostics.isEmpty());
m_extraSelectionsId = extraSelectionsId; d->m_extraSelectionsId = extraSelectionsId;
} }
void DiagnosticManager::forAllMarks(std::function<void (TextEditor::TextMark *)> func) void DiagnosticManager::forAllMarks(std::function<void (TextEditor::TextMark *)> func)
{ {
for (const Marks &marks : std::as_const(m_marks)) { for (const Marks &marks : std::as_const(d->m_marks)) {
for (TextEditor::TextMark *mark : marks.marks) for (TextEditor::TextMark *mark : marks.marks)
func(mark); func(mark);
} }
@@ -167,18 +198,18 @@ void DiagnosticManager::forAllMarks(std::function<void (TextEditor::TextMark *)>
void DiagnosticManager::clearDiagnostics() void DiagnosticManager::clearDiagnostics()
{ {
for (const Utils::FilePath &path : m_diagnostics.keys()) for (const Utils::FilePath &path : d->m_diagnostics.keys())
hideDiagnostics(path); hideDiagnostics(path);
m_diagnostics.clear(); d->m_diagnostics.clear();
QTC_ASSERT(m_marks.isEmpty(), m_marks.clear()); QTC_ASSERT(d->m_marks.isEmpty(), d->m_marks.clear());
} }
QList<Diagnostic> DiagnosticManager::diagnosticsAt(const FilePath &filePath, QList<Diagnostic> DiagnosticManager::diagnosticsAt(const FilePath &filePath,
const QTextCursor &cursor) const const QTextCursor &cursor) const
{ {
const int documentRevision = m_client->documentVersion(filePath); const int documentRevision = d->m_client->documentVersion(filePath);
auto it = m_diagnostics.find(filePath); auto it = d->m_diagnostics.find(filePath);
if (it == m_diagnostics.end()) if (it == d->m_diagnostics.end())
return {}; return {};
if (documentRevision != it->version.value_or(documentRevision)) if (documentRevision != it->version.value_or(documentRevision))
return {}; return {};
@@ -193,10 +224,10 @@ bool DiagnosticManager::hasDiagnostic(const FilePath &filePath,
{ {
if (!doc) if (!doc)
return false; return false;
const auto it = m_diagnostics.find(filePath); const auto it = d->m_diagnostics.find(filePath);
if (it == m_diagnostics.end()) if (it == d->m_diagnostics.end())
return {}; return {};
const int revision = m_client->documentVersion(filePath); const int revision = d->m_client->documentVersion(filePath);
if (revision != it->version.value_or(revision)) if (revision != it->version.value_or(revision))
return false; return false;
return it->diagnostics.contains(diag); return it->diagnostics.contains(diag);
@@ -205,18 +236,13 @@ bool DiagnosticManager::hasDiagnostic(const FilePath &filePath,
bool DiagnosticManager::hasDiagnostics(const TextDocument *doc) const bool DiagnosticManager::hasDiagnostics(const TextDocument *doc) const
{ {
const FilePath docPath = doc->filePath(); const FilePath docPath = doc->filePath();
const auto it = m_diagnostics.find(docPath); const auto it = d->m_diagnostics.find(docPath);
if (it == m_diagnostics.end()) if (it == d->m_diagnostics.end())
return {}; return {};
const int revision = m_client->documentVersion(docPath); const int revision = d->m_client->documentVersion(docPath);
if (revision != it->version.value_or(revision)) if (revision != it->version.value_or(revision))
return false; return false;
return !it->diagnostics.isEmpty(); return !it->diagnostics.isEmpty();
} }
DiagnosticManager::Marks::~Marks()
{
qDeleteAll(marks);
}
} // namespace LanguageClient } // namespace LanguageClient

View File

@@ -54,7 +54,7 @@ signals:
void textMarkCreated(const Utils::FilePath &path); void textMarkCreated(const Utils::FilePath &path);
protected: protected:
Client *client() const { return m_client; } Client *client() const;
virtual TextEditor::TextMark *createTextMark(TextEditor::TextDocument *doc, virtual TextEditor::TextMark *createTextMark(TextEditor::TextDocument *doc,
const LanguageServerProtocol::Diagnostic &diagnostic, const LanguageServerProtocol::Diagnostic &diagnostic,
bool isProjectFile) const; bool isProjectFile) const;
@@ -64,24 +64,9 @@ protected:
void setExtraSelectionsId(const Utils::Id &extraSelectionsId); void setExtraSelectionsId(const Utils::Id &extraSelectionsId);
void forAllMarks(std::function<void (TextEditor::TextMark *)> func); void forAllMarks(std::function<void (TextEditor::TextMark *)> func);
private: private:
struct VersionedDiagnostics class DiagnosticManagerPrivate;
{ std::unique_ptr<DiagnosticManagerPrivate> d;
std::optional<int> version;
QList<LanguageServerProtocol::Diagnostic> diagnostics;
};
QMap<Utils::FilePath, VersionedDiagnostics> m_diagnostics;
class Marks
{
public:
~Marks();
bool enabled = true;
QList<TextEditor::TextMark *> marks;
};
QMap<Utils::FilePath, Marks> m_marks;
Client *m_client;
Utils::Id m_extraSelectionsId;
}; };
} // namespace LanguageClient } // namespace LanguageClient