-- |Modify 'RpcCall' constructors that contain calls to @nvim_command@.
--
-- See @:h :command-modifiers@.
module Ribosome.Host.Modify where

import Data.MessagePack (Object (ObjectString))
import Exon (exon)

import Ribosome.Host.Api.Data (Buffer, Window)
import Ribosome.Host.Api.Effect (
  nvimBufGetNumber,
  nvimGetCurrentBuf,
  nvimGetCurrentWin,
  nvimSetCurrentBuf,
  nvimWinGetNumber,
  vimSetCurrentWindow,
  )
import Ribosome.Host.Data.Request (Request (Request))
import Ribosome.Host.Data.RpcCall (RpcCall (RpcCallRequest))
import qualified Ribosome.Host.Effect.Rpc as Rpc
import Ribosome.Host.Effect.Rpc (Rpc)

-- |Modify an 'RpcCall' constructor if it contains a request for @nvim_command@ by prefixing it with the given string.
modifyCall :: Text -> RpcCall a -> RpcCall a
modifyCall :: forall a. Text -> RpcCall a -> RpcCall a
modifyCall Text
modifier = \case
  RpcCallRequest (Request RpcMethod
"nvim_command" [ObjectString ByteString
cmd]) ->
    Request -> RpcCall a
forall a. MsgpackDecode a => Request -> RpcCall a
RpcCallRequest (RpcMethod -> [Object] -> Request
Request RpcMethod
"nvim_command" [ByteString -> Object
ObjectString [exon|#{encodeUtf8 modifier} #{cmd}|]])
  RpcCall a
c ->
    RpcCall a
c

-- |Prefix all @nvim_commands@ called in an action with the given string.
modifyCmd ::
  Member Rpc r =>
  Text ->
  Sem r a ->
  Sem r a
modifyCmd :: forall (r :: EffectRow) a.
Member Rpc r =>
Text -> Sem r a -> Sem r a
modifyCmd Text
modifier =
  (forall x (rInitial :: EffectRow).
 Rpc (Sem rInitial) x -> Tactical Rpc (Sem rInitial) r x)
-> Sem r a -> Sem r a
forall (e :: Effect) (r :: EffectRow) a.
Member e r =>
(forall x (rInitial :: EffectRow).
 e (Sem rInitial) x -> Tactical e (Sem rInitial) r x)
-> Sem r a -> Sem r a
interceptH \case
    Rpc.Sync RpcCall x
call ->
      x -> Sem (WithTactics Rpc f (Sem rInitial) r) (f x)
forall (f :: * -> *) a (e :: Effect) (m :: * -> *)
       (r :: EffectRow).
Functor f =>
a -> Sem (WithTactics e f m r) (f a)
pureT (x -> Sem (WithTactics Rpc f (Sem rInitial) r) (f x))
-> Sem (WithTactics Rpc f (Sem rInitial) r) x
-> Sem (WithTactics Rpc f (Sem rInitial) r) (f x)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< RpcCall x -> Sem (WithTactics Rpc f (Sem rInitial) r) x
forall (r :: EffectRow) a. Member Rpc r => RpcCall a -> Sem r a
Rpc.sync (Text -> RpcCall x -> RpcCall x
forall a. Text -> RpcCall a -> RpcCall a
modifyCall Text
modifier RpcCall x
call)
    Rpc.Async RpcCall a1
call Either RpcError a1 -> Sem rInitial ()
use ->
      () -> Sem (WithTactics Rpc f (Sem rInitial) r) (f ())
forall (f :: * -> *) a (e :: Effect) (m :: * -> *)
       (r :: EffectRow).
Functor f =>
a -> Sem (WithTactics e f m r) (f a)
pureT (() -> Sem (WithTactics Rpc f (Sem rInitial) r) (f ()))
-> Sem (WithTactics Rpc f (Sem rInitial) r) ()
-> Sem (WithTactics Rpc f (Sem rInitial) r) (f ())
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< RpcCall a1
-> (Either RpcError a1
    -> Sem (WithTactics Rpc f (Sem rInitial) r) ())
-> Sem (WithTactics Rpc f (Sem rInitial) r) ()
forall a (r :: EffectRow).
Member Rpc r =>
RpcCall a -> (Either RpcError a -> Sem r ()) -> Sem r ()
Rpc.async (Text -> RpcCall a1 -> RpcCall a1
forall a. Text -> RpcCall a -> RpcCall a
modifyCall Text
modifier RpcCall a1
call) (\ Either RpcError a1
r -> Sem (WithTactics Rpc f (Sem rInitial) r) (f ())
-> Sem (WithTactics Rpc f (Sem rInitial) r) ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (Sem rInitial () -> Tactical Rpc (Sem rInitial) r ()
forall (m :: * -> *) a (e :: Effect) (r :: EffectRow).
m a -> Tactical e m r a
runTSimple (Either RpcError a1 -> Sem rInitial ()
use Either RpcError a1
r)))
    Rpc.Notify RpcCall a1
call ->
      () -> Sem (WithTactics Rpc f (Sem rInitial) r) (f ())
forall (f :: * -> *) a (e :: Effect) (m :: * -> *)
       (r :: EffectRow).
Functor f =>
a -> Sem (WithTactics e f m r) (f a)
pureT (() -> Sem (WithTactics Rpc f (Sem rInitial) r) (f ()))
-> Sem (WithTactics Rpc f (Sem rInitial) r) ()
-> Sem (WithTactics Rpc f (Sem rInitial) r) (f ())
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< RpcCall a1 -> Sem (WithTactics Rpc f (Sem rInitial) r) ()
forall a (r :: EffectRow). Member Rpc r => RpcCall a -> Sem r ()
Rpc.notify (Text -> RpcCall a1 -> RpcCall a1
forall a. Text -> RpcCall a -> RpcCall a
modifyCall Text
modifier RpcCall a1
call)
    Rpc (Sem rInitial) x
Rpc.ChannelId ->
      ChannelId -> Sem (WithTactics Rpc f (Sem rInitial) r) (f ChannelId)
forall (f :: * -> *) a (e :: Effect) (m :: * -> *)
       (r :: EffectRow).
Functor f =>
a -> Sem (WithTactics e f m r) (f a)
pureT (ChannelId
 -> Sem (WithTactics Rpc f (Sem rInitial) r) (f ChannelId))
-> Sem (WithTactics Rpc f (Sem rInitial) r) ChannelId
-> Sem (WithTactics Rpc f (Sem rInitial) r) (f ChannelId)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< Sem (WithTactics Rpc f (Sem rInitial) r) ChannelId
forall (r :: EffectRow). Member Rpc r => Sem r ChannelId
Rpc.channelId

-- |Prefix all @nvim_commands@ called in an action with @silent@.
silent ::
  Member Rpc r =>
  Sem r a ->
  Sem r a
silent :: forall (r :: EffectRow) a. Member Rpc r => Sem r a -> Sem r a
silent =
  Text -> Sem r a -> Sem r a
forall (r :: EffectRow) a.
Member Rpc r =>
Text -> Sem r a -> Sem r a
modifyCmd Text
"silent"

-- |Prefix all @nvim_commands@ called in an action with @silent!@.
silentBang ::
  Member Rpc r =>
  Sem r a ->
  Sem r a
silentBang :: forall (r :: EffectRow) a. Member Rpc r => Sem r a -> Sem r a
silentBang =
  Text -> Sem r a -> Sem r a
forall (r :: EffectRow) a.
Member Rpc r =>
Text -> Sem r a -> Sem r a
modifyCmd Text
"silent!"

-- |Prefix all @nvim_commands@ called in an action with @noautocmd@.
noautocmd ::
  Member Rpc r =>
  Sem r a ->
  Sem r a
noautocmd :: forall (r :: EffectRow) a. Member Rpc r => Sem r a -> Sem r a
noautocmd =
  Text -> Sem r a -> Sem r a
forall (r :: EffectRow) a.
Member Rpc r =>
Text -> Sem r a -> Sem r a
modifyCmd Text
"noautocmd"

-- |Prefix all @nvim_commands@ called in an action with @windo N@ where @N@ is the number of the given window.
windo ::
  Member Rpc r =>
  Window ->
  Sem r a ->
  Sem r a
windo :: forall (r :: EffectRow) a.
Member Rpc r =>
Window -> Sem r a -> Sem r a
windo Window
win Sem r a
ma = do
  Window
previous <- Sem r Window
forall (r :: EffectRow). Member Rpc r => Sem r Window
nvimGetCurrentWin
  Int
number <- Window -> Sem r Int
forall (r :: EffectRow). Member Rpc r => Window -> Sem r Int
nvimWinGetNumber Window
win
  a
a <- Text -> Sem r a -> Sem r a
forall (r :: EffectRow) a.
Member Rpc r =>
Text -> Sem r a -> Sem r a
modifyCmd [exon|#{show number}windo|] Sem r a
ma
  Bool -> Sem r () -> Sem r ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Window
previous Window -> Window -> Bool
forall a. Eq a => a -> a -> Bool
/= Window
win) (Window -> Sem r ()
forall (r :: EffectRow). Member Rpc r => Window -> Sem r ()
vimSetCurrentWindow Window
previous)
  pure a
a

-- |Prefix all @nvim_commands@ called in an action with @bufdo N@ where @N@ is the number of the given buffer.
bufdo ::
  Member Rpc r =>
  Buffer ->
  Sem r a ->
  Sem r a
bufdo :: forall (r :: EffectRow) a.
Member Rpc r =>
Buffer -> Sem r a -> Sem r a
bufdo Buffer
buf Sem r a
ma = do
  Buffer
previous <- Sem r Buffer
forall (r :: EffectRow). Member Rpc r => Sem r Buffer
nvimGetCurrentBuf
  Int
number <- Buffer -> Sem r Int
forall (r :: EffectRow). Member Rpc r => Buffer -> Sem r Int
nvimBufGetNumber Buffer
buf
  a
a <- Text -> Sem r a -> Sem r a
forall (r :: EffectRow) a.
Member Rpc r =>
Text -> Sem r a -> Sem r a
modifyCmd [exon|#{show number}bufdo|] Sem r a
ma
  Bool -> Sem r () -> Sem r ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Buffer
previous Buffer -> Buffer -> Bool
forall a. Eq a => a -> a -> Bool
/= Buffer
buf) (Buffer -> Sem r ()
forall (r :: EffectRow). Member Rpc r => Buffer -> Sem r ()
nvimSetCurrentBuf Buffer
previous)
  pure a
a