Changelog for lsp-1.0.0.0

Revision history for lsp

1.0.0.0

1.0.0.0 is a major rework with both internal and external facing changes, and will require manual migration.

### Type safety

There are three types of concrete messages, NotificationMessage, RequestMessage and ResponseMessage. They are parameterised by their Method, which determines what type their parameters or response result must be.

data RequestMessage (m :: Method f Request) = RequestMessage
    { _jsonrpc :: Text
    , _id      :: LspId m
    , _method  :: SMethod m
    , _params  :: MessageParams m
    }

A Method in turn is parameterised by whether it originates from the client or the server, and whether it is used for notifications or requests:

TextDocumentFoldingRange           :: Method FromClient Request
TextDocumentSelectionRange         :: Method FromClient Request
WindowShowMessage                  :: Method FromServer Notification
WindowShowMessageRequest           :: Method FromServer Request

Each Method also has a singleton counterpart which allows it to be used at the term level, for example in RequestMessage._method:

STextDocumentFoldingRange           :: SMethod TextDocumentFoldingRange
STextDocumentSelectionRange         :: SMethod TextDocumentSelectionRange

SWindowShowMessage                  :: SMethod WindowShowMessage
SWindowShowMessageRequest           :: SMethod WindowShowMessageRequest

The type families MessageParams and ResponseResult map each Method to the appropriate type to be used in a response:

ResponseResult TextDocumentRename            = WorkspaceEdit
ResponseResult TextDocumentPrepareRename     = Range |? RangeWithPlaceholder

Also new is the |? type which represents union types in TypeScript, and is used throughout the specification where a field can accept several different types.

As an example of this in action, the types of your handlers will now depend on whether or not they are a request or a notification. They will pass along the precise type for the parameters the method you are handling, and in the case of a request handler, will expect that the response you give back is of the correct type as well.

type family Handler (f :: Type -> Type) (m :: Method from t) = (result :: Type) | result -> f t m where
  Handler f (m :: Method _from Request)      = RequestMessage m -> (Either ResponseError (ResponseResult m) -> f ()) -> f ()
  Handler f (m :: Method _from Notification) = NotificationMessage m -> f ()

LspT

LspFuncs has been removed and instead functionality is exposed through functions in the MonadLsp class.

getVirtualFile :: MonadLsp config m => NormalizedUri -> m (Maybe VirtualFile)
sendRequest :: forall (m :: Method FromServer Request) f config. MonadLsp config f
            => SServerMethod m
            -> MessageParams m
            -> (Either ResponseError (ResponseResult m) -> f ())
            -> f (LspId m)

It is parameterised over the server's LSP configuration type and the underlying monad. We recommend that you build your own monad for your server on top of the LspT transformer, so it will automatically become an instance of MonadLsp.

Inside the new ServerDefinition data type which gets passed to runServer, you need to specify how to convert from IO to your monad and back in interpretHandler so that lsp can execute your monad inside the handlers. You can use the result returned from doInitialize to pass along the LanguageContextEnv needed to run an LspT, as well as anything else your monad needs.

type 
ServerDefinition { ...
, doInitialize = \env _req -> pure $ Right env
, interpretHandler = \env -> Iso 
   (runLspT env) -- how to convert from IO ~> m
   liftIO        -- how to convert from m ~> IO
}

Steps to migrate

  1. In your .cabal file change any haskell-lsp dependencies to lsp
  2. Replace your existing imports with Haskell.LSP.Server
  3. If necessary define your own monad and fill in interpretHandler
  4. Migrate your handlers to use notificationHandler and requestHandler, passing along the corresponding SMethod (See example/Simple.hs)
  5. Remove any storage/use of LspFuncs and instead call the corresponding functions directly from your monad instead of IO

0.23.0.0

0.22.0.0

0.21.0.0

0.20.0.1

0.20.0.0

0.19.0.0 -- 2019-12-14

0.18.0.0 -- 2019-11-17

0.17.0.0 -- 2019-10-18

0.16.0.0 -- 2019-09-07

0.15.0.0 -- 2019-07-01

0.14.0.0 -- 2019-06-13

0.13.0.0 -- 2019-05-18

0.12.1.0 -- 2019-05-08

    , persistVirtualFileFunc       :: !(J.Uri -> IO FilePath)
    , reverseFileMapFunc           :: !(IO (FilePath -> FilePath))

0.12.0.0 -- 2019-05-05

0.11.0.0 -- 2019-04-28

0.10.0.0 -- 2019-04-22

0.9.0.0

0.8.2.0 -- 2019-04-11

0.8.1.0 -- 2019-02-28

0.8.0.1 -- 2018-10-27

0.8.0.0 -- 2018-09-08

0.7.0.0 -- 2018-08-14

0.6.0.0 -- 2018-08-06

0.5.0.0 -- 2018-08-03

0.4.0.0 -- 2018-07-10

0.3.0.0

0.2.3.0 -- 2018-99-99

0.2.2.0 -- 2018-05-04

0.2.1.0 -- 2018-05-02

0.2.0.1 -- 2017-12-27

0.2.0.0 -- 2017-11-23

0.1.0.0 -- 2017-07-19