Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

headless neovim as custom "remote command handler"#36822

stv17 started this conversation inGeneral
Discussion options

Hi, and thanks for the work on neovim. I'm an avid user and fully customize my workflow (but still haven't jumped onto nightly)

I have some questions regarding the use of neovim as a "remote command handler". This may be a long post but please bear in mind that my aim is to share the pain point I bumped into, how I tackled it and if it's of any use for upstream neovim.

This may be related to this issue:Nvim can be a Lua host; Lua plugins can define RPC method/request handlers #31868

My use case

I'm an embedded software developer and work with vendor linux kernel trees and Android checkouts daily. These projects come with a big git history that I often peek into looking for patches or insight. I've been using (vim->neovim) for quite some time and always relied on vim-fugitive for git integration.

The problem is that the single-thread nature of vim/nvim results in some git operations to lock up the UI for ~10 secs. I bumped into this every time I attempted to look at the history of a file or a log graph using vim-flog (or GV). I took a stab at my own (lua) replica of the logic found in vim-fugitive to retrieve the commit history of a file and tried to work around the lockup problem.

My attempted solution

Of course I wanted to use the nvim API functions and so found that libuv threads were hard for me to use. In the end, and after looking at neovim open issues and how fzf-lua tackled some things, I came up with a module that does the following:

  • The user "registers" a command passing in both "completion callback" and "implementation" functions.
  • Spins up a headless neovim process for RPC via unix pipe.
  • I then invokerequire("rcmd").send_remote_command("git_log_follow", { filename, gitdir }). The first time this is executed it lazily spins up the process, and then send the command request via RPC.
  • I send a call for RPC "nvim_exec_lua" API function with a small lua script to pass the args and trigger my module with the implementation to do the work.
  • Some time later I get the callback function invoked from the remote neovim instance and then just present the data in a float, effectively having skipped the lockup in my main neovim process.

Here's a link to a sequence diagram for graphical illustration (dont know how long this links remains valid for):
mermaid.ink sequence diag

Something I had to solve was how to pass lua dicts through RPC to the remote instance, in order return the commit history in a more friendly form. I took a cue from fzf-lua and just included the single-file lib "serpent.lua" for serialization and it worked very well.
See:https://github.com/pkulchenko/serpent
On top of this I have a small "xfer" module that introduces "transactions" and handles the sending of big tables. This is half-baked really, but a table can be split in packets for TX, getting RX confirmation for each one, and are completed once all packets arrive at the remote neovim.

Environment of remote nvim?

I rely on my own config being loaded in the remote neovim process for this to work. It feels like cheating. I would like to have the remote neovim spin up with--clean and somehow instruct it to load what's minimally required for the custom command implementation.

I read#18035 regarding serializing lua functions and it seems very complex for user-setup of lua modules and general environment (but I havent tried it, though).

It seems simpler to provide a special RTP path at command-registration time, or something of the like.

Questions

  1. Is this approach completely off the mark?

I don't know about co-routines in lua but see there's some present work for them. Maybe with coroutes no additional nvim process is needed?

  1. If not, is there any way I can contribute/help to move a similar feature forward? I attempted to clean up my stuff multiple times but dont know really what my "target" is.

If you read up to this point, thank you! I've been postponing this question almost all year long.
If not, that's fine

You must be logged in to vote

Replies: 1 comment 2 replies

Comment options

I wanted to use the nvim API functions and so found that libuv threads were hard for me to use

vim.system() supports async jobs without libuv threads.

how to pass lua dicts through RPC to the remote instance,

Unless you wantstreaming, this already "just works" when you using the RPC API. Or if you want to do things manually you can serialize viavim.mpack orvim.json. Assuming you don't need to serialize functions.

I read#18035 regarding serializing lua functions and it seems very complex for user-setup of lua modules

Depends on what you want to do, but you can of course send entire scripts vianvim_exec_lua.

Is this approach completely off the mark?

Would need to know more specifics about your use-case.vim.system already supports async as mentioned. If git operations are slow, that never needs to block Nvim, since the slow part happens in thegit subprocess.

There are numerous libraries for structured concurrency discussed in#19624 , but for "slow git" that isn't fundamentally different than usingvim.system. Coroutines aren't going to help there.

Ifprocessing the git results in Nvim Lua also is slow, then yes, coordinating with anvim peer process could make sense, and then this story becomes relevant:

which would enable Lua plugins to be "API clients" easily (likehttps://github.com/neovim/node-client ).

You must be logged in to vote
2 replies
@stv17
Comment options

Hey, thanks for your reply.

Yes, processing data in a remote neovim is what I'm doing. The data comes from thevim.system() output of a git command that is executed by the headless nvim instance itself, which then parses the output text and sends back a lua dict.

The data is just git output text formatted in a similar fashion to what0Gclog from vim-fugitive produces and then parses (to the best of my knowledge). If you care to see an example, you can run the following git command in the neovim project:

git log --follow --oneline --summary"--pretty=format:\th%H\tp%P\ts%s" -- src/nvim/main.c

This outputs 2 lines per commit (the 2nd line is from --summary, most often empty). This info is needed to track filename/path changes across the git log history.

But nevermind my git use-case, this is just an example of the concept of:
"offloading work to a secondary nvim for processing with full access to the neovim API" (while preventing UI lockups).

Can I ask for your opinion on how would the user provide the logic (e.g., the lua implementation) to process the data to the remote neovim? These are my thoughts:

  • I just rely on my own config being loaded by the remote neovim. E.g., the same init.lua.
    • Thus, the custom command is also registered by the remote instance, which can then map the command to the implementation function registered.
    • I have full access to my lua modules to do what I please.
  • Loading a full user config for the remote nvim doesn't feel clean. What would be the preferred approach? (without serializing lua functions, or passing huge scripts vianvim_exec_lua).

Im already using short snippets ofnvim_exec_lua to trigger the work on the remote. For example, when requesting the server to execute a command:

localfunctionsend_request(channel,req)localrpc_args= {pid=req.client_pid,cmd=req.command,user_args=req.extra_args, }returnvim.rpcrequest(channel,'nvim_exec_lua',[[        local args = { ... }        local rpc_args = args[1]        return require("rcmd.server"):handle_command(rpc_args.pid, rpc_args.cmd, rpc_args.user_args)]], {rpc_args })end

And btw,

Unless you want streaming, this already "just works" when you using the RPC API.

Thanks, I missed this ! My little test wasn't wrapping the dict in an array and thus errored out. Now I can drop variadic args in the snippets ofnvim_exec_lua, like shown above.

@justinmk
Comment options

Loading a full user config for the remote nvim doesn't feel clean. What would be the preferred approach? (without serializing lua functions, or passing huge scripts vianvim_exec_lua).

A "user config" is just a set of packages (modules), I would not consider it hacky.

But just for "science", let's imagine a use-case where one wants to do everything through the same RPC channel, without pre-loading the modules on the remote filesystem.

That's somewhat related to the "streaming" topic, which is something we discussed early on#4971 but haven't had much demand for it (though I still consider it in-scope).

However, we actually have at least 1 streaming API:nvim_paste. It's the recommended way to input large amounts of text in a buffer, even for non-RPC scenarios.

Option 1

To send an entire set of files (Lua modules or whatever) to a remote system:

  1. on the remote system,:edit /path/to/foo.lua
  2. nvim_paste the content into the buffer
  3. :write
  4. repeat for all other files...

Option 2

You don't actually neednvim_paste.

  1. zip the files locally
  2. the local client can send chunks of the zip file to the remote, which appends the bytes to a file viaio.write().
  3. the remote unzips the file, which has all of your modules + directory tree.

P.S. We probably should have something for this built-in. It's related to theremote ssh story.

Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Category
General
Labels
apilibnvim, Nvim RPC APIchannels-rpcchannels, RPC, msgpackremote-pluginplugins as RPC coprocesses (node.js, python, etc)remoteremote UI, --remote commands, p2p / peer-to-peer
2 participants
@stv17@justinmk

[8]ページ先頭

©2009-2025 Movatter.jp