From de040fa53697b556ee27dd2e741cf7a62a3bac14 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 1 Apr 2026 04:50:23 +0000 Subject: [PATCH 1/2] Guard fsharp/ custom notifications with client capability check Only send Ionide-specific fsharp/ notifications (notifyWorkspace, notifyWorkspacePeek, fileParsed, documentAnalyzed, testDetected, etc.) when the client has sent Ionide configuration in InitializationOptions. Other LSP clients (Emacs, Helix, etc.) do not expect these custom fsharp/ notifications and may crash or behave unexpectedly when they receive them. Using the presence of Ionide InitializationOptions as the signal that the client is Ionide-compatible is reliable since Ionide always sends its FSharpConfigDto there. Fixes #1032 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../LspServers/AdaptiveFSharpLspServer.fs | 7 +++ .../LspServers/FSharpLspClient.fs | 53 +++++++++++++------ .../LspServers/FSharpLspClient.fsi | 2 + 3 files changed, 47 insertions(+), 15 deletions(-) diff --git a/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs b/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs index 0b3e55043..4065b4972 100644 --- a/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs +++ b/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs @@ -547,6 +547,13 @@ type AdaptiveFSharpLspServer state.ClientCapabilities <- Some p.Capabilities lspClient.ClientCapabilities <- Some p.Capabilities + // Enable Ionide-specific fsharp/ notifications only when the client sends + // Ionide configuration in InitializationOptions (a reliable Ionide client signal). + // Other LSP clients (Emacs, Helix, etc.) should not receive these notifications + // as they may crash or behave unexpectedly in response. + lspClient.SupportsCustomFSharpNotifications <- + p.InitializationOptions |> Option.exists (fun options -> options.HasValues) + state.DiagnosticCollections.ClientSupportsDiagnostics <- match p.Capabilities with | { TextDocument = Some { PublishDiagnostics = Some _ } } -> true diff --git a/src/FsAutoComplete/LspServers/FSharpLspClient.fs b/src/FsAutoComplete/LspServers/FSharpLspClient.fs index 8b5cabd41..6edfdfe57 100644 --- a/src/FsAutoComplete/LspServers/FSharpLspClient.fs +++ b/src/FsAutoComplete/LspServers/FSharpLspClient.fs @@ -19,6 +19,10 @@ type FSharpLspClient(sendServerNotification: ClientNotificationSender, sendServe member val ClientCapabilities: ClientCapabilities option = None with get, set + /// When true, the client supports Ionide-specific fsharp/ custom notifications. + /// Set to true during Initialize if the client sends Ionide configuration in InitializationOptions. + member val SupportsCustomFSharpNotifications: bool = false with get, set + override __.WindowShowMessage(p) = sendServerNotification "window/showMessage" (box p) |> Async.Ignore override __.WindowShowMessageRequest(p) = sendServerRequest.Send "window/showMessageRequest" (box p) @@ -44,23 +48,42 @@ type FSharpLspClient(sendServerNotification: ClientNotificationSender, sendServe sendServerNotification "textDocument/publishDiagnostics" (box p) |> Async.Ignore ///Custom notification for workspace/solution/project loading events - member __.NotifyWorkspace(p: PlainNotification) = - sendServerNotification "fsharp/notifyWorkspace" (box p) |> Async.Ignore + member x.NotifyWorkspace(p: PlainNotification) = + if x.SupportsCustomFSharpNotifications then + sendServerNotification "fsharp/notifyWorkspace" (box p) |> Async.Ignore + else + async { return () } ///Custom notification for initial workspace peek - member __.NotifyWorkspacePeek(p: PlainNotification) = - sendServerNotification "fsharp/notifyWorkspacePeek" (box p) |> Async.Ignore - - member __.NotifyCancelledRequest(p: PlainNotification) = - sendServerNotification "fsharp/notifyCancel" (box p) |> Async.Ignore - - member __.NotifyFileParsed(p: PlainNotification) = sendServerNotification "fsharp/fileParsed" (box p) |> Async.Ignore - - member __.NotifyDocumentAnalyzed(p: DocumentAnalyzedNotification) = - sendServerNotification "fsharp/documentAnalyzed" (box p) |> Async.Ignore - - member __.NotifyTestDetected(p: TestDetectedNotification) = - sendServerNotification "fsharp/testDetected" (box p) |> Async.Ignore + member x.NotifyWorkspacePeek(p: PlainNotification) = + if x.SupportsCustomFSharpNotifications then + sendServerNotification "fsharp/notifyWorkspacePeek" (box p) |> Async.Ignore + else + async { return () } + + member x.NotifyCancelledRequest(p: PlainNotification) = + if x.SupportsCustomFSharpNotifications then + sendServerNotification "fsharp/notifyCancel" (box p) |> Async.Ignore + else + async { return () } + + member x.NotifyFileParsed(p: PlainNotification) = + if x.SupportsCustomFSharpNotifications then + sendServerNotification "fsharp/fileParsed" (box p) |> Async.Ignore + else + async { return () } + + member x.NotifyDocumentAnalyzed(p: DocumentAnalyzedNotification) = + if x.SupportsCustomFSharpNotifications then + sendServerNotification "fsharp/documentAnalyzed" (box p) |> Async.Ignore + else + async { return () } + + member x.NotifyTestDetected(p: TestDetectedNotification) = + if x.SupportsCustomFSharpNotifications then + sendServerNotification "fsharp/testDetected" (box p) |> Async.Ignore + else + async { return () } member __.NotifyTestDiscoveryUpdate(p: TestDiscoveryUpdateNotification) = sendServerNotification "test/testDiscoveryUpdate" (box { Content = JsonSerializer.writeJson p }) diff --git a/src/FsAutoComplete/LspServers/FSharpLspClient.fsi b/src/FsAutoComplete/LspServers/FSharpLspClient.fsi index 0576df043..733e6786a 100644 --- a/src/FsAutoComplete/LspServers/FSharpLspClient.fsi +++ b/src/FsAutoComplete/LspServers/FSharpLspClient.fsi @@ -13,6 +13,8 @@ type FSharpLspClient = new: sendServerNotification: ClientNotificationSender * sendServerRequest: ClientRequestSender -> FSharpLspClient inherit LspClient member ClientCapabilities: ClientCapabilities option with get, set + /// When true, the client supports Ionide-specific fsharp/ custom notifications. + member SupportsCustomFSharpNotifications: bool with get, set override WindowShowMessage: ShowMessageParams -> Async override WindowShowMessageRequest: ShowMessageRequestParams -> AsyncLspResult override WindowLogMessage: LogMessageParams -> Async From 8ce387fc00e3d0594928dc0ce800688b927fffec Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 1 Apr 2026 04:50:26 +0000 Subject: [PATCH 2/2] ci: trigger checks