{-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} {- | Module : Neovim.Plugin.Classes Description : Classes and data types related to plugins Copyright : (c) Sebastian Witte License : Apache-2.0 Maintainer : woozletoff@gmail.com Stability : experimental Portability : GHC -} module Neovim.Plugin.Classes ( ExportedFunctionality(..), getFunction, getDescription, FunctionalityDescription(..), FunctionName(..), NeovimPlugin(..), Plugin(..), wrapPlugin, Synchronous(..), CommandOptions(..), AutocmdOptions(..), ) where import Neovim.Classes import Neovim.Context import Data.Default import qualified Data.Map as Map import Data.Maybe import Data.MessagePack import Data.Text (Text) -- | This data type is used in the plugin registration to properly register the -- functions. newtype ExportedFunctionality r st = EF (FunctionalityDescription, [Object] -> Neovim r st Object) -- | Extract the description of an 'ExportedFunctionality'. getDescription :: ExportedFunctionality r st -> FunctionalityDescription getDescription (EF (d,_)) = d -- | Extract the function of an 'ExportedFunctionality'. getFunction :: ExportedFunctionality r st -> [Object] -> Neovim r st Object getFunction (EF (_, f)) = f -- | Functionality specific functional description entries. -- -- All fields which are directly specified in these constructors are not -- optional, but can partialy be generated via the Template Haskell functions. -- The last field is a data type that contains all relevant options with -- sensible defaults, hence 'def' can be used as an argument. data FunctionalityDescription = Function Text Synchronous -- ^ Exported function. Callable via @call name(arg1,arg2)@. -- -- * Name of the function (must start with an uppercase letter) -- * Option to indicate how neovim should behave when calling this function | Command Text CommandOptions -- ^ Exported Command. Callable via @:Name arg1 arg2@. -- -- * Name of the command (must start with an uppercase letter) -- * Options to configure neovim's behavior for calling the command | Autocmd Text Text AutocmdOptions -- ^ Exported autocommand. Will call the given function if the type and -- filter match. -- -- NB: Since we are registering this on the Haskell side of things, the -- number of accepted arguments should be 0. -- TODO Should this be enforced somehow? Possibly via the TH generator. -- -- * Type of the autocmd (e.g. \"BufWritePost\") -- * Name for the function to call deriving (Show, Read, Eq, Ord) -- | This option detemines how neovim should behave when calling some -- functionality on a remote host. data Synchronous = Async -- ^ Call the functionality entirely for its side effects and do not wait -- for it to finish. Calling a functionality with this flag set is -- completely asynchronous and nothing is really expected to happen. This -- is why a call like this is called notification on the neovim side of -- things. | Sync -- ^ Call the function and wait for its result. This is only synchronous on -- the neovim side. For comands it means that the GUI will (probably) not -- allow any user input until a reult is received. Functions run -- asynchronously inside neovim (or in one of its plugin providers) can use -- these functions concurrently. deriving (Show, Read, Eq, Ord, Enum) instance Default Synchronous where def = Sync instance NvimObject Synchronous where toObject = \case Async -> toObject False Sync -> toObject True fromObject = \case ObjectBool True -> return Sync ObjectBool False -> return Async ObjectInt 0 -> return Async _ -> return Sync -- | Options that can be optionally set for commands. -- -- TODO Determine which of these make sense, how they are transmitted back and -- which options are still missing. -- (see remote#define#CommandOnHost in runtime\/autoload\/remote\/define.vim)) data CommandOptions = CommandOptions { cmdSync :: Synchronous -- ^ Option to indicate whether vim shuould block until the command has -- completed. (default: 'Sync') , cmdRange :: Maybe Text -- ^ Vim expression for the range (or count). (default: \"\") , cmdCount :: Bool -- ^ If true, , cmdNargs :: Int -- ^ Number of arguments. Note that all arguments have to be a string type. -- -- TODO Check this in the Template Haskell functions. -- -- If you're using the template haskell functions for registering commands, -- this field is overridden by it. , cmdBang :: Bool -- ^ Behavior changes when using a bang. } deriving (Show, Read, Eq, Ord) instance Default CommandOptions where def = CommandOptions { cmdSync = Sync , cmdRange = Nothing , cmdCount = False , cmdNargs = 0 , cmdBang = False } instance NvimObject CommandOptions where toObject (CommandOptions{..}) = (toObject :: Dictionary -> Object) . Map.fromList . catMaybes $ [ cmdRange >>= \r -> Just ("range", toObject r) , if cmdCount then Just ("count", toObject True) else Nothing , if cmdNargs > 0 then Just ("nargs", toObject cmdNargs) else Nothing , if cmdBang then Just ("bang", toObject True) else Nothing ] fromObject o = throwError $ "Did not expect to receive a CommandOptions object: " ++ show o data AutocmdOptions = AutocmdOptions { acmdSync :: Synchronous -- ^ Option to indicate whether vim shuould block until the function has -- completed. (default: 'Sync') , acmdPattern :: Text -- ^ Pattern to match on. (default: \"*\") , acmdNested :: Bool -- ^ Nested autocmd. (default: False) -- -- See @:h autocmd-nested@ } deriving (Show, Read, Eq, Ord) instance Default AutocmdOptions where def = AutocmdOptions { acmdSync = Sync , acmdPattern = "*" , acmdNested = False } instance NvimObject AutocmdOptions where toObject (AutocmdOptions{..}) = (toObject :: Dictionary -> Object) . Map.fromList $ [ ("pattern", toObject acmdPattern) , ("nested", toObject acmdNested) ] fromObject o = throwError $ "Did not expect to receive an AutocmdOptions object: " ++ show o -- | Conveniennce class to extract a name from some value. class FunctionName a where name :: a -> Text instance FunctionName FunctionalityDescription where name = \case Function n _ -> n Command n _ -> n Autocmd _ n _ -> n instance FunctionName (ExportedFunctionality r st) where name = name . getDescription -- | This data type contains meta information for the plugin manager. -- data Plugin r st = Plugin { exports :: [ExportedFunctionality () ()] , statefulExports :: [(r, st, [ExportedFunctionality r st])] } data NeovimPlugin = forall r st. NeovimPlugin (Plugin r st) -- | Wrap a 'Plugin' in some nice blankets, so that we can put them in a simple -- list. wrapPlugin :: Monad m => Plugin r st -> m NeovimPlugin wrapPlugin = return . NeovimPlugin