{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TupleSections #-}
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE PolyKinds #-}
+{-# LANGUAGE KindSignatures #-}
+{-# LANGUAGE DataKinds #-}
module Language.Haskell.LSP.Test.Decoding where
import Prelude hiding ( id )
import Data.Aeson
+import Data.Aeson.Types
+import Data.Foldable
+import Data.Functor.Product
+import Data.Functor.Const
+import Control.Exception
import Control.Lens
import qualified Data.ByteString.Lazy.Char8 as B
import Data.Maybe
import System.IO
+import System.IO.Error
import Language.Haskell.LSP.Types
- hiding ( error )
-import Language.Haskell.LSP.Messages
-import qualified Data.HashMap.Strict as HM
+import Language.Haskell.LSP.Types.Lens
+import Language.Haskell.LSP.Test.Exceptions
+
+import Data.IxMap
+import Data.Kind
getAllMessages :: Handle -> IO [B.ByteString]
getAllMessages h = do
getNextMessage h = do
headers <- getHeaders h
case read . init <$> lookup "Content-Length" headers of
- Nothing -> error "Couldn't read Content-Length header"
+ Nothing -> throw NoContentLengthHeader
Just size -> B.hGet h size
addHeader :: B.ByteString -> B.ByteString
getHeaders :: Handle -> IO [(String, String)]
getHeaders h = do
- l <- hGetLine h
+ l <- catch (hGetLine h) eofHandler
let (name, val) = span (/= ':') l
if null val then return [] else ((name, drop 2 val) :) <$> getHeaders h
+ where eofHandler e
+ | isEOFError e = throw UnexpectedServerTermination
+ | otherwise = throw e
+
+type RequestMap = IxMap LspId (SMethod :: Method FromClient Request -> Type )
+
+newRequestMap :: RequestMap
+newRequestMap = emptyIxMap
-type RequestMap = HM.HashMap LspId FromClientMessage
+updateRequestMap :: RequestMap -> LspId m -> SClientMethod m -> Maybe RequestMap
+updateRequestMap reqMap id method = insertIxMap id method reqMap
getRequestMap :: [FromClientMessage] -> RequestMap
-getRequestMap = foldl helper HM.empty
+getRequestMap = foldl' helper emptyIxMap
where
+ helper :: RequestMap -> FromClientMessage -> RequestMap
helper acc msg = case msg of
- (ReqInitialize val) -> insert val msg acc
- (ReqShutdown val) -> insert val msg acc
- (ReqHover val) -> insert val msg acc
- (ReqCompletion val) -> insert val msg acc
- (ReqCompletionItemResolve val) -> insert val msg acc
- (ReqSignatureHelp val) -> insert val msg acc
- (ReqDefinition val) -> insert val msg acc
- (ReqFindReferences val) -> insert val msg acc
- (ReqDocumentHighlights val) -> insert val msg acc
- (ReqDocumentSymbols val) -> insert val msg acc
- (ReqWorkspaceSymbols val) -> insert val msg acc
- (ReqCodeAction val) -> insert val msg acc
- (ReqCodeLens val) -> insert val msg acc
- (ReqCodeLensResolve val) -> insert val msg acc
- (ReqDocumentFormatting val) -> insert val msg acc
- (ReqDocumentRangeFormatting val) -> insert val msg acc
- (ReqDocumentOnTypeFormatting val) -> insert val msg acc
- (ReqRename val) -> insert val msg acc
- (ReqExecuteCommand val) -> insert val msg acc
- (ReqDocumentLink val) -> insert val msg acc
- (ReqDocumentLinkResolve val) -> insert val msg acc
- (ReqWillSaveWaitUntil val) -> insert val msg acc
+ FromClientMess m mess -> case splitClientMethod m of
+ IsClientNot -> acc
+ IsClientReq -> fromJust $ updateRequestMap acc (mess ^. id) m
+ IsClientEither -> case mess of
+ NotMess _ -> acc
+ ReqMess msg -> fromJust $ updateRequestMap acc (msg ^. id) m
_ -> acc
- insert m = HM.insert (m ^. id)
-matchResponseMsgType :: FromClientMessage -> B.ByteString -> FromServerMessage
-matchResponseMsgType req bytes = case req of
- ReqInitialize _ -> RspInitialize $ fromJust $ decode bytes
- ReqShutdown _ -> RspShutdown $ fromJust $ decode bytes
- ReqHover _ -> RspHover $ fromJust $ decode bytes
- ReqCompletion _ -> RspCompletion $ fromJust $ decode bytes
- ReqCompletionItemResolve _ ->
- RspCompletionItemResolve $ fromJust $ decode bytes
- ReqSignatureHelp _ -> RspSignatureHelp $ fromJust $ decode bytes
- ReqDefinition _ -> RspDefinition $ fromJust $ decode bytes
- ReqFindReferences _ -> RspFindReferences $ fromJust $ decode bytes
- ReqDocumentHighlights _ -> RspDocumentHighlights $ fromJust $ decode bytes
- ReqDocumentSymbols _ -> RspDocumentSymbols $ fromJust $ decode bytes
- ReqWorkspaceSymbols _ -> RspWorkspaceSymbols $ fromJust $ decode bytes
- ReqCodeAction _ -> RspCodeAction $ fromJust $ decode bytes
- ReqCodeLens _ -> RspCodeLens $ fromJust $ decode bytes
- ReqCodeLensResolve _ -> RspCodeLensResolve $ fromJust $ decode bytes
- ReqDocumentFormatting _ -> RspDocumentFormatting $ fromJust $ decode bytes
- ReqDocumentRangeFormatting _ ->
- RspDocumentRangeFormatting $ fromJust $ decode bytes
- ReqDocumentOnTypeFormatting _ ->
- RspDocumentOnTypeFormatting $ fromJust $ decode bytes
- ReqRename _ -> RspRename $ fromJust $ decode bytes
- ReqExecuteCommand _ -> RspExecuteCommand $ fromJust $ decode bytes
- ReqDocumentLink _ -> RspDocumentLink $ fromJust $ decode bytes
- ReqDocumentLinkResolve _ -> RspDocumentLinkResolve $ fromJust $ decode bytes
- ReqWillSaveWaitUntil _ -> RspWillSaveWaitUntil $ fromJust $ decode bytes
- x -> error $ "Not a request: " ++ show x
-
-decodeFromServerMsg :: RequestMap -> B.ByteString -> FromServerMessage
-decodeFromServerMsg reqMap bytes =
- case HM.lookup "method" (fromJust $ decode bytes :: Object) of
- Just methodStr -> case fromJSON methodStr of
- Success method -> case method of
- -- We can work out the type of the message
- TextDocumentPublishDiagnostics -> NotPublishDiagnostics $ fromJust $ decode bytes
- WindowShowMessage -> NotShowMessage $ fromJust $ decode bytes
- WindowLogMessage -> NotLogMessage $ fromJust $ decode bytes
- CancelRequestServer -> NotCancelRequestFromServer $ fromJust $ decode bytes
- TelemetryEvent -> NotTelemetry $ fromJust $ decode bytes
- WindowShowMessageRequest -> ReqShowMessage $ fromJust $ decode bytes
- ClientRegisterCapability -> ReqRegisterCapability $ fromJust $ decode bytes
- ClientUnregisterCapability -> ReqUnregisterCapability $ fromJust $ decode bytes
- WorkspaceApplyEdit -> ReqApplyWorkspaceEdit $ fromJust $ decode bytes
+decodeFromServerMsg :: RequestMap -> B.ByteString -> (RequestMap, FromServerMessage)
+decodeFromServerMsg reqMap bytes = unP $ fromJust $ parseMaybe p obj
+ where obj = fromJust $ decode bytes :: Value
+ p = parseServerMessage $ \lid ->
+ let (mm, newMap) = pickFromIxMap lid reqMap
+ in case mm of
+ Nothing -> Nothing
+ Just m -> Just $ (m, Pair m (Const newMap))
+ unP (FromServerMess m msg) = (reqMap, FromServerMess m msg)
+ unP (FromServerRsp (Pair m (Const newMap)) msg) = (newMap, FromServerRsp m msg)
+ {-
+ WorkspaceWorkspaceFolders -> error "ReqWorkspaceFolders not supported yet"
+ WorkspaceConfiguration -> error "ReqWorkspaceConfiguration not supported yet"
+ CustomServerMethod _
+ | "id" `HM.member` obj && "method" `HM.member` obj -> ReqCustomServer $ fromJust $ decode bytes
+ | "id" `HM.member` obj -> RspCustomServer $ fromJust $ decode bytes
+ | otherwise -> NotCustomServer $ fromJust $ decode bytes
Error e -> error e
-
- Nothing -> case decode bytes :: Maybe (ResponseMessage Value) of
- Just msg -> case HM.lookup (requestId $ msg ^. id) reqMap of
- Just req -> matchResponseMsgType req bytes -- try to decode it to more specific type
- Nothing -> error "Couldn't match up response with request"
- Nothing -> error "Couldn't decode message"
\ No newline at end of file
+ -}