Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use typed response errors #587

Merged
merged 1 commit into from
Jun 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions lsp-test/src/Language/LSP/Test.hs
Original file line number Diff line number Diff line change
Expand Up @@ -674,7 +674,7 @@ getDocumentSymbols doc = do
Right (InL xs) -> return (Left xs)
Right (InR (InL xs)) -> return (Right xs)
Right (InR (InR _)) -> return (Right [])
Left err -> throw (UnexpectedResponseError (SomeLspId $ fromJust rspLid) err)
Left err -> throw (UnexpectedResponseError (fromJust rspLid) err)

-- | Returns the code actions in the specified range.
getCodeActions :: TextDocumentIdentifier -> Range -> Session [Command |? CodeAction]
Expand All @@ -685,7 +685,7 @@ getCodeActions doc range = do
case rsp ^. L.result of
Right (InL xs) -> return xs
Right (InR _) -> return []
Left error -> throw (UnexpectedResponseError (SomeLspId $ fromJust $ rsp ^. L.id) error)
Left error -> throw (UnexpectedResponseError (fromJust $ rsp ^. L.id) error)

{- | Returns the code actions in the specified range, resolving any with
a non empty _data_ field.
Expand Down Expand Up @@ -713,7 +713,7 @@ getAllCodeActions doc = do
TResponseMessage _ rspLid res <- request SMethod_TextDocumentCodeAction (CodeActionParams Nothing Nothing doc (diag ^. L.range) ctx)

case res of
Left e -> throw (UnexpectedResponseError (SomeLspId $ fromJust rspLid) e)
Left e -> throw (UnexpectedResponseError (fromJust rspLid) e)
Right (InL cmdOrCAs) -> pure (acc ++ cmdOrCAs)
Right (InR _) -> pure acc

Expand Down Expand Up @@ -781,7 +781,7 @@ resolveCodeAction ca = do
rsp <- request SMethod_CodeActionResolve ca
case rsp ^. L.result of
Right ca -> return ca
Left er -> throw (UnexpectedResponseError (SomeLspId $ fromJust $ rsp ^. L.id) er)
Left er -> throw (UnexpectedResponseError (fromJust $ rsp ^. L.id) er)

{- | If a code action contains a _data_ field: resolves the code action, then
executes it. Otherwise, just executes it.
Expand Down Expand Up @@ -849,7 +849,7 @@ resolveCompletion ci = do
rsp <- request SMethod_CompletionItemResolve ci
case rsp ^. L.result of
Right ci -> return ci
Left error -> throw (UnexpectedResponseError (SomeLspId $ fromJust $ rsp ^. L.id) error)
Left error -> throw (UnexpectedResponseError (fromJust $ rsp ^. L.id) error)

-- | Returns the references for the position in the document.
getReferences ::
Expand Down Expand Up @@ -937,11 +937,11 @@ getHighlights doc pos =
{- | Checks the response for errors and throws an exception if needed.
Returns the result if successful.
-}
getResponseResult :: (ToJSON (ErrorData m)) => TResponseMessage m -> MessageResult m
getResponseResult :: (Show (ErrorData m)) => TResponseMessage m -> MessageResult m
getResponseResult rsp =
case rsp ^. L.result of
Right x -> x
Left err -> throw $ UnexpectedResponseError (SomeLspId $ fromJust $ rsp ^. L.id) err
Left err -> throw $ UnexpectedResponseError (fromJust $ rsp ^. L.id) err

-- | Applies formatting to the specified document.
formatDoc :: TextDocumentIdentifier -> FormattingOptions -> Session ()
Expand Down Expand Up @@ -984,7 +984,7 @@ resolveCodeLens cl = do
rsp <- request SMethod_CodeLensResolve cl
case rsp ^. L.result of
Right cl -> return cl
Left error -> throw (UnexpectedResponseError (SomeLspId $ fromJust $ rsp ^. L.id) error)
Left error -> throw (UnexpectedResponseError (fromJust $ rsp ^. L.id) error)

-- | Returns the inlay hints in the specified range.
getInlayHints :: TextDocumentIdentifier -> Range -> Session [InlayHint]
Expand All @@ -1006,7 +1006,7 @@ resolveInlayHint ih = do
rsp <- request SMethod_InlayHintResolve ih
case rsp ^. L.result of
Right ih -> return ih
Left error -> throw (UnexpectedResponseError (SomeLspId $ fromJust $ rsp ^. L.id) error)
Left error -> throw (UnexpectedResponseError (fromJust $ rsp ^. L.id) error)

-- | Pass a param and return the response from `prepareCallHierarchy`
prepareCallHierarchy :: CallHierarchyPrepareParams -> Session [CallHierarchyItem]
Expand All @@ -1021,7 +1021,7 @@ outgoingCalls = resolveRequestWithListResp SMethod_CallHierarchyOutgoingCalls
-- | Send a request and receive a response with list.
resolveRequestWithListResp ::
forall (m :: Method ClientToServer Request) a.
(ToJSON (ErrorData m), MessageResult m ~ ([a] |? Null)) =>
(Show (ErrorData m), MessageResult m ~ ([a] |? Null)) =>
SMethod m ->
MessageParams m ->
Session [a]
Expand Down
3 changes: 1 addition & 2 deletions lsp-test/src/Language/LSP/Test/Exceptions.hs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,10 @@ data SessionException
| ReplayOutOfOrder FromServerMessage [FromServerMessage]
| UnexpectedDiagnostics
| IncorrectApplyEditRequest String
| UnexpectedResponseError SomeLspId ResponseError
| forall m. Show (ErrorData m) => UnexpectedResponseError (LspId m) (TResponseError m)
| UnexpectedServerTermination
| IllegalInitSequenceMessage FromServerMessage
| MessageSendError Value IOError
deriving (Eq)

instance Exception SessionException

Expand Down
2 changes: 1 addition & 1 deletion lsp-test/src/Language/LSP/Test/Session.hs
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ updateStateC = awaitForever $ \msg -> do
sendMessage $ TResponseMessage "2.0" (Just $ r ^. L.id) $
if null errs
then Right configs
else Left $ ResponseError (InL LSPErrorCodes_RequestFailed) ("No configuration for requested sections: " <> T.pack (show errs)) Nothing
else Left $ TResponseError (InL LSPErrorCodes_RequestFailed) ("No configuration for requested sections: " <> T.pack (show errs)) Nothing
_ -> pure ()
unless (
(ignoringLogNotifications state && isLogNotification msg)
Expand Down
2 changes: 1 addition & 1 deletion lsp-test/test/DummyServer.hs
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ handlers =
, requestHandler SMethod_TextDocumentSemanticTokensFull $ \_req resp -> do
let tokens = makeSemanticTokens defaultSemanticTokensLegend [SemanticTokenAbsolute 0 1 2 SemanticTokenTypes_Type []]
case tokens of
Left t -> resp $ Left $ ResponseError (InR ErrorCodes_InternalError) t Nothing
Left t -> resp $ Left $ TResponseError (InR ErrorCodes_InternalError) t Nothing
Right tokens -> resp $ Right $ InL tokens
, requestHandler SMethod_TextDocumentInlayHint $ \req resp -> do
let TRequestMessage _ _ _ params = req
Expand Down
1 change: 1 addition & 0 deletions lsp-types/ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## Unreleased

- Add support for identifying client and server capabilities associated with a method.
- `TResponseMessage` now contains a `TResponseError` instead of a `ResponseError`

## 2.2.0.0 -- 2024-04-29

Expand Down
3 changes: 1 addition & 2 deletions lsp-types/src/Language/LSP/Protocol/Message/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,7 @@ toUntypedResponseError (TResponseError c m d) = ResponseError c m (fmap toJSON d
data TResponseMessage (m :: Method f Request) = TResponseMessage
{ _jsonrpc :: Text
, _id :: Maybe (LspId m)
, -- TODO: use `TResponseError m` for the error type, this will require quite a lot of adaptation downstream
_result :: Either ResponseError (MessageResult m)
, _result :: Either (TResponseError m) (MessageResult m)
}
deriving stock (Generic)

Expand Down
2 changes: 1 addition & 1 deletion lsp-types/test/JsonSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ spec = do
let input = "{\"jsonrpc\": \"2.0\", \"id\": 123, \"error\": { \"code\": -32700, \"message\": \"oh no\", \"data\": null }}"
in J.decode input
`shouldBe` Just
( (TResponseMessage "2.0" (Just (IdInt 123)) (Left $ ResponseError (InR ErrorCodes_ParseError) "oh no" (Just J.Null))) ::
( (TResponseMessage "2.0" (Just (IdInt 123)) (Left $ TResponseError (InR ErrorCodes_ParseError) "oh no" (Just J.Null))) ::
TResponseMessage ('Method_CustomMethod "hello")
)
it "throws if neither result nor error is present" $ do
Expand Down
1 change: 1 addition & 0 deletions lsp/ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

- Drop dependency on `uuid` and `random`
- Fix handling of `rootPath` in `intializeParams`
- Update to newer `lsp-types`

## 2.6.0.0

Expand Down
10 changes: 5 additions & 5 deletions lsp/src/Language/LSP/Server/Core.hs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ data LspCoreLog
NewConfig J.Value
| ConfigurationParseError J.Value T.Text
| ConfigurationNotSupported
| BadConfigurationResponse ResponseError
| BadConfigurationResponse (TResponseError Method_WorkspaceConfiguration)
| WrongConfigSections [J.Value]
| forall m. CantRegister (SMethod m)

Expand Down Expand Up @@ -177,7 +177,7 @@ newtype ClientMessageHandler f (t :: MessageKind) (m :: Method ClientToServer t)
from the server or client
-}
type family Handler (f :: Type -> Type) (m :: Method from t) = (result :: Type) | result -> f t m where
Handler f (m :: Method _from Request) = TRequestMessage m -> (Either ResponseError (MessageResult m) -> f ()) -> f ()
Handler f (m :: Method _from Request) = TRequestMessage m -> (Either (TResponseError m) (MessageResult m) -> f ()) -> f ()
Handler f (m :: Method _from Notification) = TNotificationMessage m -> f ()

-- | How to convert two isomorphic data structures between each other.
Expand Down Expand Up @@ -348,7 +348,7 @@ data ServerDefinition config = forall m a.
-- the new config. Servers that want to react to config changes should provide
-- a callback here, it is not sufficient to just add e.g. a @workspace/didChangeConfiguration@
-- handler.
, doInitialize :: LanguageContextEnv config -> TMessage Method_Initialize -> IO (Either ResponseError a)
, doInitialize :: LanguageContextEnv config -> TMessage Method_Initialize -> IO (Either (TResponseError Method_Initialize) a)
-- ^ Called *after* receiving the @initialize@ request and *before*
-- returning the response. This callback will be invoked to offer the
-- language server implementation the chance to create any processes or
Expand Down Expand Up @@ -383,7 +383,7 @@ data ServerDefinition config = forall m a.
request with either an error, or the response params.
-}
newtype ServerResponseCallback (m :: Method ServerToClient Request)
= ServerResponseCallback (Either ResponseError (MessageResult m) -> IO ())
= ServerResponseCallback (Either (TResponseError m) (MessageResult m) -> IO ())

{- | Return value signals if response handler was inserted successfully
Might fail if the id was already in the map
Expand Down Expand Up @@ -412,7 +412,7 @@ sendRequest ::
MonadLsp config f =>
SServerMethod m ->
MessageParams m ->
(Either ResponseError (MessageResult m) -> f ()) ->
(Either (TResponseError m) (MessageResult m) -> f ()) ->
f (LspId m)
sendRequest m params resHandler = do
reqId <- IdInt <$> freshLspId
Expand Down
10 changes: 5 additions & 5 deletions lsp/src/Language/LSP/Server/Processing.hs
Original file line number Diff line number Diff line change
Expand Up @@ -196,9 +196,9 @@ initializeRequestHandler logger ServerDefinition{..} vfs sendFunc req = do
makeResponseMessage rid result = TResponseMessage "2.0" (Just rid) (Right result)
makeResponseError origId err = TResponseMessage "2.0" (Just origId) (Left err)

initializeErrorHandler :: (ResponseError -> IO ()) -> E.SomeException -> IO (Maybe a)
initializeErrorHandler :: (TResponseError Method_Initialize -> IO ()) -> E.SomeException -> IO (Maybe a)
initializeErrorHandler sendResp e = do
sendResp $ ResponseError (InR ErrorCodes_InternalError) msg Nothing
sendResp $ TResponseError (InR ErrorCodes_InternalError) msg Nothing
pure Nothing
where
msg = T.pack $ unwords ["Error on initialize:", show e]
Expand Down Expand Up @@ -518,13 +518,13 @@ handle' logger mAction m msg = do
(Nothing, Just (ClientMessageHandler h)) -> Just h
(Nothing, Nothing) -> Nothing

sendResponse :: forall m1. TRequestMessage (m1 :: Method ClientToServer Request) -> Either ResponseError (MessageResult m1) -> m ()
sendResponse :: forall m1. TRequestMessage (m1 :: Method ClientToServer Request) -> Either (TResponseError m1) (MessageResult m1) -> m ()
sendResponse req res = sendToClient $ FromServerRsp (req ^. L.method) $ TResponseMessage "2.0" (Just (req ^. L.id)) res

requestDuringShutdown :: forall m1. TRequestMessage (m1 :: Method ClientToServer Request) -> m ()
requestDuringShutdown req = do
logger <& MessageDuringShutdown m `WithSeverity` Warning
sendResponse req (Left (ResponseError (InR ErrorCodes_InvalidRequest) "Server is shutdown" Nothing))
sendResponse req (Left (TResponseError (InR ErrorCodes_InvalidRequest) "Server is shutdown" Nothing))

notificationDuringShutdown :: m ()
notificationDuringShutdown = logger <& MessageDuringShutdown m `WithSeverity` Warning
Expand All @@ -541,7 +541,7 @@ handle' logger mAction m msg = do
missingRequestHandler req = do
logger <& MissingHandler False m `WithSeverity` Error
let errorMsg = T.pack $ unwords ["No handler for: ", show m]
err = ResponseError (InR ErrorCodes_MethodNotFound) errorMsg Nothing
err = TResponseError (InR ErrorCodes_MethodNotFound) errorMsg Nothing
sendResponse req (Left err)

progressCancelHandler :: (m ~ LspM config) => LogAction m (WithSeverity LspProcessingLog) -> TMessage Method_WindowWorkDoneProgressCancel -> m ()
Expand Down
Loading