neovimhaskell / nvim-hs

Neovim API for Haskell plugins as well as the plugin provider
Other
267 stars 18 forks source link

Investigate how to register plugins #3

Closed saep closed 9 years ago

saep commented 9 years ago

I cannot really make head or tails on how to actually tell neovim which functions a plugin provider offers. I had a somewhat serious glimpse at the python code, but I could not find the proper location. The :UpdateRemotePlugins function couldn't help me either.

osa1 commented 9 years ago

Here's one example that I could find: https://gist.github.com/tarruda/ea4b9854955451c66f70

This is a snake game, written using Python API. The specific line for registering key bindings to nvim is here: https://gist.github.com/tarruda/ea4b9854955451c66f70#file-snake-py-L44

I'm not sure if this is the only way to do it though. In the worst case we may need to write some wrappers around vim_command to map keys and register functions/commands.

osa1 commented 9 years ago

I believe this is the relevant documentation: http://neovim.org/doc/user/eval.html#rpcnotify()

rpcnotify() and rpcrequest() functions are the ones that we need to use to send messages to plugins from nvim. Basically we need map some commands to RPC calls using these functions. Mapping commands is done via vim_command as mentioned in my previous comment.

saep commented 9 years ago

I'm reading through this https://github.com/neovim/neovim/blob/master/runtime/autoload/remote/host.vim#L118-148 and it seems that a compiled program that magically provides plugins is not really supported yet. The function bails out if there are no files returned by this globpath expression

 let paths = globpath(&rtp, 'rplugin/'.a:host.'/'.pattern, 0, 1) let paths = globpath(&rtp, 'rplugin/'.a:host.'/'.pattern, 0, 1)
 if len(paths) < 1
echom "Could not find any plugins when attempting to register plugin "
\ ."commands. See :he remote-plugin"
return []
endif

We have to create some spec object for each plugin that we have. All necessary information for that should be put in the Plugin data type, I think.

Currently, the registration seems to expect the plugin provider to return these /specifications/ if it is asked for a file name. As this works for python, I don' t see how this can be feasible for Haskell. The simple end result seems to be some lines in .nvim/.nvimrc-rplugin~

We probably have to create an issue/pull request in the neovim repository. (Maybe there is already such a thing, I'll ckeck that before doing something further in this regard.)

osa1 commented 9 years ago

Interesting. It's good to know that we have a better solution than manually mapping commands to rpcnotify() and rpcrequest().

We have to create some spec object for each plugin that we have. All necessary information for that should be put in the Plugin data type, I think.

Agreed.

Currently, the registration seems to expect the plugin provider to return these /specifications/ if it is asked for a file name. As this works for python, I don' t see how this can be feasible for Haskell.

We probably have to create an issue/pull request in the neovim repository.

I don't understand the problem here, can you elaborate?

Btw, I found this discussion about Go host: https://github.com/myitcv/neovim/issues/10 too bad it's not updated since Nov 2014.

For completeness, here's the remote plugin documentation that mentions manifests: http://neovim.org/doc/user/remote_plugin.html It's not defining what is a manifest though.(e.g. file format) For that, here's an example manifest file: https://github.com/neovim/neovim/blob/master/runtime/autoload/remote/host.vim#L58-63

(actually, I'm not sure if that's really a manifest file. I believe the manifest file is the part starting at line 59 and ending at 62, other lines are added by s:RegistrationCommands(host))

Update: I just created this issue in neovim repository about remote-plugin documentation: https://github.com/neovim/neovim/issues/1929

saep commented 9 years ago

On Mon, 02 Feb 2015, Ömer Sinan Ağacan wrote:

Currently, the registration seems to expect the plugin provider to return these /specifications/ if it is asked for a file name. As this works for python, I don' t see how this can be feasible for Haskell.

We probably have to create an issue/pull request in the neovim repository.

I don't understand the problem here, can you elaborate?

On my train ride home I was about to answer this question, but then my elaborations led to three rewrites of an answer and none seemed to solve this problem.

One thing that led me to that assumption is that you can generally install a plugin via cabal, say cabal install ghc-mod-nvim will install a library that exports a ghc-mod plugin for neovim. Since the current way of adding a plugin is tied to the dyre library, we would add the plugin this way:

import GHCMod.Neovim (ghcModPlugin)

main = neovim def
        { plugins = [ ghcModPlugin ]
    }

There is no file created at all, so there is no file that can be sent via an rpcrequest to receive a spec.

Anyhow, we should probably write a few simple test plugins as test cases and see if we can compile it and register it manually via some vim script.

Here is an example for a config file that also declares a plugin which returns a random number.

{-# LANGUAGE OverloadedStrings #-}
import Neovim
import Neovim.API.Plugin

import System.Random

main :: IO ()
main = neovim def
    { logOptions = Just ("/home/saep/nvim-log.txt", DEBUG)
    , plugins    = [ randPlugin ]
    }

randPlugin :: IO (Plugin () StdGen)
randPlugin = do
    q <- newTQueueIO
    g <- newStdGen
    return $ Plugin { name = "Random number generator"
                    , functions = []
                    , statefulFunctions = [("Rand", q)]
                    , services = [((), g, nextRand q)]
                    }

nextRand :: TQueue SomeMessage -> Neovim () StdGen ()
nextRand q = do
    req <- awaitRequest q
    case () of
        _ | reqMethod req == "Random" -> do
            (r,g) <- random <$> get
            put g
            respond (reqId req) (Right (r :: Int64))
        _ -> return ()
    nextRand q

This currently fails with this log:

/home/saep/.config/nvim/nvim.hs:10:22:
    Couldn't match type `st' with `StdGen'
      `st' is a rigid type variable bound by
           a type expected by the context: [IO (Plugin r st)]
           at /home/saep/.config/nvim/nvim.hs:8:15
    Expected type: IO (Plugin r st)
      Actual type: IO (Plugin () StdGen)
    In the expression: randPlugin
    In the `plugins' field of a record
    In the first argument of `neovim', namely
      `def
         {logOptions = Just ("/home/saep/nvim-log.txt", DEBUG),
          plugins = [randPlugin]}'

/home/saep/.config/nvim/nvim.hs:10:22:
    Couldn't match type `r' with `()'
      `r' is a rigid type variable bound by
          a type expected by the context: [IO (Plugin r st)]
          at /home/saep/.config/nvim/nvim.hs:8:15
    Expected type: IO (Plugin r st)
      Actual type: IO (Plugin () StdGen)
    In the expression: randPlugin
    In the `plugins' field of a record
    In the first argument of `neovim', namely
      `def
         {logOptions = Just ("/home/saep/nvim-log.txt", DEBUG),
          plugins = [randPlugin]}'

I guess the interface must be redesigned and/or some more type and extension trickery is required.

saep commented 9 years ago

I factored out the transforme stacks in a branch and I can compile the plugin. (remove-transformer-branch). But it just hangs if I register and call the plugin according to https://github.com/neovim/neovim/issues/1929. I'll see to investigate that if I have some time.

{-# LANGUAGE OverloadedStrings #-}
import Neovim
import Neovim.API.IPC
import Neovim.API.Plugin

import System.Random
import Control.Monad.State

main :: IO ()
main = neovim def
    { logOptions = Just ("/home/saep/nvim-log.txt", DEBUG)
    , plugins    = [ randPlugin ]
    }

randPlugin :: IO Plugin
randPlugin = do
    q <- newTQueueIO
    g <- newStdGen
    return $ Plugin { name = "Random number generator"
                    , functions = []
                    , statefulFunctions = [("Rand", q)]
                    , services = [\rpc -> void (runStateT (nextRand q rpc) g)]
                    }

nextRand :: TQueue SomeMessage -> CommunicationInformation -> StateT StdGen IO ()
nextRand q rpc = do
    req <- awaitRequest q
    case () of
        _ | reqMethod req == "Random" -> do
            (r,g) <- random <$> get
            put g
            respond (reqId req) (Right (r :: Int64)) rpc
        _ -> return ()
    nextRand q rpc

https://github.com/neovim/neovim/issues/1929

osa1 commented 9 years ago

I just gave remove-transformers branch it a try, but it fails to compile here:

library/Neovim/API/String.hs:19:3:
    ObjectFixInt (PosFixInt 0) is not an Int64.
cabal: Error: some packages failed to install:
nvim-hs-0.0.1 failed during the building phase. The exception was:
ExitFailure 1

Any ideas why? Is it related with nvim --api-info? I'm using NVIM 0.0.0-alpha+201502081746 (compiled Feb 8 2015 20:07:48).

saep commented 9 years ago

My guess: Template Haskell residue. cabal clean should fix it.

saep commented 9 years ago

Or the local gutsubmodule version is used (which has no release). I debased the branch on the other pushed branch.

saep commented 9 years ago

I think we are now able to register commands, which may well be enough for the time being. We basically have to decide how we generate the arguments for remote#define#CommandOnHost().

saep commented 9 years ago

We essentially have to call the functions in remote#define with the right arguments. If any work is done for this, it should probably be based upon #19 (at least until it is merged).

saep commented 9 years ago

Using remote#define#FunctionOnHost always wraps all arguments in a list. So we either have to wait for a response on the neovim side if this is intended behavior (see neovim/neovim#1929), or we must work around that.

saep commented 9 years ago

The issue as it stands has been resolved. Implementation details should be put into separate issues and the implementation of #20 will resolve the basics for commands and autocmds that are not properly tested and implemented.