{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE RecordWildCards #-} {- | Module : Neovim.Plugin.IPC.Classes Description : Classes used for Inter Plugin Communication Copyright : (c) Sebastian Witte License : Apache-2.0 Maintainer : woozletoff@gmail.com Stability : experimental Portability : GHC -} module Neovim.Plugin.IPC.Classes ( SomeMessage(..), Message(..), FunctionCall(..), Request(..), Notification(..), UTCTime, getCurrentTime, module Data.Int, ) where import Neovim.Classes import Neovim.Plugin.Classes (FunctionName) import Control.Concurrent.STM import Data.Data (Typeable, cast) import Data.Int (Int64) import Data.MessagePack import Data.Time (UTCTime, formatTime, getCurrentTime) import Data.Time.Locale.Compat (defaultTimeLocale) import Text.PrettyPrint.ANSI.Leijen hiding ((<$>)) import Prelude -- | Taken from xmonad and based on ideas in /An Extensible Dynamically-Typed -- Hierarchy of Exceptions/, Simon Marlow, 2006. -- -- User-extensible messages must be put into a value of this type, so that it -- can be sent to other plugins. data SomeMessage = forall msg. Message msg => SomeMessage msg -- | This class allows type safe casting of 'SomeMessage' to an actual message. -- The cast is successful if the type you're expecting matches the type in the -- 'SomeMessage' wrapper. This way, you can subscribe to an arbitrary message -- type withouth having to pattern match on the constructors. This also allows -- plugin authors to create their own message types without having to change the -- core code of /nvim-hs/. class Typeable message => Message message where -- | Try to convert a given message to a value of the message type we are -- interested in. Will evaluate to 'Nothing' for any other type. fromMessage :: SomeMessage -> Maybe message fromMessage (SomeMessage message) = cast message -- | Haskell representation of supported Remote Procedure Call messages. data FunctionCall = FunctionCall FunctionName [Object] (TMVar (Either Object Object)) UTCTime -- ^ Method name, parameters, callback, timestamp deriving (Typeable) instance Message FunctionCall instance Pretty FunctionCall where pretty (FunctionCall fname args _ t) = nest 2 $ text "Function call for:" <+> pretty fname <$$> text "Arguments:" <+> text (show args) <$$> text "Timestamp:" <+> (yellow . text . formatTime defaultTimeLocale "%H:%M:%S (%q)") t -- | A request is a data type containing the method to call, its arguments and -- an identifier used to map the result to the function that has been called. data Request = Request { reqMethod :: FunctionName -- ^ Name of the function to call. , reqId :: !Int64 -- ^ Identifier to map the result to a function call invocation. , reqArgs :: [Object] -- ^ Arguments for the function. } deriving (Eq, Ord, Show, Typeable, Generic) instance NFData Request instance Message Request instance Pretty Request where pretty Request{..} = nest 2 $ text "Request" <+> (yellow . text . ('#':) . show) reqId <$$> (text "Method:" <+> pretty reqMethod) <$$> (text "Arguments:" <+> text (show reqArgs)) -- | A notification is similar to a 'Request'. It essentially does the same -- thing, but the function is only called for its side effects. This type of -- message is sent by neovim if the caller there does not care about the result -- of the computation. data Notification = Notification { notMethod :: FunctionName -- ^ Name of the function to call. , notArgs :: [Object] -- ^ Arguments for the function. } deriving (Eq, Ord, Show, Typeable, Generic) instance NFData Notification instance Message Notification instance Pretty Notification where pretty Notification{..} = nest 2 $ text "Notification" <$$> (text "Method:" <+> pretty notMethod) <$$> (text "Arguments:" <+> text (show notArgs))