[utilities/kate] addons/lspclient: lspclient: add support for pull diagnostics

Christoph Cullmann <[email protected]>
Newsgroups gmane.comp.kde.cvs
Message-ID <[email protected]>
Git commit eee1b3ea340d75942a1d408da721ef7532c38843 by Christoph Cullmann, on behalf of leia uwu.
Committed on 18/08/2026 at 18:04.
Pushed by cullmann into branch 'master'.

lspclient: add support for pull diagnostics

M  +28   -0    addons/lspclient/lspclientpluginview.cpp
M  +12   -0    addons/lspclient/lspclientprotocol.h
M  +35   -0    addons/lspclient/lspclientserver.cpp
M  +3    -0    addons/lspclient/lspclientserver.h

https://invent.kde.org/utilities/kate/-/commit/eee1b3ea340d75942a1d408da721ef7532c38843

diff --git a/addons/lspclient/lspclientpluginview.cpp b/addons/lspclient/lspclientpluginview.cpp
index 90a11bdf59..b0e34d7f45 100644
--- a/addons/lspclient/lspclientpluginview.cpp
+++ b/addons/lspclient/lspclientpluginview.cpp
@@ -416,6 +416,9 @@ class LSPClientPluginViewImpl : public QObject, public KXMLGUIClient
             , m_lsp(lsp)
         {
             name = i18n("LSP");
+
+            m_viewTracker.reset(LSPClientViewTracker::new_(mainWin, 250, 0));
+            connect(m_viewTracker.get(), &LSPClientViewTracker::newState, this, &LSPDiagnosticProvider::onViewState);
         }
 
         QJsonObject suppressions(KTextEditor::Document *doc) const override
@@ -428,8 +431,33 @@ class LSPClientPluginViewImpl : public QObject, public KXMLGUIClient
             return {};
         }
 
+        void onViewState(KTextEditor::View *view, LSPClientViewTracker::State state)
+        {
+            // ignore cursor change events, we shouldn't even receive them because the motion timer is 0
+            // and the tracker ignores motion events if its 0, but just to be safe :)
+            if (state == LSPClientViewTracker::State::LineChanged)
+                return;
+
+            auto server = m_lsp->m_serverManager->findServer(view);
+            if (server && server->capabilities().diagnosticProvider) {
+                auto url = view->document()->url();
+
+                server->documentDiagnostic(url, this, [url, this](const LSPPullDiagnosticParams &params) {
+                    if (params.kind == LSPPullDiagnosticKind::Unchanged)
+                        return;
+
+                    FileDiagnostics diagnostics{
+                        .uri = url,
+                        .diagnostics = params.items,
+                    };
+                    Q_EMIT diagnosticsAdded(diagnostics);
+                });
+            }
+        }
+
     private:
         LSPClientPluginViewImpl *m_lsp;
+        std::unique_ptr<LSPClientViewTracker> m_viewTracker;
     };
 
     LSPDiagnosticProvider m_diagnosticProvider;
diff --git a/addons/lspclient/lspclientprotocol.h b/addons/lspclient/lspclientprotocol.h
index 17f97590ef..4cd9e2fa3e 100644
--- a/addons/lspclient/lspclientprotocol.h
+++ b/addons/lspclient/lspclientprotocol.h
@@ -121,6 +121,7 @@ struct LSPServerCapabilities {
     LSPWorkspaceFoldersServerCapabilities workspaceFolders;
     bool selectionRangeProvider = false;
     bool inlayHintProvider = false;
+    bool diagnosticProvider = false;
 };
 
 enum class LSPMarkupKind {
@@ -318,6 +319,17 @@ using LSPDiagnostic = Diagnostic;
 
 using LSPPublishDiagnosticsParams = FileDiagnostics;
 
+enum class LSPPullDiagnosticKind {
+    Full,
+    Unchanged
+};
+
+struct LSPPullDiagnosticParams {
+    LSPPullDiagnosticKind kind;
+    QString resultId;
+    QList<Diagnostic> items;
+};
+
 enum class LSPMessageType {
     Error = 1,
     Warning = 2,
diff --git a/addons/lspclient/lspclientserver.cpp b/addons/lspclient/lspclientserver.cpp
index e8f0c6d056..d2a5626639 100644
--- a/addons/lspclient/lspclientserver.cpp
+++ b/addons/lspclient/lspclientserver.cpp
@@ -539,6 +539,7 @@ static void from_json(LSPServerCapabilities &caps, const rapidjson::Value &json)
     from_json(caps.workspaceFolders, GetJsonObjectForKey(workspace, "workspaceFolders"));
     caps.selectionRangeProvider = json.HasMember("selectionRangeProvider");
     caps.inlayHintProvider = json.HasMember("inlayHintProvider");
+    caps.diagnosticProvider = json.HasMember("diagnosticProvider");
 }
 
 static QUrl urlFromRemote(const QString &s, bool normalize = true)
@@ -1243,6 +1244,24 @@ static LSPPublishDiagnosticsParams parseDiagnostics(const rapidjson::Value &resu
     return ret;
 }
 
+static LSPPullDiagnosticParams parsePullDiagnostics(const rapidjson::Value &result)
+{
+    LSPPullDiagnosticParams ret;
+
+    QString kind = GetStringValue(result, "kind");
+
+    ret.kind = kind == QStringLiteral("full") ? LSPPullDiagnosticKind::Full : LSPPullDiagnosticKind::Unchanged;
+
+    ret.resultId = GetStringValue(result, "resultId");
+
+    auto it = result.FindMember(MEMBER_ITEMS);
+    if (it != result.MemberEnd()) {
+        ret.items = parseDiagnosticsArray(it->value);
+    }
+
+    return ret;
+}
+
 static LSPApplyWorkspaceEditParams parseApplyWorkspaceEditParams(const rapidjson::Value &result)
 {
     LSPApplyWorkspaceEditParams ret;
@@ -1838,6 +1857,10 @@ private:
                                             }},
                                             {QLatin1String("inlayHint"), QJsonObject{
                                                 {QLatin1String("dynamicRegistration"), false}
+                                            }},
+                                            {QLatin1String("diagnostic"), QJsonObject{
+                                                {QLatin1String("dynamicRegistration"), false},
+                                                {QLatin1String("relatedInformation"), true}
                                             }}
                                         },
                                   },
@@ -2121,6 +2144,13 @@ public:
         return send(init_request(QStringLiteral("textDocument/inlayHint"), params), h);
     }
 
+    RequestHandle documentDiagnostic(const QUrl &document, const GenericReplyHandler &h)
+    {
+        PushCurrentServer g(q);
+        auto params = textDocumentParams(document);
+        return send(init_request(QStringLiteral("textDocument/diagnostic"), params), h);
+    }
+
     void executeCommand(const LSPCommand &command)
     {
         PushCurrentServer g(q);
@@ -2573,6 +2603,11 @@ LSPClientServer::documentInlayHint(const QUrl &document, const LSPRange &range,
     return d->documentInlayHint(document, range, make_handler(h, context, parseInlayHints));
 }
 
+LSPClientServer::RequestHandle LSPClientServer::documentDiagnostic(const QUrl &document, const QObject *context, const DiagnosticReplayHandler &h)
+{
+    return d->documentDiagnostic(document, make_handler(h, context, parsePullDiagnostics));
+}
+
 void LSPClientServer::executeCommand(const LSPCommand &command)
 {
     d->executeCommand(command);
diff --git a/addons/lspclient/lspclientserver.h b/addons/lspclient/lspclientserver.h
index 815a0c3ce0..5d293a6e95 100644
--- a/addons/lspclient/lspclientserver.h
+++ b/addons/lspclient/lspclientserver.h
@@ -74,6 +74,7 @@ using WorkspaceSymbolsReplyHandler = ReplyHandler<std::vector<LSPSymbolInformati
 using SelectionRangeReplyHandler = ReplyHandler<QList<std::shared_ptr<LSPSelectionRange>>>;
 using InlayHintsReplyHandler = ReplyHandler<std::vector<LSPInlayHint>>;
 using ConfigurationReplyHandler = ReplyHandler<QList<QJsonValue>>;
+using DiagnosticReplayHandler = ReplyHandler<LSPPullDiagnosticParams>;
 
 class LSPClientPlugin;
 
@@ -210,6 +211,8 @@ public:
 
     RequestHandle documentInlayHint(const QUrl &document, const LSPRange &range, const QObject *context, const InlayHintsReplyHandler &h);
 
+    RequestHandle documentDiagnostic(const QUrl &document, const QObject *context, const DiagnosticReplayHandler &h);
+
     void executeCommand(const LSPCommand &command);
 
     // rust-analyzer specific
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.