<commit_before>{-# LANGUAGE GADTs               #-}
{-# LANGUAGE LambdaCase          #-}
{-# LANGUAGE RecordWildCards     #-}
{-# LANGUAGE ScopedTypeVariables #-}
{- |
Module      :  Neovim.Plugin
Description :  Plugin and functionality registration code
Copyright   :  (c) <NAME>
License     :  Apache-2.0

Maintainer  :  <EMAIL>
Stability   :  experimental
Portability :  GHC

-}

module Neovim.Plugin (
    startPluginThreads,
    StartupConfig,
    wrapPlugin,
    NeovimPlugin,
    Plugin(..),
    Synchronous(..),
    CommandOption(..),

    addAutocmd,
    addAutocmd',

    registerInStatelessContext,
    registerInStatefulContext,
    ) where

import           Neovim.API.String
import           Neovim.Classes
import           Neovim.Config
import           Neovim.Context
import           Neovim.Context.Internal      (FunctionType (..))
import qualified Neovim.Context.Internal      as Internal
import           Neovim.Plugin.Classes        hiding (register)
import           Neovim.Plugin.Internal
import           Neovim.Plugin.IPC.Classes
import qualified Neovim.Plugin.Startup        as Plugin
import           Neovim.RPC.FunctionCall

import           Control.Applicative
import           Control.Concurrent           (ThreadId, forkIO)
import           Control.Concurrent.STM
import           Control.Monad                (foldM, void)
import           Control.Monad.Catch          (SomeException, try)
import           Control.Monad.Trans.Resource hiding (register)
import           Data.ByteString              (ByteString)
import           Data.Foldable                (forM_)
import           Data.Map                     (Map)
import qualified Data.Map                     as Map
import           Data.Maybe                   (catMaybes)
import           Data.MessagePack
import           Data.Traversable             (forM)
import           System.Log.Logger

import           Prelude


logger :: String
logger = "Neovim.Plugin"


type StartupConfig = Plugin.StartupConfig NeovimConfig


startPluginThreads :: Internal.Config StartupConfig ()
                   -> [Neovim StartupConfig () NeovimPlugin]
                   -> IO (Either String ([FunctionMapEntry],[ThreadId]))
startPluginThreads cfg = fmap (fmap fst)
    . runNeovim cfg ()
    . foldM go ([], [])
  where
    go :: ([FunctionMapEntry], [ThreadId])
       -> Neovim StartupConfig () NeovimPlugin
       -> Neovim StartupConfig () ([FunctionMapEntry], [ThreadId])
    go acc iop = do
        NeovimPlugin p <- iop

        (es, tids) <- foldl (\(es, tids) (es', tid) -> (es'++es, tid:tids)) acc
            <$> mapM registerStatefulFunctionality (statefulExports p)

        es' <- forM (exports p) $ \e -> do
            registerInStatelessContext
                (\_ -> return ())
                (getDescription e)
                (getFunction e)

        return $ (catMaybes es' ++ es, tids)


-- | Callthe vimL functions to define a function, command or autocmd on the
-- neovim side. Returns 'True' if registration was successful.
--
-- Note that this does not have any effect on the side of /nvim-hs/.
registerWithNeovim :: FunctionalityDescription -> Neovim anyConfig anyState Bool
registerWithNeovim = \case
    Function (F functionName) s -> do
        pName <- getProviderName
        ret <- vim_call_function "remote#define#FunctionOnHost"
            [ toObject pName, toObject functionName, toObject s
            , toObject functionName, toObject (Map.empty :: Dictionary)
            ]
        case ret of
            Left e -> do
                liftIO . errorM logger $
                    "Failed to register function: " ++ show functionName ++ show e
                return False
            Right _ -> do
                liftIO . debugM logger $
                    "Registered function: " ++ show functionName
                return True

    Command (F functionName) copts -> do
        let sync = case getCommandOptions copts of
                    -- This works because CommandOptions are sorted and CmdSync is
                    -- the smallest element in the sorting
                    (CmdSync s:_) -> s
                    _             -> Sync

        pName <- getProviderName
        ret <- vim_call_function "remote#define#CommandOnHost"
            [ toObject pName, toObject functionName, toObject sync
            , toObject functionName, toObject copts
            ]
        case ret of
            Left e -> do
                liftIO . errorM logger $
                    "Failed to register command: " ++ show functionName ++ show e
                return False
            Right _ -> do
                liftIO . debugM logger $
                    "Registered command: " ++ show functionName
                return True

    Autocmd acmdType (F functionName) opts -> do
        pName <- getProviderName
        ret <- vim_call_function "remote#define#AutocmdOnHost"
            [ toObject pName, toObject functionName, toObject Async
            , toObject acmdType , toObject opts
            ]
        case ret of
            Left e -> do
                liftIO . errorM logger $
                    "Failed to register autocmd: " ++ show functionName ++ show e
                return False
            Right _ -> do
                liftIO . debugM logger $
                    "Registered autocmd: " ++ show functionName
                return True


-- | Return or retrive the provider name that the current instance is associated
-- with on the neovim side.
getProviderName :: Neovim r st (Either String Int)
getProviderName = do
    mp <- Internal.asks' Internal.providerName<commit_msg>Register functionality different if no host name is specified<commit_after>{-# LANGUAGE GADTs               #-}
{-# LANGUAGE LambdaCase          #-}
{-# LANGUAGE RecordWildCards     #-}
{-# LANGUAGE ScopedTypeVariables #-}
{- |
Module      :  Neovim.Plugin
Description :  Plugin and functionality registration code
Copyright   :  (c) <NAME>
License     :  Apache-2.0

Maintainer  :  <EMAIL>
Stability   :  experimental
Portability :  GHC

-}

module Neovim.Plugin (
    startPluginThreads,
    StartupConfig,
    wrapPlugin,
    NeovimPlugin,
    Plugin(..),
    Synchronous(..),
    CommandOption(..),

    addAutocmd,
    addAutocmd',

    registerInStatelessContext,
    registerInStatefulContext,
    ) where

import           Neovim.API.String
import           Neovim.Classes
import           Neovim.Config
import           Neovim.Context
import           Neovim.Context.Internal      (FunctionType (..))
import qualified Neovim.Context.Internal      as Internal
import           Neovim.Plugin.Classes        hiding (register)
import           Neovim.Plugin.Internal
import           Neovim.Plugin.IPC.Classes
import qualified Neovim.Plugin.Startup        as Plugin
import           Neovim.RPC.FunctionCall

import           Control.Applicative
import           Control.Concurrent           (ThreadId, forkIO)
import           Control.Concurrent.STM
import           Control.Monad                (foldM, void)
import           Control.Monad.Catch          (SomeException, try)
import           Control.Monad.Trans.Resource hiding (register)
import           Data.ByteString              (ByteString)
import           Data.Foldable                (forM_)
import           Data.Map                     (Map)
import qualified Data.Map                     as Map
import           Data.Maybe                   (catMaybes)
import           Data.MessagePack
import           Data.Traversable             (forM)
import           System.Log.Logger

import           Prelude


logger :: String
logger = "Neovim.Plugin"


type StartupConfig = Plugin.StartupConfig NeovimConfig


startPluginThreads :: Internal.Config StartupConfig ()
                   -> [Neovim StartupConfig () NeovimPlugin]
                   -> IO (Either String ([FunctionMapEntry],[ThreadId]))
startPluginThreads cfg = fmap (fmap fst)
    . runNeovim cfg ()
    . foldM go ([], [])
  where
    go :: ([FunctionMapEntry], [ThreadId])
       -> Neovim StartupConfig () NeovimPlugin
       -> Neovim StartupConfig () ([FunctionMapEntry], [ThreadId])
    go acc iop = do
        NeovimPlugin p <- iop

        (es, tids) <- foldl (\(es, tids) (es', tid) -> (es'++es, tid:tids)) acc
            <$> mapM registerStatefulFunctionality (statefulExports p)

        es' <- forM (exports p) $ \e -> do
            registerInStatelessContext
                (\_ -> return ())
                (getDescription e)
                (getFunction e)

        return $ (catMaybes es' ++ es, tids)


-- | Callthe vimL functions to define a function, command or autocmd on the
-- neovim side. Returns 'True' if registration was successful.
--
-- Note that this does not have any effect on the side of /nvim-hs/.
registerWithNeovim :: FunctionalityDescription -> Neovim anyConfig anyState Bool
registerWithNeovim = \case
    Function (F functionName) s -> do
        pName <- getProviderName
        let (defineFunction, host) = either
                (\n -> ("remote#define#FunctionOnHost", toObject n))
                (\c -> ("remote#define#FunctionOnChannel", toObject c))
                pName
        ret <- vim_call_function defineFunction
            [ host, toObject functionName, toObject s
            , toObject functionName, toObject (Map.empty :: Dictionary)
            ]
        case ret of
            Left e -> do
                liftIO . errorM logger $
                    "Failed to register function: " ++ show functionName ++ show e
                return False
            Right _ -> do
                liftIO . debugM logger $
                    "Registered function: " ++ show functionName
                return True

    Command (F functionName) copts -> do
        let sync = case getCommandOptions copts of
                    -- This works because CommandOptions are sorted and CmdSync is
                    -- the smallest element in the sorting
                    (CmdSync s:_) -> s
                    _             -> Sync

        pName <- getProviderName
        let (defineFunction, host) = either
                (\n -> ("remote#define#CommandOnHost", toObject n))
                (\c -> ("remote#define#CommandOnChannel", toObject c))
                pName
        ret <- vim_call_function defineFunction
            [ host, toObject functionName, toObject sync
            , toObject functionName, toObject copts
            ]
        case ret of
            Left e -> do
                liftIO . errorM logger $
                    "Failed to register command: " ++ show functionName ++ show e
                return False
            Right _ -> do
                liftIO . debugM logger $
                    "Registered command: " ++ show functionName
                return True

    Autocmd acmdType (F functionName) opts -> do
        pName <- getProviderName
        let (defineFunction, host) = either
                (\n -> ("remote#define#AutocmdOnHost", toObject n))
                (\c -> ("remote#define#AutocmdOnChannel", toObject c))
                pName
        ret <- vim_call_function defineFunction
            [ host, toObject functionName, toObject Async
            , toObject acmdType , toObject opts
            ]
        case ret of
            Left e -> do
                liftIO . errorM logger $
                    "Failed to register autocmd: " ++ show functionName ++ show e
                return False
            Right _ -> do
                liftIO . debugM logger $
                    "Registered autocmd: " ++ show functionName
                return True


-- | Return or retrive the provider name that the current instance is associated
-- with on the neovim side.
getProviderName :: Neovim r st (Either String Int)
getProviderName = do
    mp <- Internal.asks' Internal.providerName
{'fraction_non_alphanumeric': 0.08056751963516595, 'fraction_numerical': 0.0003378093066463981, 'mean_word_length': 1.7197978870004593, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 53, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7357439', 'n_tokens_mistral': 3193, 'n_tokens_neox': 3041, 'n_words': 927}