-
-
Notifications
You must be signed in to change notification settings - Fork 6.3k
API: buffer notifications #7917
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Events should have a prefix to distinguish builtin events from events sent by user code ( |
Ok, how about
|
No, this would make it more easy to write racy code, subscription and full buffer send should be the same method call when applicable. I would rather have a separate method to stop subscription.
I think we should attempt at reducing copying and allocation before we consider to add user configuration for memory limits. |
Ok, I will introduce a second function.
I was afraid you were going to say something like this :D Are you positive a channel capacity of 10MB can be made sufficient to transfer a 1GB file in a sane number of notifications? Do you have something particular in mind? |
I'm thinking of event names that maybe contains "lines" to mirror get_lines/set_lines. Also I would replace |
How do you mean chunking solve the allocation problem? If the client hasn't read the first chunk when nvim wants to send the second, what happens? |
I think it makes far more sense to block the channel when the buffer fills, encourage clients to drain the buffer as fast as they can (thus moving the buffer size explosion to the client), and have a user setting for client timeout (so users who want neovim to block for multiple second sometimes need to enable it themselves), than it does for clients to ask neovim for more buffer size. this mode is still helped greatly by copy on write, which may be able to remove the need for large channel buffer sizes |
We'd have to change that if we ever want to send partial lines though. That's why I'm kinda leaning towards not having
I really don't know, I was hoping something along the lines of "chunk it up and hope the client fetches the chunks fast enough".
Will do. |
I'm sorry to be "that one" but "chunking" doesn't say me anything unless you specify what decision rule nvim will apply at the chunk boundary (that it couldn't apply otherwise) |
No, worries, I'm really just "handwaving" in the worst possible way here. Note that the |
Limiting chunk size surely has other benefits as it allows interleaving a smaller unrelated message without waiting for the full 1GB. But that is a separate problem (head-of-line problem). I will investigate if memline can support COW-references without going insane (that would mean zero allocation of line contents whenever the line is still in undo history, even if it was changed), until then (in this PR) I think we should just increase the memory limit. |
Sounds good :) To what should I increase it? I went with 2GB locally, but I'm unsure if there are consequences of just using a large value. |
Let's go for that unless someone protests. Alternatively, one possibility could be to make the memory quota shared among all channels, and at OOM close the one with largest memory consumption... |
runtime/doc/msgpack_rpc.txt
Outdated
`"nvim_buf_live_updates"`API method to activate Live Update events for a | ||
specific buffer. For example, in python > | ||
|
||
import sys, neovim |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this belongs here. At this point we can assume that the reader is aware of clients, attach-ment etc. Also we shouldn't start with a language-specific example, just describe the API functionality. (See :help ui-intro
for comparison)
runtime/doc/msgpack_rpc.txt
Outdated
The co-process will start receiving the notification events which will be | ||
equivilent to the following |rpcnotify()| calls: | ||
|
||
1. rpcnotify({channel}, "LiveUpdateStart", *LiveUpdateStart* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rpcnotfy
shouldn't be used here, the fundamental concept is msgpack notifications (and what's relevant for the reader is how they are represented at the receiving end, which we don't control). Just mention name + [argument, list]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Like this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Possibly, but I would like to make events visually distinct from methods in some way, maybe by using [ ]
argument list.
8d75ac7
to
d2f3bd9
Compare
@KillTheMule Thanks for picking this up! I haven't had energy to keep this going after so long, so it'd be absolutely awesome if you can get it across the finish line. WRT to renaming events from "LiveUpdate" to "[something else]", remember to make the name something that is easy to google. :-) WRT chunking ... when the LiveUpdateStart message is being generated, you could count the number of characters in each line as it is added to the array, and start a new chunk when the number of characters goes over some predefined limit. I don't know what the limit should be, but you could set it low enough that you never hit the channel buffer limits. And if there's a single line of code that's too many characters even on its own, you could just disable LiveUpdates and return an error message saying Thanks again for taking this on - I look forward to a future version of neovim with "Merged #7917" in the release notes. 👍 |
Yeah, that's basically what I meant to say :) But since the buffer capacity is 10MB right now, sending a 1GB file would probably produce a lot of overhead. We'll just go with upping the capacity for now. @bfredl So, for naming, we have the API functions, which are called |
I made the changes @bfredl requested to the Here are the C functions we have (from
So how about we replace that with
Just throwing out ideas, get me something better (or what you like better, I don't mind), and I'll put it in :) |
Also, this fails on windows with Third, this fails on windows with @janlazo You seem to know a lot about windows things, could you have a look? Unfortunately, I can't test on windows locally... (e) justin says on gitter:
|
b84c411
to
7bccf03
Compare
where = alter_slashes(where) | ||
print("serverstart('"..where.."')") | ||
--eval("serverstart('"..where.."')") | ||
helpers.command("call serverstart('"..where.."')") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really need multiple servers for this? Can't we just connect multiple times to v:servername
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, adjusted that.
QB/Travis failures unrelated. AV failure seems geniune, but I have not idea how to go about it, I could not reproduce it manually. I'll do some desperate debugging attempts, but hopefully someone can lend me a hand. Other than that, I think what's needed is a decision about naming. That's it :) |
@chemzqm In general attachment is on a channel basis (same as UI). Rather the host should allow multiple plugins to share a buffer attachment. I might prototype this in the python host, to show the general pattern. This will have other benefits: in any sync context |
@bfredl I've tested that this could happen for different node plugins let buffer = null
async function getLines () {
let {nvim} = this
buffer = await nvim.buffer
await buffer.request('nvim_buf_attach', [buffer, false, {}])
}
async function unbind() {
await buffer.request('nvim_buf_detach', [buffer])
}
module.exports = (plugin) => {
plugin.registerCommand('LiveListen', getLines.bind(plugin), {sync: false})
plugin.registerCommand('LiveunListen', unbind.bind(plugin), {sync: false})
plugin.nvim.on('notification', (name, args) => {
if (name === 'nvim_buf_lines_event') {
let [buf, tick, firstline, lastline, linedata, more] = args
console.error(`changes: ${firstline} ${lastline}`)
}
})
} let buffer = null
async function getLines () {
let {nvim} = this
buffer = await nvim.buffer
await buffer.request('nvim_buf_attach', [buffer, false, {}])
}
module.exports = (plugin) => {
plugin.registerCommand('LiveNotify', getLines.bind(plugin), {sync: false})
plugin.nvim.on('notification', (name, args) => {
if (name === 'nvim_buf_lines_event') {
let [buf, tick, firstline, lastline, linedata, more] = args
console.error(`notify: ${firstline} ${lastline}`)
}
})
} Detach called in one plugin would cause notification not working in other plugins. There is a detach event |
Well the point is that the plugin won't call the nvim methods directly, rather helper functions in the host. The host can keep refcounting on the buffer, and only call detach when the number of plugins using it goes to zero. I think I have an old unfinished branch for this in python, seems like a good time for me to revive it.... |
@bfredl But there would some other problem for a host to manage the count of |
@chemzqm That sounds to me more like an "accidental" problem (change the helper functions to expose the most convenient interface to plugins) rather than a "fundamental" problem (that helper function solution won't work at all) My thought for python host would be to just reify the subscription rather than introduce plugin identity, so something like
and the plugin could later do The alternative for me, if we want nvim core to be more involved, would be to introduce some kind of sub-channel routing mechanism, where each |
Wait, am I missing something? Registering for updats happens via channel_id. Am I wrong in thinking that every remote plugin gets its own channel? So what I think you're describing shouldn't happen, @chemzqm . I can't read your code, but maybe I can cook up a test for that. Or am I off with something here? (e) Btw, calling attach multiple times is equivalent to calling it once. (ee) Actually this test tests the scenario, doesn't it? We're detaching here, but still receive notifications here. |
@KillTheMule remote plugins that use the registration mechanism will all run in a host shared among all such plugins in the same language. My thought was to add a thin layer in the host to make sure multiple plugins can subscribe and only get callbacks when their buffers has an event. |
Oh, ok, so there even are docs for this feature :) Imho, that would be the job of the host indeed. |
Nicely done. Thank you @phodge @KillTheMule @bfredl @chemzqm and @ZyX-I . I made some superficial changes in the merge-commit. |
@@ -1234,7 +1234,8 @@ static void refresh_screen(Terminal *term, buf_T *buf) | |||
|
|||
int change_start = row_to_linenr(term, term->invalid_start); | |||
int change_end = change_start + changed; | |||
changed_lines(change_start, 0, change_end, added); | |||
// Note: don't send nvim_buf_lines_event event for a :terminal buffer | |||
changed_lines(change_start, 0, change_end, added, false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@KillTheMule is there at test to check that :terminal
buffers do not trigger nvim_buf_lines_event
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No there isn't. But now that you mention it: Why shouldn't they? I mean, they're buffers after all, if someone wants to subscribe to their updates, I don't see I reason why we should deny them. Or is there a specific reason I'm not seeing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, why not. I was just going by the comment here.
Would still want a test either way
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll send a PR, might take some time though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I want to monitor the content of a terminal buffer that loaded from session file, it seems no other options except using the buffer updates API.
I began to look into python host integration: neovim/pynvim#340 |
doc: termios defaults. ref neovim#6992 doc: :help shell-powershell doc: provider: Python minimum version is 2.7, 3.4 doc: remove :!start special-case. neovim#5844 mention neovim#7917 change which accepts empty Array for Dictionary parameter
doc: termios defaults. ref neovim#6992 doc: :help shell-powershell doc: provider: Python minimum version is 2.7, 3.4 doc: remove :!start special-case. neovim#5844 doc: mention neovim#7917 change which accepts empty Array for Dictionary parameter doc: <Cmd> pseudokey doc: lmap change neovim#5658 doc: -s, -es
doc: termios defaults. ref neovim#6992 doc: :help shell-powershell doc: provider: Python minimum version is 2.7, 3.4 doc: remove :!start special-case. neovim#5844 doc: mention neovim#7917 change which accepts empty Array for Dictionary parameter doc: <Cmd> pseudokey doc: lmap change neovim#5658 doc: -s, -es
FEATURES: 3cc7ebf #7234 built-in VimL expression parser 6a7c904 #4419 implement <Cmd> key to invoke command in any mode b836328 #7679 'startup: treat stdin as text instead of commands' 58b210e :digraphs : highlight with hl-SpecialKey #2690 7a13611 #8276 'startup: Let `-s -` read from stdin' 1e71978 events: VimSuspend, VimResume #8280 1e7d5e8 #6272 'stdpath()' f96d99a #8247 server: introduce --listen e8c39f7 #8226 insert-mode: interpret unmapped META as ESC 98e7112 msg: do not scroll entire screen (#8088) f72630b #8055 let negative 'writedelay' show all redraws 5d2dd2e win: has("wsl") on Windows Subsystem for Linux #7330 a4f6cec cmdline: CmdlineEnter and CmdlineLeave autocommands (#7422) 207b7ca #6844 channels: support buffered output and bytes sockets/stdio API: f85cbea #7917 API: buffer updates 418abfc #6743 API: list information about all channels/jobs. 36b2e3f #8375 API: nvim_get_commands 273d2cd #8329 API: Make nvim_set_option() update `:verbose set …` 8d40b36 #8371 API: more reliable/descriptive VimL errors ebb1acb #8353 API: nvim_call_dict_function 9f994bb #8004 API: nvim_list_uis 3405704 #7520 API/UI: forward option updates to UIs 911b1e4 #7821 API: improve nvim_command_output WINDOWS OS: 9cefd83 #8084, #8516 build/win: support MSVC ee4e1fd win: Fix reading content from stdin (#8267) TUI: ffb8904 #8309 TUI: add support for mouse release events in urxvt 8d5a46e #8081 TUI: implement "standout" attribute 6071637 TUI: support TERM=konsole-256color 67848c0 #7653 TUI: report TUI info with -V3 ('verbose' >= 3) 3d0ee17 TUI/rxvt: enable focus-reporting d109f56 #7640 TUI: 'term' option: reflect effective terminal behavior FIXES: ed6a113 #8273 'job-control: avoid kill-timer race' 4e02f1a #8107 'jobs: separate process-group' 451c48a terminal: flush vterm output buffer on pty output #8486 5d6732f :checkhealth fixes #8335 53f11dc #8218 'Fix errors reported by PVS' d05712f inccommand: pause :terminal redraws (#8307) 51af911 inccommand: do not execute trailing commands #8256 84359a4 terminal: resize to the max dimensions (#8249) d49c1dd #8228 Make vim_fgets() return the same values as in Vim 60e96a4 screen: winhl=Normal:Background should not override syntax (#8093) 0c59ac1 #5908 'shada: Also save numbered marks' ba87a2c cscope: ignore EINTR while reading the prompt (#8079) b1412dc #7971 ':terminal Enter/Leave should not increment jumplist' 3a5721e TUI: libtermkey: force CSI driver for mouse input #7948 6ff13d7 #7720 TUI: faster startup 1c6e956 #7862 TUI: fix resize-related segfaults a58c909 #7676 TUI: always hide cursor when flushing, never flush buffers during unibilium output 303e1df #7624 TUI: disable BCE almost always 249bdb0 #7761 mark: Make sure that jumplist item will not have zero lnum 6f41ce0 #7704 macOS: Set $LANG based on the system locale a043899 #7633 'Retry fgets on EINTR' CHANGES: ad60927 #8304 default to 'nofsync' f3f1970 #8035 defaults: 'fillchars' a6052c7 #7984 defaults: sidescroll=1 b69fa86 #7888 defaults: enable cscopeverbose 7c4bb23 defaults: do :filetype stuff unless explicitly "off" 2aa308c #5658 'Apply :lmap in macros' 8ce6393 terminal: Leave 'relativenumber' alone (#8360) e46534b #4486 refactor: Remove maxmem, maxmemtot options 131aad9 win: defaults: 'shellcmdflag', 'shellxquote' #7343 c57d315 #8031 jobwait(): return -2 on interrupt also with timeout 6452831 clipboard: macOS: fallback to tmux if pbcopy is broken #7940 300d365 #7919 Make 'langnoremap' apply directly after a map ada1956 #7880 'lua/executor: Remove lightuserdata' INTERNAL: de0a954 #7806 internal statistics for list impl dee78a4 #7708 rewrite internal list impl
@@ -223,6 +223,11 @@ for i = 1, #functions do | |||
output:write('\n } else if (args.items['..(j - 1)..'].type == kObjectTypeInteger && args.items['..(j - 1)..'].data.integer >= 0) {') | |||
output:write('\n '..converted..' = (handle_T)args.items['..(j - 1)..'].data.integer;') | |||
end | |||
-- accept empty lua tables as empty dictionarys |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"lua" in the above comment is a bit misleading. This applies to all msgpack requests--though it's probably only useful for a Lua client.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, I was somewhat in "lua-land" when I did this.
Since @phodge seems to have run out of time, I hope you don't mind me taking over your PR. Thanks for your work so far :)
I've added an argument to
nvim_buf_live_udpates
that notes if the full buffer should be sent upon starting the updates. I've also added a test, should work.TODO:
num_replaced
withlastline
, akin toset_lines
new_pipename()
for the multiple channel testSend a message to the client and abort (only happens with really large diffs/files on 32bit systems anyways).Put in asserts for future proofness, can't happen today anyways.Questions:
nvim_buf_live_updates
is fine, as long as we want to call this functionalitylive updates
. Any thoughts?Is the documentation in the right place? Let me know where it should live. Is there another place this needs to be added to?As it stands now, registering and unregistering for the updates is done through the same function and takes a boolean argument to distinguish between registering and unregistering, e.g.nvim_buf_live_updates(true)
vs.nvim_buf_live_updates(false)
. Since I've added a flag to maybe not send the whole buffer, those would benvim_buf_live_updates(true, true)
vs.nvim_buf_live_updates(false, true)
where in the 2nd case the second argument is simply ignored. Feels ugly and weird. Should I make the 2 different functions? Or should there be a function to request the full buffer separately?As I mentioned in the original PR, I'm getting trouble from the channel capacity. My idea would be to allow this to be configured by the plugin (i.e. introduce methodsnvim_get_channel_capacity
andnvim_set_channel_capacity
), and keep the current default. I'd also think this should be another PR (I'd do it), so unless someone pipes up, I'll not include this here.All comments and ideas welcome, although please keep in mind I'm a beginner programmer.
cc @justinmk, @bfredl, @phodge, @jamessan, @lunixbochs, @bryphe,@Chillee
Note: Unless you like function names like
function_to_make_nvim_send_lines_when_a_buffer_updated
, DON'T leave me alone with the naming issues. You have been warned :P