-
-
Couldn't load subscription status.
- Fork 6.4k
Open
Labels
apilibnvim, Nvim RPC APIlibnvim, Nvim RPC APIpluginremote-pluginplugins as RPC coprocesses (node.js, python, etc)plugins as RPC coprocesses (node.js, python, etc)
Milestone
Description
Problem
The "remote plugin" model currently is too complex, and 99% of the complexity provides very little benefit.
- Lifecycle is complicated, unnecessarily hard to test, troubleshoot, and explain to plugin authors.
- Every "remote plugin host" must implement 1000s of lines of code to solve the same problem in every API client. This is a pointless waste of time.
- Example: all of the code below is just for the "remote plugin" implementation in:
node-client:go-client:
- The benefits of the extra code are
- "multi-tenancy" (one node process for all node rplugins).
- "decorators" can be used in the remote module to define Nvim commands/autocmds.
- Example: all of the code below is just for the "remote plugin" implementation in:
- 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 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
- 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 callsnvim_set_client_info()with themethodsdefined bysetHandler().
- Users no longer need to globally install
- 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".
- Eliminates all the unnecessary complexity of trying to find
- 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):- go-client example
- pynvim example:
"Remote" Python code:"Local" Lua code:import pynvim def main(): 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()
local rplugin = vim.rplugin('python', 'path/to/init.py') vim.api.nvim_create_user_command('Cmd', function(args) vim.rpcrequest(rplugin.chan_id, 'command_handler', args.args, args.range) end, { nargs = '*' }) vim.api.nvim_create_autocmd({'BufEnter'}, { pattern = {"*.c", "*.h"}, callback = function(ev) vim.rpcrequest(rplugin.chan_id, 'autocmd_handler', ev.file) end }) function my_func(...) vim.rpcrequest(rplugin.chan_id, 'function_handler', {...}) end
- One (API client) process per plugin.
- With the above design...
- ELIMINATES:
- a fuckton of redundant documentation explaining crap like decorators, the "sync" flag, Remote plugin manifest,
remote#host#RegisterPlugin, "bootstrapping" details. - a fuckton of code: neovim/src/plugin decorators/src/plugin neovim/src/host
rplugin/node/runtimepath directory- "registration",
:UpdateRemotePlugins, rplugin "manifest" remote#host#PluginsForHostNvimPlugin.registerFunction,NvimPlugin.registerAutocmd,NvimPlugin.registerCommand
- a fuckton of redundant documentation explaining crap like decorators, the "sync" flag, Remote plugin manifest,
- STILL NEEDED:
- instead of
NvimPlugin, the remote plugin uses the exact sameNvimClienttype that is returned byattach().
A remote plugin is just a library that operates on the same oldNvimClientthat any other API client operates on. provider#Poll()detect()require()
- instead of
- 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 resultingNvimClientto serve requests.
- The plugin "main" file is expected to
- calls
provider#Poll()until success. - returns a channel id.
- find the platform interpreter (
- 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.vim.api.nvim_create_autocmd({'BufEnter'}, { pattern = {"*.c", "*.h"}, callback = function(ev) vim.rpcrequest(rplugin.chan_id, 'autocmd_handler', ev.file) end })
- The "bootstrapping" is now extremely obvioius and the plugin implementor fully controls when to call
- ELIMINATES:
FAQ
- How does a plugin avoid loading the interpreter (slow) on startup?
- To "share" an API client we could add an optional
ns(namespace) parameter tovim.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().
- This doesn't need to be solved right now, can just use
- To "share" an API client we could add an optional
Related
- The above proposal has similarities to nvim-yarp cc @roxma
- But unlike nvim-yarp, we can go further and eliminate the concept of "registering" anything.
Work plan
- Add a util function that migrates legacy plugins using their "specs".
- Deprecate
:UpdateRemotePlugins.- point to
:help remote-plugin-migrate - ask user to report legacy plugins that haven't migrated yet: Which plugins use :UpdateRemotePlugins ? #29270
- point to
- Update the core API clients (implement
addHandler();setup()in the client automatically callsnvim_set_client_info()with themethodsdefined byaddHandler(); letjobstart()invoke the module directly): - Update handling of
g:node_host_prog, so it can point tonode- Transitional phase:
neovim-node-hostwill continue to be accepted; the path tonodewill be derived by inspecting the shebang inneovim-node-host.
- Transitional phase:
- Update handling of
g:ruby_host_prog, so it can point toruby- Transitional phase:
neovim-ruby-hostwill continue to be accepted; the path torubywill be derived by inspecting the shebang inneovim-ruby-host.
- Transitional phase:
- Update
:checkhealth. - Update
:help remote-plugin. - (Nvim 0.12) Remove old rplugin-related code
- (Nvim 0.12) Require
g:node_host_progto point tonode, remove support forneovim-node-host. - (Nvim 0.12) Require
g:ruby_host_progto point toruby, remove support forneovim-ruby-host.
jonatan-branting, noahfrederick, adrian5, Kampouse, vuki656 and 37 moretowry, miyl, Lattay, brianhuster, sohanglal and 1 moretowry, miyl, Lattay, brianhuster, ngortheone and 3 morereo101, seandewar, jupblb, gpanders, Kampouse and 15 morereo101, seandewar, Kampouse, alexgenco, Shougo and 7 more
Metadata
Metadata
Assignees
Labels
apilibnvim, Nvim RPC APIlibnvim, Nvim RPC APIpluginremote-pluginplugins as RPC coprocesses (node.js, python, etc)plugins as RPC coprocesses (node.js, python, etc)