{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}

{- |
Module      :  Neovim.Context.Internal
Description :  Abstract description of the plugin provider's internal context
Copyright   :  (c) Sebastian Witte
License     :  Apache-2.0

Maintainer  :  woozletoff@gmail.com
Stability   :  experimental
Portability :  GHC

To shorten function and data type names, import this qualfied as @Internal@.
-}
module Neovim.Context.Internal where

import Neovim.Classes (
    AnsiStyle,
    Doc,
    NFData,
    Pretty (pretty),
    deepseq,
 )
import Neovim.Exceptions (
    NeovimException (..),
    exceptionToDoc,
 )
import Neovim.Plugin.Classes (
    FunctionName (..),
    FunctionalityDescription,
    HasFunctionName (nvimMethod),
    NeovimEventId (..),
    NvimMethod,
    Subscription (..),
    SubscriptionId (..),
 )
import Neovim.Plugin.IPC (SomeMessage)

import Data.Map (Map)
import qualified Data.Map as Map
import Data.MessagePack (Object)
import Data.Monoid (Ap (Ap))
import Data.Text (Text, pack)
import System.Log.Logger (errorM)
import UnliftIO (
    Exception (fromException),
    Handler (..),
    MVar,
    MonadIO (..),
    MonadUnliftIO,
    SomeException,
    TMVar,
    TQueue,
    TVar,
    atomically,
    catches,
    modifyTVar',
    newEmptyMVar,
    newEmptyTMVarIO,
    newTMVarIO,
    newTQueueIO,
    newTVarIO,
    putTMVar,
    readTVar,
    takeTMVar,
    throwIO,
    try,
 )

import Prettyprinter (viaShow)

import Conduit (MonadThrow)
import Control.Exception (
    ArithException,
    ArrayException,
    ErrorCall,
    PatternMatchFail,
 )
import qualified Control.Monad.Fail as Fail
import Control.Monad.Reader (
    MonadReader (ask, local),
    ReaderT (..),
    asks,
    void,
 )
import Prelude

{- | This is the environment in which all plugins are initially started.

 Functions have to run in this transformer stack to communicate with neovim.
 If parts of your own functions dont need to communicate with neovim, it is
 good practice to factor them out. This allows you to write tests and spot
 errors easier. Essentially, you should treat this similar to 'IO' in general
 haskell programs.
-}
newtype Neovim env a = Neovim
    {forall env a. Neovim env a -> ReaderT (Config env) IO a
unNeovim :: ReaderT (Config env) IO a}
    deriving newtype (forall a b. a -> Neovim env b -> Neovim env a
forall a b. (a -> b) -> Neovim env a -> Neovim env b
forall env a b. a -> Neovim env b -> Neovim env a
forall env a b. (a -> b) -> Neovim env a -> Neovim env b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
<$ :: forall a b. a -> Neovim env b -> Neovim env a
$c<$ :: forall env a b. a -> Neovim env b -> Neovim env a
fmap :: forall a b. (a -> b) -> Neovim env a -> Neovim env b
$cfmap :: forall env a b. (a -> b) -> Neovim env a -> Neovim env b
Functor, forall env. Functor (Neovim env)
forall a. a -> Neovim env a
forall env a. a -> Neovim env a
forall a b. Neovim env a -> Neovim env b -> Neovim env a
forall a b. Neovim env a -> Neovim env b -> Neovim env b
forall a b. Neovim env (a -> b) -> Neovim env a -> Neovim env b
forall env a b. Neovim env a -> Neovim env b -> Neovim env a
forall env a b. Neovim env a -> Neovim env b -> Neovim env b
forall env a b. Neovim env (a -> b) -> Neovim env a -> Neovim env b
forall a b c.
(a -> b -> c) -> Neovim env a -> Neovim env b -> Neovim env c
forall env a b c.
(a -> b -> c) -> Neovim env a -> Neovim env b -> Neovim env c
forall (f :: * -> *).
Functor f
-> (forall a. a -> f a)
-> (forall a b. f (a -> b) -> f a -> f b)
-> (forall a b c. (a -> b -> c) -> f a -> f b -> f c)
-> (forall a b. f a -> f b -> f b)
-> (forall a b. f a -> f b -> f a)
-> Applicative f
<* :: forall a b. Neovim env a -> Neovim env b -> Neovim env a
$c<* :: forall env a b. Neovim env a -> Neovim env b -> Neovim env a
*> :: forall a b. Neovim env a -> Neovim env b -> Neovim env b
$c*> :: forall env a b. Neovim env a -> Neovim env b -> Neovim env b
liftA2 :: forall a b c.
(a -> b -> c) -> Neovim env a -> Neovim env b -> Neovim env c
$cliftA2 :: forall env a b c.
(a -> b -> c) -> Neovim env a -> Neovim env b -> Neovim env c
<*> :: forall a b. Neovim env (a -> b) -> Neovim env a -> Neovim env b
$c<*> :: forall env a b. Neovim env (a -> b) -> Neovim env a -> Neovim env b
pure :: forall a. a -> Neovim env a
$cpure :: forall env a. a -> Neovim env a
Applicative, forall env. Applicative (Neovim env)
forall a. a -> Neovim env a
forall env a. a -> Neovim env a
forall a b. Neovim env a -> Neovim env b -> Neovim env b
forall a b. Neovim env a -> (a -> Neovim env b) -> Neovim env b
forall env a b. Neovim env a -> Neovim env b -> Neovim env b
forall env a b. Neovim env a -> (a -> Neovim env b) -> Neovim env b
forall (m :: * -> *).
Applicative m
-> (forall a b. m a -> (a -> m b) -> m b)
-> (forall a b. m a -> m b -> m b)
-> (forall a. a -> m a)
-> Monad m
return :: forall a. a -> Neovim env a
$creturn :: forall env a. a -> Neovim env a
>> :: forall a b. Neovim env a -> Neovim env b -> Neovim env b
$c>> :: forall env a b. Neovim env a -> Neovim env b -> Neovim env b
>>= :: forall a b. Neovim env a -> (a -> Neovim env b) -> Neovim env b
$c>>= :: forall env a b. Neovim env a -> (a -> Neovim env b) -> Neovim env b
Monad, forall env. Monad (Neovim env)
forall a. IO a -> Neovim env a
forall env a. IO a -> Neovim env a
forall (m :: * -> *).
Monad m -> (forall a. IO a -> m a) -> MonadIO m
liftIO :: forall a. IO a -> Neovim env a
$cliftIO :: forall env a. IO a -> Neovim env a
MonadIO, forall env. Monad (Neovim env)
forall e a. Exception e => e -> Neovim env a
forall env e a. Exception e => e -> Neovim env a
forall (m :: * -> *).
Monad m -> (forall e a. Exception e => e -> m a) -> MonadThrow m
throwM :: forall e a. Exception e => e -> Neovim env a
$cthrowM :: forall env e a. Exception e => e -> Neovim env a
MonadThrow, forall env. MonadIO (Neovim env)
forall b.
((forall a. Neovim env a -> IO a) -> IO b) -> Neovim env b
forall env b.
((forall a. Neovim env a -> IO a) -> IO b) -> Neovim env b
forall (m :: * -> *).
MonadIO m
-> (forall b. ((forall a. m a -> IO a) -> IO b) -> m b)
-> MonadUnliftIO m
withRunInIO :: forall b.
((forall a. Neovim env a -> IO a) -> IO b) -> Neovim env b
$cwithRunInIO :: forall env b.
((forall a. Neovim env a -> IO a) -> IO b) -> Neovim env b
MonadUnliftIO)
    deriving (NonEmpty (Neovim env a) -> Neovim env a
Neovim env a -> Neovim env a -> Neovim env a
forall b. Integral b => b -> Neovim env a -> Neovim env a
forall a.
(a -> a -> a)
-> (NonEmpty a -> a)
-> (forall b. Integral b => b -> a -> a)
-> Semigroup a
forall env a.
Semigroup a =>
NonEmpty (Neovim env a) -> Neovim env a
forall env a.
Semigroup a =>
Neovim env a -> Neovim env a -> Neovim env a
forall env a b.
(Semigroup a, Integral b) =>
b -> Neovim env a -> Neovim env a
stimes :: forall b. Integral b => b -> Neovim env a -> Neovim env a
$cstimes :: forall env a b.
(Semigroup a, Integral b) =>
b -> Neovim env a -> Neovim env a
sconcat :: NonEmpty (Neovim env a) -> Neovim env a
$csconcat :: forall env a.
Semigroup a =>
NonEmpty (Neovim env a) -> Neovim env a
<> :: Neovim env a -> Neovim env a -> Neovim env a
$c<> :: forall env a.
Semigroup a =>
Neovim env a -> Neovim env a -> Neovim env a
Semigroup, Neovim env a
[Neovim env a] -> Neovim env a
Neovim env a -> Neovim env a -> Neovim env a
forall a.
Semigroup a -> a -> (a -> a -> a) -> ([a] -> a) -> Monoid a
forall {env} {a}. Monoid a => Semigroup (Neovim env a)
forall env a. Monoid a => Neovim env a
forall env a. Monoid a => [Neovim env a] -> Neovim env a
forall env a.
Monoid a =>
Neovim env a -> Neovim env a -> Neovim env a
mconcat :: [Neovim env a] -> Neovim env a
$cmconcat :: forall env a. Monoid a => [Neovim env a] -> Neovim env a
mappend :: Neovim env a -> Neovim env a -> Neovim env a
$cmappend :: forall env a.
Monoid a =>
Neovim env a -> Neovim env a -> Neovim env a
mempty :: Neovim env a
$cmempty :: forall env a. Monoid a => Neovim env a
Monoid) via (Ap (Neovim env) a)

-- | User facing instance declaration for the reader state.
instance MonadReader env (Neovim env) where
    ask :: Neovim env env
ask = forall env a. ReaderT (Config env) IO a -> Neovim env a
Neovim forall a b. (a -> b) -> a -> b
$ forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks forall env. Config env -> env
customConfig
    local :: forall a. (env -> env) -> Neovim env a -> Neovim env a
local env -> env
f (Neovim ReaderT (Config env) IO a
a) = do
        Config env
r <- forall env a. ReaderT (Config env) IO a -> Neovim env a
Neovim forall r (m :: * -> *). MonadReader r m => m r
ask
        forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall r (m :: * -> *) a. ReaderT r m a -> r -> m a
runReaderT ReaderT (Config env) IO a
a (Config env
r{customConfig :: env
customConfig = env -> env
f (forall env. Config env -> env
customConfig Config env
r)})

instance Fail.MonadFail (Neovim env) where
    fail :: forall a. String -> Neovim env a
fail = forall (m :: * -> *) e a. (MonadIO m, Exception e) => e -> m a
throwIO forall b c a. (b -> c) -> (a -> b) -> a -> c
. Doc AnsiStyle -> NeovimException
ErrorMessage forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a ann. Pretty a => a -> Doc ann
pretty

-- | Same as 'ask' for the 'InternalConfig'.
ask' :: Neovim env (Config env)
ask' :: forall env. Neovim env (Config env)
ask' = forall env a. ReaderT (Config env) IO a -> Neovim env a
Neovim forall r (m :: * -> *). MonadReader r m => m r
ask

-- | Same as 'asks' for the 'InternalConfig'.
asks' :: (Config env -> a) -> Neovim env a
asks' :: forall env a. (Config env -> a) -> Neovim env a
asks' = forall env a. ReaderT (Config env) IO a -> Neovim env a
Neovim forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks

exceptionHandlers :: [Handler IO (Either (Doc ann) a)]
exceptionHandlers :: forall ann a. [Handler IO (Either (Doc ann) a)]
exceptionHandlers =
    [ forall (m :: * -> *) a e. Exception e => (e -> m a) -> Handler m a
Handler forall a b. (a -> b) -> a -> b
$ \(ArithException
_ :: ArithException) -> forall {a} {b}. a -> IO (Either a b)
ret Doc ann
"ArithException (e.g. division by 0)"
    , forall (m :: * -> *) a e. Exception e => (e -> m a) -> Handler m a
Handler forall a b. (a -> b) -> a -> b
$ \(ArrayException
_ :: ArrayException) -> forall {a} {b}. a -> IO (Either a b)
ret Doc ann
"ArrayException"
    , forall (m :: * -> *) a e. Exception e => (e -> m a) -> Handler m a
Handler forall a b. (a -> b) -> a -> b
$ \(ErrorCall
_ :: ErrorCall) -> forall {a} {b}. a -> IO (Either a b)
ret Doc ann
"ErrorCall (e.g. call of undefined or error"
    , forall (m :: * -> *) a e. Exception e => (e -> m a) -> Handler m a
Handler forall a b. (a -> b) -> a -> b
$ \(PatternMatchFail
_ :: PatternMatchFail) -> forall {a} {b}. a -> IO (Either a b)
ret Doc ann
"Pattern match failure"
    , forall (m :: * -> *) a e. Exception e => (e -> m a) -> Handler m a
Handler forall a b. (a -> b) -> a -> b
$ \(SomeException
_ :: SomeException) -> forall {a} {b}. a -> IO (Either a b)
ret Doc ann
"Unhandled exception"
    ]
  where
    ret :: a -> IO (Either a b)
ret = forall (m :: * -> *) a. Monad m => a -> m a
return forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. a -> Either a b
Left

-- | Initialize a 'Neovim' context by supplying an 'InternalEnvironment'.
runNeovim ::
    NFData a =>
    Config env ->
    Neovim env a ->
    IO (Either (Doc AnsiStyle) a)
runNeovim :: forall a env.
NFData a =>
Config env -> Neovim env a -> IO (Either (Doc AnsiStyle) a)
runNeovim = forall a env.
(a -> IO a)
-> Config env -> Neovim env a -> IO (Either (Doc AnsiStyle) a)
runNeovimInternal (\a
a -> a
a forall a b. NFData a => a -> b -> b
`deepseq` forall (m :: * -> *) a. Monad m => a -> m a
return a
a)

runNeovimInternal ::
    (a -> IO a) ->
    Config env ->
    Neovim env a ->
    IO (Either (Doc AnsiStyle) a)
runNeovimInternal :: forall a env.
(a -> IO a)
-> Config env -> Neovim env a -> IO (Either (Doc AnsiStyle) a)
runNeovimInternal a -> IO a
f Config env
r (Neovim ReaderT (Config env) IO a
a) =
    (forall (m :: * -> *) e a.
(MonadUnliftIO m, Exception e) =>
m a -> m (Either e a)
try forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall r (m :: * -> *) a. ReaderT r m a -> r -> m a
runReaderT ReaderT (Config env) IO a
a) Config env
r forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
        Left SomeException
e -> case forall e. Exception e => SomeException -> Maybe e
fromException SomeException
e of
            Just NeovimException
e' ->
                forall (m :: * -> *) a. Monad m => a -> m a
return forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. a -> Either a b
Left forall b c a. (b -> c) -> (a -> b) -> a -> c
. NeovimException -> Doc AnsiStyle
exceptionToDoc forall a b. (a -> b) -> a -> b
$ (NeovimException
e' :: NeovimException)
            Maybe NeovimException
Nothing -> do
                forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> String -> IO ()
errorM String
"Context" forall a b. (a -> b) -> a -> b
$ String
"Converting Exception to Error message: " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show SomeException
e
                (forall (m :: * -> *) a. Monad m => a -> m a
return forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. a -> Either a b
Left forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a ann. Show a => a -> Doc ann
viaShow) SomeException
e
        Right a
res ->
            (forall a b. b -> Either a b
Right forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> a -> IO a
f a
res) forall (m :: * -> *) a.
MonadUnliftIO m =>
m a -> [Handler m a] -> m a
`catches` forall ann a. [Handler IO (Either (Doc ann) a)]
exceptionHandlers

{- | Create a new unique function name. To prevent possible name clashes, digits
 are stripped from the given suffix.
-}
newUniqueFunctionName :: Neovim env FunctionName
newUniqueFunctionName :: forall env. Neovim env FunctionName
newUniqueFunctionName = do
    TVar Integer
tu <- forall env a. (Config env -> a) -> Neovim env a
asks' forall env. Config env -> TVar Integer
uniqueCounter
    -- reverseing the integer string should distribute the first character more
    -- evently and hence cause faster termination for comparisons.
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Text -> FunctionName
F forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
pack forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. [a] -> [a]
reverse forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Show a => a -> String
show) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (m :: * -> *) a. MonadIO m => STM a -> m a
atomically forall a b. (a -> b) -> a -> b
$ do
        Integer
u <- forall a. TVar a -> STM a
readTVar TVar Integer
tu
        forall a. TVar a -> (a -> a) -> STM ()
modifyTVar' TVar Integer
tu forall a. Enum a => a -> a
succ
        forall (m :: * -> *) a. Monad m => a -> m a
return Integer
u

{- | This data type is used to dispatch a remote function call to the appopriate
 recipient.
-}
newtype FunctionType
    = -- | 'Stateful' functions are handled within a special thread, the 'TQueue'
      -- is the communication endpoint for the arguments we have to pass.
      Stateful (TQueue SomeMessage)

instance Pretty FunctionType where
    pretty :: forall ann. FunctionType -> Doc ann
pretty = \case
        Stateful TQueue SomeMessage
_ -> Doc ann
"\\os -> Neovim env o"

-- | Type of the values stored in the function map.
type FunctionMapEntry = (FunctionalityDescription, FunctionType)

{- | A function map is a map containing the names of functions as keys and some
 context dependent value which contains all the necessary information to
 execute that function in the intended way.

 This type is only used internally and handles two distinct cases. One case
 is a direct function call, wich is simply a function that accepts a list of
 'Object' values and returns a result in the 'Neovim' context. The second
 case is calling a function that has a persistent state. This is mediated to
 a thread that reads from a 'TQueue'. (NB: persistent currently means, that
 state is stored for as long as the plugin provider is running and not
 restarted.)
-}
type FunctionMap = Map NvimMethod FunctionMapEntry

-- | Create a new function map from the given list of 'FunctionMapEntry' values.
mkFunctionMap :: [FunctionMapEntry] -> FunctionMap
mkFunctionMap :: [FunctionMapEntry] -> FunctionMap
mkFunctionMap = forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> b) -> [a] -> [b]
map (\FunctionMapEntry
e -> (forall a. HasFunctionName a => a -> NvimMethod
nvimMethod (forall a b. (a, b) -> a
fst FunctionMapEntry
e), FunctionMapEntry
e))

data Subscriptions = Subscriptions
    { Subscriptions -> SubscriptionId
nextSubscriptionId :: SubscriptionId
    , Subscriptions -> Map NeovimEventId [Subscription]
byEventId :: Map NeovimEventId [Subscription]
    }

{- | Subscribe to an event. When the event is received, the given callback function
 is run. It is usually necessary to call the appropriate API function in order for
 /neovim/ to send the notifications to /nvim-hs/. The returned subscription can be
 used to 'unsubscribe'.
-}
subscribe :: Text -> ([Object] -> Neovim env ()) -> Neovim env Subscription
subscribe :: forall env.
Text -> ([Object] -> Neovim env ()) -> Neovim env Subscription
subscribe Text
event [Object] -> Neovim env ()
action = do
    let eventId :: NeovimEventId
eventId = Text -> NeovimEventId
NeovimEventId Text
event
    Config env
cfg <- forall env. Neovim env (Config env)
ask'
    let subscriptions' :: TMVar Subscriptions
subscriptions' = forall env. Config env -> TMVar Subscriptions
subscriptions Config env
cfg
    forall (m :: * -> *) a. MonadIO m => STM a -> m a
atomically forall a b. (a -> b) -> a -> b
$ do
        Subscriptions
s <- forall a. TMVar a -> STM a
takeTMVar TMVar Subscriptions
subscriptions'
        let subscriptionId :: SubscriptionId
subscriptionId = Subscriptions -> SubscriptionId
nextSubscriptionId Subscriptions
s
        let newSubscription :: Subscription
newSubscription =
                Subscription
                    { subId :: SubscriptionId
subId = SubscriptionId
subscriptionId
                    , subEventId :: NeovimEventId
subEventId = NeovimEventId
eventId
                    , subAction :: [Object] -> IO ()
subAction = forall (f :: * -> *) a. Functor f => f a -> f ()
void forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a env.
NFData a =>
Config env -> Neovim env a -> IO (Either (Doc AnsiStyle) a)
runNeovim Config env
cfg forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Object] -> Neovim env ()
action
                    }
        forall a. TMVar a -> a -> STM ()
putTMVar
            TMVar Subscriptions
subscriptions'
            Subscriptions
s
                { nextSubscriptionId :: SubscriptionId
nextSubscriptionId = forall a. Enum a => a -> a
succ SubscriptionId
subscriptionId
                , byEventId :: Map NeovimEventId [Subscription]
byEventId = forall k a. Ord k => (a -> a -> a) -> k -> a -> Map k a -> Map k a
Map.insertWith forall a. Semigroup a => a -> a -> a
(<>) NeovimEventId
eventId [Subscription
newSubscription] (Subscriptions -> Map NeovimEventId [Subscription]
byEventId Subscriptions
s)
                }
        forall (f :: * -> *) a. Applicative f => a -> f a
pure Subscription
newSubscription

-- | Remove the subscription that has been returned by 'subscribe'.
unsubscribe :: Subscription -> Neovim env ()
unsubscribe :: forall env. Subscription -> Neovim env ()
unsubscribe Subscription
subscription = do
    TMVar Subscriptions
subscriptions' <- forall env a. (Config env -> a) -> Neovim env a
asks' forall env. Config env -> TMVar Subscriptions
subscriptions
    forall (f :: * -> *) a. Functor f => f a -> f ()
void forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (m :: * -> *) a. MonadIO m => STM a -> m a
atomically forall a b. (a -> b) -> a -> b
$ do
        Subscriptions
s <- forall a. TMVar a -> STM a
takeTMVar TMVar Subscriptions
subscriptions'
        let eventId :: NeovimEventId
eventId = Subscription -> NeovimEventId
subEventId Subscription
subscription
            deleteSubscription :: [Subscription] -> Maybe [Subscription]
deleteSubscription = forall a. a -> Maybe a
Just forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. (a -> Bool) -> [a] -> [a]
filter ((forall a. Eq a => a -> a -> Bool
/= Subscription -> SubscriptionId
subId Subscription
subscription) forall b c a. (b -> c) -> (a -> b) -> a -> c
. Subscription -> SubscriptionId
subId)
        forall a. TMVar a -> a -> STM ()
putTMVar
            TMVar Subscriptions
subscriptions'
            Subscriptions
s
                { byEventId :: Map NeovimEventId [Subscription]
byEventId = forall k a. Ord k => (a -> Maybe a) -> k -> Map k a -> Map k a
Map.update [Subscription] -> Maybe [Subscription]
deleteSubscription NeovimEventId
eventId (Subscriptions -> Map NeovimEventId [Subscription]
byEventId Subscriptions
s)
                }

{- | A wrapper for a reader value that contains extra fields required to
 communicate with the messagepack-rpc components and provide necessary data to
 provide other globally available operations.

 Note that you most probably do not want to change the fields prefixed with an
 underscore.
-}
data Config env = Config
    -- Global settings; initialized once
    { forall env. Config env -> TQueue SomeMessage
eventQueue :: TQueue SomeMessage
    -- ^ A queue of messages that the event handler will propagate to
    -- appropriate threads and handlers.
    , forall env. Config env -> MVar StateTransition
transitionTo :: MVar StateTransition
    -- ^ The main thread will wait for this 'MVar' to be filled with a value
    -- and then perform an action appropriate for the value of type
    -- 'StateTransition'.
    , forall env. Config env -> TMVar (Either String Int)
providerName :: TMVar (Either String Int)
    -- ^ Since nvim-hs must have its "Neovim.RPC.SocketReader" and
    -- "Neovim.RPC.EventHandler" running to determine the actual channel id
    -- (i.e. the 'Int' value here) this field can only be set properly later.
    -- Hence, the value of this field is put in an 'TMVar'.
    -- Name that is used to identify this provider. Assigning such a name is
    -- done in the neovim config (e.g. ~\/.nvim\/nvimrc).
    , forall env. Config env -> TVar Integer
uniqueCounter :: TVar Integer
    -- ^ This 'TVar' is used to generate uniqe function names on the side of
    -- /nvim-hs/. This is useful if you don't want to overwrite existing
    -- functions or if you create autocmd functions.
    , forall env. Config env -> TMVar FunctionMap
globalFunctionMap :: TMVar FunctionMap
    -- ^ This map is used to dispatch received messagepack function calls to
    -- it's appropriate targets.
    , -- Local settings; intialized for each stateful component

      forall env. Config env -> Maybe (PluginSettings env)
pluginSettings :: Maybe (PluginSettings env)
    -- ^ In a registered functionality this field contains a function (and
    -- possibly some context dependent values) to register new functionality.
    , forall env. Config env -> TMVar Subscriptions
subscriptions :: TMVar Subscriptions
    -- ^ Plugins can dynamically subscribe to events that neovim sends.
    , forall env. Config env -> env
customConfig :: env
    -- ^ Plugin author supplyable custom configuration. Queried on the
    -- user-facing side with 'ask' or 'asks'.
    }

{- | Convenient helper to create a new config for the given state and read-only
 config.

 Sets the 'pluginSettings' field to 'Nothing'.
-}
retypeConfig :: env -> Config anotherEnv -> Config env
retypeConfig :: forall env anotherEnv. env -> Config anotherEnv -> Config env
retypeConfig env
r Config anotherEnv
cfg = Config anotherEnv
cfg{pluginSettings :: Maybe (PluginSettings env)
pluginSettings = forall a. Maybe a
Nothing, customConfig :: env
customConfig = env
r}

{- | This GADT is used to share information between stateless and stateful
 plugin threads since they work fundamentally in the same way. They both
 contain a function to register some functionality in the plugin provider
 as well as some values which are specific to the one or the other context.
-}
data PluginSettings env where
    StatefulSettings ::
        ( FunctionalityDescription ->
          ([Object] -> Neovim env Object) ->
          TQueue SomeMessage ->
          TVar (Map NvimMethod ([Object] -> Neovim env Object)) ->
          Neovim env (Maybe FunctionMapEntry)
        ) ->
        TQueue SomeMessage ->
        TVar (Map NvimMethod ([Object] -> Neovim env Object)) ->
        PluginSettings env

{- | Create a new 'InternalConfig' object by providing the minimal amount of
 necessary information.

 This function should only be called once per /nvim-hs/ session since the
 arguments are shared across processes.
-}
newConfig :: IO (Maybe String) -> IO env -> IO (Config env)
newConfig :: forall env. IO (Maybe String) -> IO env -> IO (Config env)
newConfig IO (Maybe String)
ioProviderName IO env
r =
    forall env.
TQueue SomeMessage
-> MVar StateTransition
-> TMVar (Either String Int)
-> TVar Integer
-> TMVar FunctionMap
-> Maybe (PluginSettings env)
-> TMVar Subscriptions
-> env
-> Config env
Config
        forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (m :: * -> *) a. MonadIO m => m (TQueue a)
newTQueueIO
        forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall (m :: * -> *) a. MonadIO m => m (MVar a)
newEmptyMVar
        forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> (forall b a. b -> (a -> b) -> Maybe a -> b
maybe forall (m :: * -> *) a. MonadIO m => m (TMVar a)
newEmptyTMVarIO (forall (m :: * -> *) a. MonadIO m => a -> m (TMVar a)
newTMVarIO forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. a -> Either a b
Left) forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< IO (Maybe String)
ioProviderName)
        forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall (m :: * -> *) a. MonadIO m => a -> m (TVar a)
newTVarIO Integer
100
        forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall (m :: * -> *) a. MonadIO m => m (TMVar a)
newEmptyTMVarIO
        forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a. Maybe a
Nothing
        forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall (m :: * -> *) a. MonadIO m => a -> m (TMVar a)
newTMVarIO (SubscriptionId -> Map NeovimEventId [Subscription] -> Subscriptions
Subscriptions (Int64 -> SubscriptionId
SubscriptionId Int64
1) forall a. Monoid a => a
mempty)
        forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> IO env
r

-- | The state that the plugin provider wants to transition to.
data StateTransition
    = -- | Quit the plugin provider.
      Quit
    | -- | Restart the plugin provider.
      Restart
    | -- | The plugin provider failed to start or some other error occured.
      Failure (Doc AnsiStyle)
    | -- | The plugin provider started successfully.
      InitSuccess
    deriving (Int -> StateTransition -> ShowS
[StateTransition] -> ShowS
StateTransition -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [StateTransition] -> ShowS
$cshowList :: [StateTransition] -> ShowS
show :: StateTransition -> String
$cshow :: StateTransition -> String
showsPrec :: Int -> StateTransition -> ShowS
$cshowsPrec :: Int -> StateTransition -> ShowS
Show)