packages feed

ribosome-0.3.0.0: lib/Ribosome/Api/Exists.hs

module Ribosome.Api.Exists where

import Control.Monad.IO.Class (MonadIO)
import Data.Default (Default(def))
import Data.Either (isRight)
import Data.Text.Prettyprint.Doc (viaShow, (<+>))
import Neovim (
  AnsiStyle,
  Doc,
  NvimObject,
  Object(ObjectInt),
  )

import Ribosome.Control.Monad.Ribo (NvimE)
import Ribosome.Msgpack.Decode (MsgpackDecode, fromMsgpack)
import Ribosome.Msgpack.Encode (toMsgpack)
import Ribosome.Nvim.Api.IO (vimCallFunction)
import Ribosome.System.Time (epochSeconds, sleep)

data Retry =
  Retry Int Double
  deriving Show

instance Default Retry where
  def = Retry 3 0.1

retry ::
  MonadIO f =>
  f a ->
  (a -> f (Either c b)) ->
  Retry ->
  f (Either c b)
retry thunk check (Retry timeout interval) = do
  start <- epochSeconds
  step start
  where
    step start = do
      result <- thunk
      checked <- check result
      recurse start checked
    recurse _ (Right b) = return (Right b)
    recurse start (Left e) = do
      current <- epochSeconds
      if (current - start) < timeout
      then do
        sleep interval
        step start
      else return $ Left e

waitFor ::
  NvimE e m =>
  MonadIO m =>
  NvimObject b =>
  m Object ->
  (Object -> m (Either (Doc AnsiStyle) b)) ->
  Retry ->
  m (Either (Doc AnsiStyle) b)
waitFor thunk check' =
  retry thunk check
  where
    check result =
      case fromMsgpack result of
        Right a -> check' a
        Left e -> return $ Left e

existsResult :: Object -> Either (Doc AnsiStyle) ()
existsResult (ObjectInt 1) = Right ()
existsResult a =
  Left $ "weird return type " <+> viaShow a

vimExists ::
  NvimE e m =>
  Text ->
  m Object
vimExists entity =
  vimCallFunction "exists" [toMsgpack entity]

vimDoesExist ::
  NvimE e m =>
  Text ->
  m Bool
vimDoesExist entity =
  fmap (isRight . existsResult) (vimExists entity)

function ::
  NvimE e m =>
  Text ->
  m Bool
function name =
  vimDoesExist ("*" <> name)

waitForFunction ::
  NvimE e m =>
  MonadIO m =>
  Text ->
  Retry ->
  m (Either (Doc AnsiStyle) ())
waitForFunction name =
  waitFor thunk (return . existsResult)
  where
    thunk = vimExists ("*" <> name)

waitForFunctionResult ::
  NvimE e m =>
  MonadIO m =>
  Eq a =>
  Show a =>
  MsgpackDecode a =>
  Text ->
  a ->
  Retry ->
  m (Either (Doc AnsiStyle) ())
waitForFunctionResult name a retry' =
  waitForFunction name retry' >>= \case
    Right _ -> waitFor thunk (return . check . fromMsgpack) retry'
    Left e -> return (Left e)
  where
    thunk = vimCallFunction name []
    check (Right a') | a == a' =
      Right ()
    check (Right a') =
      Left $ "results differ:" <+> show a <+> "/" <+> show a'
    check (Left e) =
      Left $ "weird return type: " <+> e