Skip to content

Conversation

MangoIV
Copy link
Contributor

@MangoIV MangoIV commented Aug 8, 2025

Allow the user to set up an lsp server that talks to the user using the websocket protocol instead of standard IO and do enough refactoring to make the setup with websockets possible

@MangoIV MangoIV changed the title Add support for setting up language servers using to use websockets Add support for setting up language servers to use websockets Aug 9, 2025
@MangoIV
Copy link
Contributor Author

MangoIV commented Aug 9, 2025

I have tried to make as few breaking changes as possible (I don't think there are any) except that I changed from BS.ByteString and BSL.ByteString (which are not visible in the haddock) to BS.StrictByteString and BSL.LazyByteString (which are visible in the haddock) Unfortunately these are very recent additions, so let me know if I should revert them for the sake of better backwards compatibility with older bytestring versions.

@MangoIV MangoIV force-pushed the mangoiv/websocket branch from 679c8f1 to e07c7e3 Compare August 9, 2025 10:30
@MangoIV MangoIV marked this pull request as ready for review September 17, 2025 11:06
Copy link
Collaborator

@michaelpj michaelpj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks fine to me.


instance Pretty WebsocketLog where
pretty l = case l of
WebsocketPing -> "Ping"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should some of these include extra information beyond the constant string?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just thought about it, but nothing interesting comes to mind - I think this should purely exist to get status information of the websocket, the rest should be logged elsewhere

@derekstavis
Copy link

Hello 👋 is there anything I could help with to get this one merged? This is going to be quite useful for a project I'm working on!

@MangoIV
Copy link
Contributor Author

MangoIV commented Oct 14, 2025

Not really. I just came back from holidays and gotta finish this :)

@MangoIV
Copy link
Contributor Author

MangoIV commented Oct 14, 2025

It’s usable though, you can pin it to this revision

@derekstavis
Copy link

I'm testing it here and it doesn't seem to reply to any messages - it does read incoming requests but does not send a reply.

@MangoIV
Copy link
Contributor Author

MangoIV commented Oct 15, 2025

You probably need

defaultOptions
{ optTextDocumentSync =
Just
TextDocumentSyncOptions
{ _openClose = Just True
, _willSaveWaitUntil = Just False
, _willSave = Just False
, _save = Just $ InR $ SaveOptions $ Just False
, _change = Just TextDocumentSyncKind_Incremental
}

Do you already have this?

@derekstavis
Copy link

Yep. Here's my server definition - confirmed to work with the stdin transport:

serverDefinition state =
  ServerDefinition
    { defaultConfig = defaultLspConfig
    , configSection = "mylang"
    , parseConfig = \_oldConfig newConfigValue -> case fromJSON newConfigValue of
        Success cfg -> Right cfg
        Error err -> Left (T.pack err)
    , onConfigChange = liftIO . atomically . writeTVar (config state)
    , doInitialize = \env _req -> pure $ Right env
    , staticHandlers = const $ lspHandlers state
    , interpretHandler = \env -> Iso (runLspT env) liftIO
    , options =
        S.defaultOptions
          { optTextDocumentSync =
              Just $
                LSP.TextDocumentSyncOptions
                  { _openClose = Just True
                  , _change = Just LSP.TextDocumentSyncKind_Full
                  , _willSave = Just False
                  , _willSaveWaitUntil = Just False
                  , _save = Just $ InR $ LSP.SaveOptions $ Just False
                  }
          }
    }

The web socket message is being sent and received:

image

Logs:

[Debug] Websocket: New connection established
[Info] Starting server
[Debug] Websocket: Received request

@MangoIV
Copy link
Contributor Author

MangoIV commented Oct 15, 2025

Alright, imma look into it tomorrow, thanks for testing.

@MangoIV MangoIV marked this pull request as draft October 16, 2025 09:52
@MangoIV
Copy link
Contributor Author

MangoIV commented Oct 16, 2025

you are right, there must be something gone wrong when copy-pasting the working definition over - I'm investigating.

@MangoIV
Copy link
Contributor Author

MangoIV commented Oct 16, 2025

i found the reason - parse takeByteString always returns partial - I somehow need to mark the end of websocket request

@derekstavis
Copy link

My understanding is that stdio can be chunked but WebSockets are not, this means that we can just trust that whatever the websockets package gives is the full packet.

I'm not a Haskeller so I asked Claude to start from scratch basing off of this PR's changes and this was the result. Now I get the responses accordingly.

@MangoIV
Copy link
Contributor Author

MangoIV commented Oct 17, 2025

this means that we can just trust that whatever the websockets package gives is the full packet.

That is correct. I just want to figure out the best way to do this. I know how to „make it work“ that is not the problem. If you want to use whatever the LLM spat out, then that is not relevant to my PR :)

@MangoIV MangoIV marked this pull request as ready for review October 17, 2025 21:34
Allow the user to set up an lsp server that talks to the user using
the websocket protocol instead of standard IO and do enough refactoring
to make the setup with websockets possible

Whenever a new message arrives on the websocket, push the new message as
well as an empty message as the followup - additionally, do not throw an
error when the parser encounters an empty ByteString, it should be able
to handle that by itself.

Link the lspAsync to cascade a dead server to its sockets
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants