module Ribosome.Plugin.Watch where

import Control.Lens (Lens')
import qualified Control.Lens as Lens (at)
import qualified Data.Map.Strict as Map (toList)
import Data.MessagePack (Object(ObjectNil))

import Ribosome.Control.Lock (lockOrSkip)
import Ribosome.Control.Monad.Ribo (MonadRibo, NvimE, pluginInternalL, pluginInternalModifyL)
import Ribosome.Control.Ribosome (RibosomeInternal)
import qualified Ribosome.Control.Ribosome as RibosomeInternal (watchedVariables)
import Ribosome.Nvim.Api.IO (vimGetVar)
import Ribosome.Nvim.Api.RpcCall (RpcError)

data WatchedVariable m =
  WatchedVariable {
    WatchedVariable m -> Text
wvName :: Text,
    WatchedVariable m -> Object -> m ()
wvHandler :: Object -> m ()
  }

watchedVariables :: Map Text (Object -> m ()) -> [WatchedVariable m]
watchedVariables :: Map Text (Object -> m ()) -> [WatchedVariable m]
watchedVariables =
  ((Text, Object -> m ()) -> WatchedVariable m)
-> [(Text, Object -> m ())] -> [WatchedVariable m]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Text, Object -> m ()) -> WatchedVariable m
forall (m :: * -> *). (Text, Object -> m ()) -> WatchedVariable m
create ([(Text, Object -> m ())] -> [WatchedVariable m])
-> (Map Text (Object -> m ()) -> [(Text, Object -> m ())])
-> Map Text (Object -> m ())
-> [WatchedVariable m]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Map Text (Object -> m ()) -> [(Text, Object -> m ())]
forall k a. Map k a -> [(k, a)]
Map.toList
  where
    create :: (Text, Object -> m ()) -> WatchedVariable m
create (Text
name, Object -> m ()
handler) = Text -> (Object -> m ()) -> WatchedVariable m
forall (m :: * -> *). Text -> (Object -> m ()) -> WatchedVariable m
WatchedVariable Text
name Object -> m ()
handler

storedVarLens :: Text -> Lens' RibosomeInternal (Maybe Object)
storedVarLens :: Text -> Lens' RibosomeInternal (Maybe Object)
storedVarLens Text
name =
  (Map Text Object -> f (Map Text Object))
-> RibosomeInternal -> f RibosomeInternal
forall c. HasRibosomeInternal c => Lens' c (Map Text Object)
RibosomeInternal.watchedVariables ((Map Text Object -> f (Map Text Object))
 -> RibosomeInternal -> f RibosomeInternal)
-> ((Maybe Object -> f (Maybe Object))
    -> Map Text Object -> f (Map Text Object))
-> (Maybe Object -> f (Maybe Object))
-> RibosomeInternal
-> f RibosomeInternal
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Index (Map Text Object)
-> Lens' (Map Text Object) (Maybe (IxValue (Map Text Object)))
forall m. At m => Index m -> Lens' m (Maybe (IxValue m))
Lens.at Text
Index (Map Text Object)
name

runHandler ::
  MonadRibo m =>
  WatchedVariable m ->
  Object ->
  m ()
runHandler :: WatchedVariable m -> Object -> m ()
runHandler (WatchedVariable Text
name Object -> m ()
handler) Object
new = do
  Lens' RibosomeInternal (Maybe Object)
-> (Maybe Object -> Maybe Object) -> m ()
forall (m :: * -> *) a.
MonadRibo m =>
Lens' RibosomeInternal a -> (a -> a) -> m ()
pluginInternalModifyL (Text -> Lens' RibosomeInternal (Maybe Object)
storedVarLens Text
name) (Maybe Object -> Maybe Object -> Maybe Object
forall a b. a -> b -> a
const (Object -> Maybe Object
forall a. a -> Maybe a
Just Object
new))
  Object -> m ()
handler Object
new

compareVar ::
  MonadRibo m =>
  WatchedVariable m ->
  Maybe Object ->
  Object ->
  m ()
compareVar :: WatchedVariable m -> Maybe Object -> Object -> m ()
compareVar WatchedVariable m
_ (Just Object
old) Object
new | Object
old Object -> Object -> Bool
forall a. Eq a => a -> a -> Bool
== Object
new =
  () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
compareVar WatchedVariable m
wv Maybe Object
_ Object
new =
  WatchedVariable m -> Object -> m ()
forall (m :: * -> *).
MonadRibo m =>
WatchedVariable m -> Object -> m ()
runHandler WatchedVariable m
wv Object
new

checkVar ::
  MonadRibo m =>
  NvimE e m =>
  WatchedVariable m ->
  m ()
checkVar :: WatchedVariable m -> m ()
checkVar wv :: WatchedVariable m
wv@(WatchedVariable Text
name Object -> m ()
_) = do
  Maybe Object
old <- Lens' RibosomeInternal (Maybe Object) -> m (Maybe Object)
forall (m :: * -> *) a.
MonadRibo m =>
Lens' RibosomeInternal a -> m a
pluginInternalL (Text -> Lens' RibosomeInternal (Maybe Object)
storedVarLens Text
name)
  Either () Object
new <- (RpcError -> m (Either () Object))
-> m (Either () Object) -> m (Either () Object)
forall e' e (m :: * -> *) a.
MonadDeepError e e' m =>
(e' -> m a) -> m a -> m a
catchAt @RpcError RpcError -> m (Either () Object)
forall (m :: * -> *) p b. Monad m => p -> m (Either () b)
recover (Object -> Either () Object
forall a b. b -> Either a b
Right (Object -> Either () Object) -> m Object -> m (Either () Object)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Text -> m Object
forall (m :: * -> *) e a.
(Nvim m, MonadDeepError e RpcError m, MsgpackDecode a) =>
Text -> m a
vimGetVar Text
name)
  (Object -> m ()) -> Either () Object -> m ()
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
(a -> f b) -> t a -> f ()
traverse_ (WatchedVariable m -> Maybe Object -> Object -> m ()
forall (m :: * -> *).
MonadRibo m =>
WatchedVariable m -> Maybe Object -> Object -> m ()
compareVar WatchedVariable m
wv Maybe Object
old) Either () Object
new
  where
  recover :: p -> m (Either () b)
recover p
_ = Either () b -> m (Either () b)
forall (m :: * -> *) a. Monad m => a -> m a
return (() -> Either () b
forall a b. a -> Either a b
Left ())

handleWatcherRequest ::
  MonadRibo m =>
  NvimE e m =>
  [WatchedVariable m] ->
  [Object] ->
  m Object
handleWatcherRequest :: [WatchedVariable m] -> [Object] -> m Object
handleWatcherRequest [WatchedVariable m]
variables [Object]
_ =
  Object
ObjectNil Object -> m () -> m Object
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ (WatchedVariable m -> m ()) -> [WatchedVariable m] -> m ()
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
(a -> f b) -> t a -> f ()
traverse_ WatchedVariable m -> m ()
forall (m :: * -> *) e.
(MonadRibo m, NvimE e m) =>
WatchedVariable m -> m ()
checkVar [WatchedVariable m]
variables

handleWatcherRequestSafe ::
  MonadBaseControl IO m =>
  MonadRibo m =>
  NvimE e m =>
  [WatchedVariable m] ->
  [Object] ->
  m Object
handleWatcherRequestSafe :: [WatchedVariable m] -> [Object] -> m Object
handleWatcherRequestSafe [WatchedVariable m]
variables [Object]
o =
  Object
ObjectNil Object -> m () -> m Object
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ Text -> m () -> m ()
forall (m :: * -> *).
(MonadRibo m, MonadBaseControl IO m) =>
Text -> m () -> m ()
lockOrSkip Text
"variable-watcher" (m Object -> m ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (m Object -> m ()) -> m Object -> m ()
forall a b. (a -> b) -> a -> b
$ [WatchedVariable m] -> [Object] -> m Object
forall (m :: * -> *) e.
(MonadRibo m, NvimE e m) =>
[WatchedVariable m] -> [Object] -> m Object
handleWatcherRequest [WatchedVariable m]
variables [Object]
o)