    {- | This module provides assistance in implementing \"hooks\" in Yi.
    This module provides no major new functionality -- only assistance in
    using 'YiConfigVariable's more easily to implement hooks.

    We consider a simple example. Suppose we have a function

    > promptForFile :: Maybe FilePath -> YiM FilePath

    which prompts the user to select a file from their file system,
    starting with the provided directory (if actually provided). Since
    this is a frequent task in Yi, it is important for it to be as
    user-friendly as possible. If opinions vary on the meaning of
    \"user-friendly\", then we would really like to provide multiple
    implementations of @promptForFile@, and allow users to select which
    implementation to use in their config files.

    A way to achieve this is using hooks, as follows:

    > -- create a new type
    > newtype FilePrompter = FilePrompter
    >   { runFilePrompter :: Maybe FilePath -> YiM FilePath }
    >   deriving (Typeable)
    > $(nameDeriveAccessors ''FilePrompter (n -> Just (n ++ "A")))
    >
    > -- give some implementations
    > filePrompter1, filePrompter2, filePrompter3 :: FilePrompter
    > ...
    >
    > -- declare FilePrompter as a YiConfigVariable (so it can go in the Config)
    > instance YiConfigVariable FilePrompter
    >
    > -- specify the default FilePrompter
    > instance Default FilePrompter where
    >    def = filePrompter1
    >
    > -- replace the old promptForFile function with a shim
    > promptForFile :: Maybe FilePath -> YiM FilePath
    > promptForFile = runHook runFilePrompter
    >
    > -- provide a custom-named Field for Yi.Config.Simple (not
    > -- strictly necessary, but user-friendly)
    > filePrompter :: Field FilePrompter
    > filePrompter = customVariable

    The user can write

    > ...
    >    filePrompter %= filePrompter2
    > ...

    in their config file, and calls to @promptForFile@ will now use the
    different prompter. Library code which called @promptForFile@ does not
    need to be changed, but it gets the new @filePrompter2@ behaviour
    automatically.

    See "Yi.Eval" for a real example of hooks.
    -}

    module Yi.Hooks(
      -- * Convenience function 'runHook'
      runHook,
      HookType,
      -- * Re-exports from "Yi.Config.Simple"
      customVariable,
      Field,
     )
      where

    import Lens.Micro.Platform             ((^.))
    import Yi.Config              (configVariable)
    import Yi.Config.Simple.Types (Field, customVariable)
    import Yi.Editor              (EditorM, askCfg)
    import Yi.Keymap              (YiM)
    import Yi.Types               (YiConfigVariable)

    -- | Looks up the configured value for the hook, and runs it. The
    -- argument to 'runHook' will typically be a record accessor. See
    -- 'HookType' for the valid hook types.
    runHook :: (HookType ty, YiConfigVariable var) => (var -> ty) -> ty
    runHook :: forall ty var.
    (HookType ty, YiConfigVariable var) =>
    (var -> ty) -> ty
    runHook = (var -> ty) -> ty
    forall var. YiConfigVariable var => (var -> ty) -> ty
    forall ty var.
    (HookType ty, YiConfigVariable var) =>
    (var -> ty) -> ty
    runHookImpl

    -- | The class of \"valid hooks\". This class is exported abstractly,
    -- but the instances can be phrased quite simply: the functions (of
    -- arbitrarily many arguments, including zero) which run in either the
    -- 'EditorM' or 'YiM' monads.
    --
    --A typical example would be something like
    --
    -- @Int -> String -> 'EditorM' String@.
    class HookType ty where
        runHookImpl :: YiConfigVariable var => (var -> ty) -> ty

    instance HookType (EditorM a) where
        runHookImpl :: forall var. YiConfigVariable var => (var -> EditorM a) -> EditorM a
    runHookImpl var -> EditorM a
    lookupHook = do
            cfg <- EditorM Config
    forall (m :: * -> *). MonadEditor m => m Config
    askCfg
            lookupHook (cfg ^. configVariable)
    instance HookType (YiM a) where
        runHookImpl :: forall var. YiConfigVariable var => (var -> YiM a) -> YiM a
    runHookImpl var -> YiM a
    lookupHook = do
            cfg <- YiM Config
    forall (m :: * -> *). MonadEditor m => m Config
    askCfg
            lookupHook (cfg ^. configVariable)
    instance HookType b => HookType (a -> b) where
        runHookImpl :: forall var. YiConfigVariable var => (var -> a -> b) -> a -> b
    runHookImpl var -> a -> b
    lookupHook a
    a = (var -> b) -> b
    forall var. YiConfigVariable var => (var -> b) -> b
    forall ty var.
    (HookType ty, YiConfigVariable var) =>
    (var -> ty) -> ty
    runHookImpl (((a -> b) -> a -> b
    forall a b. (a -> b) -> a -> b
    $a
    a) ((a -> b) -> b) -> (var -> a -> b) -> var -> b
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . var -> a -> b
    lookupHook)