You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
"multi-tenancy" (one node process for all node rplugins).
"decorators" can be used in the remote module to define Nvim commands/autocmds.
Too many moving parts, too many concepts.
why is a "plugin host" needed?
why can't I just call vim.rplugin('node', 'path/to/index.js'), then call functions defined in the remote process?
why do I need "decorators" (@pynvim.plugin) ? why can't I just use Lua to define commands/events in plugin/foo.lua?
@pynvim.command('MyCommand', …) vs Lua vim.api.nvim_create_user_command('MyCommand', function(...) vim.rpcrequest('MyCommand', ...))
:UpdateRemotePlugins and the "manifest" are extra state that must be resolved, refreshed, and healthchecked.
Solution
Reposition "remote plugins" as "remote modules". For example, a Node.js "remote plugin" is just a Node.js module that (1) imports "neovim", (2) can be started as a Nvim RPC client, and (3) handles requests from Nvim.
Users no longer need to globally install neovim-node-host.
Client defines RPC method handlers via setHandler().
Client calls setup() (placeholder name) which attaches and calls nvim_set_client_info() with the methods defined by setHandler().
Eliminate the "host" / "client" distinction. The client is the host (it can handle requests from Nvim).
Drop "multi tenancy". Each plugin starts its own client ("host"). Plugins do not share a "host".
Drop host-specific "sugar" for registering Nvim-side commands/autocmds (aka "bindings"). Plugins define commands/autocmds in Lua, like any other Nvim plugin. The commands call methods in the remote module.
Implementation
Implementation details
From doc/remote_plugin.txt:
Plugin hosts ... take care of most boilerplate involved in defining commands, autocmds, and functions implemented over |RPC| connections. Hosts are loaded only when one of their registered plugins require it.
We can address the above use-cases as follows:
Remote-plugins are just plain Lua plugins (plugin/foo.lua) that start API clients and call functions on the client.
Eliminates all the unnecessary complexity of trying to find foo.js/foo.py/… plugin "mains".
Make it easy from Lua to start any API client and treat it as a "remote host".
One (API client) process per plugin.
Future: Consider "sharing" client processes in the future. Out of scope for now.
Eliminates all the redundant, per-platform (js/py/…) impls that implement a "remote host".
Drop the concept of a "plugin host". We only need plain old API clients, not a "plugin host".
Nvim Lua stdlib will provide (once—not in every API client!) an ergonomic interface to:
start a "known" API client
allow the caller to specify the path to the remote-plugin "module" or "main.
define commands/autocmds/functions that call remote functions.
"Decorators" are not need for this! If it's painful to do this in Lua, fix that once, in Nvim instead of "fixing" it N times in every API client!
Examples (compare to :help remote-plugin-example):
importpynvimdefmain():
pynvim.setHandler('command_handler', command_handler)
pynvim.setHandler('autocmd_handler', autocmd_handler)
pynvim.setHandler('function_handler', function_handler)
# (placeholder name, TBD) # Attaches and calls `nvim_set_client_info()` with the `methods` defined by `setHandler()`.pynvim.setup()
main()
a fuckton of redundant documentation explaining crap like decorators, the "sync" flag, Remote plugin manifest, remote#host#RegisterPlugin, "bootstrapping" details.
instead of NvimPlugin, the remote plugin uses the exact same NvimClient type that is returned by attach().
A remote plugin is just a library that operates on the same old NvimClient that any other API client operates on.
provider#Poll()
detect()
require()
GAINS:
The "bootstrapping" is now extremely obvioius and the plugin implementor fully controls when to call vim.rplugin() from Lua.
vim.rplugin() loads the plugin "main" and returns a channel:
find the platform interpreter (node, python, …)
start the interpreter with a stdio RPC channel, targeting the plugin "main" file.
The plugin "main" file is expected to attach() to stdio, and use the resulting NvimClient to serve requests.
calls provider#Poll() until success.
returns a channel id.
NO sugar for creating commands/functions/autocmds that connect to the remote plugin.
If creating commands/functions/autocmds is cumbersome we should fix that IN GENERAL, not only for remote plugins.
How does a plugin avoid loading the interpreter (slow) on startup?
To "share" an API client we could add an optional ns (namespace) parameter to vim.rplugin(), then it could be called on-demand and re-uses the existing channel if found.
This doesn't need to be solved right now, can just use jobstart().
Update the core API clients (implement addHandler(); setup() in the client automatically calls nvim_set_client_info() with the methods defined by addHandler(); let jobstart() invoke the module directly):
Problem
The "remote plugin" model currently is too complex, and 99% of the complexity provides very little benefit.
node-client:go-client:vim.rplugin('node', 'path/to/index.js'), then call functions defined in the remote process?@pynvim.plugin) ? why can't I just use Lua to define commands/events inplugin/foo.lua?@pynvim.command('MyCommand', …)vs Luavim.api.nvim_create_user_command('MyCommand', function(...) vim.rpcrequest('MyCommand', ...)):UpdateRemotePluginsand the "manifest" are extra state that must be resolved, refreshed, and healthchecked.Solution
neovim-node-host.setHandler().setup()(placeholder name) which attaches and callsnvim_set_client_info()with themethodsdefined bysetHandler().Implementation
Implementation details
From
doc/remote_plugin.txt:We can address the above use-cases as follows:
plugin/foo.lua) that start API clients and call functions on the client.foo.js/foo.py/… plugin "mains".:help remote-plugin-example):"Remote" Python code:
remote#host#RegisterPlugin, "bootstrapping" details.rplugin/node/runtimepath directory:UpdateRemotePlugins, rplugin "manifest"remote#host#PluginsForHostNvimPlugin.registerFunction,NvimPlugin.registerAutocmd,NvimPlugin.registerCommandNvimPlugin, the remote plugin uses the exact sameNvimClienttype that is returned byattach().A remote plugin is just a library that operates on the same old
NvimClientthat any other API client operates on.provider#Poll()detect()require()vim.rplugin()from Lua.vim.rplugin()loads the plugin "main" and returns a channel:node,python, …)attach()to stdio, and use the resultingNvimClientto serve requests.provider#Poll()until success.If creating commands/functions/autocmds is cumbersome we should fix that IN GENERAL, not only for remote plugins.
FAQ
ns(namespace) parameter tovim.rplugin(), then it could be called on-demand and re-uses the existing channel if found.jobstart().Related
Work plan
:UpdateRemotePlugins.:help remote-plugin-migrateaddHandler();setup()in the client automatically callsnvim_set_client_info()with themethodsdefined byaddHandler(); letjobstart()invoke the module directly):g:node_host_prog, so it can point tonodeneovim-node-hostwill continue to be accepted; the path tonodewill be derived by inspecting the shebang inneovim-node-host.g:ruby_host_prog, so it can point torubyneovim-ruby-hostwill continue to be accepted; the path torubywill be derived by inspecting the shebang inneovim-ruby-host.:checkhealth.:help remote-plugin.g:node_host_progto point tonode, remove support forneovim-node-host.g:ruby_host_progto point toruby, remove support forneovim-ruby-host.