-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
Improve multiple neovim instances support (viminfo) #999
Comments
If the viminfo file format should change (i.e. get incompatible with vim), we should rename it and also ensure future extensibility without breaking the old formatting. Plain TLV approach (that is e.g. "these 256 bytes have a type and/or version field I don't understand/support and thus I can skip them while issuing some warning") might be a viable concept e.g. for using msgpack directly for the data. |
I think I have a good option:
If this variant is acceptable I can write a PR for this. Note: I am going to change format, not implement additional options for sharing data between nvim instances. Some simple merging algorythm will be added, but duplicating full set of zsh features is not needed before the release unlike format changes and merging function will not care a tiny bit about locks. Not sure whether or not I will implement additional information saving (i.e. information for compatibility and timestamps): depends on the complexity. Key change is new format, not new features. Ping @tarruda. |
I have nothing against what you proposed, it's not like changing the format will have serious implications. In fact, using a standard serialization format such as msgpack means it can be parsed more easily by other programs Have you thought about how to synchronize access to the file? BTW where does the "ShaDa" name comes from? |
Since this is merely a log I was originally going to just take code from zsh. (Though just taking code means that you now only control shada length in term of top-level item concats: can’t say “1000 lines of history” (for each history kind), only “100 000” elements in shada file whatever elements will be.) At the first stage this will be simple “read shada -> merge -> write shada.tmp -> rename”. Without any kind of synchronization. I guess though that when I will start looking at how merging stage should be implemented I may deduce slightly different format.
SHAred DAta. It is really great way of constructing easily memorable identifiers if you don’t have enough letters for good abbreviation. |
I have a positive experience with this simple approach (and it should be pretty fast in this case as the whole file won't need to be read through due to time stamps). Btw nice format specification, I looking forward to using it :) |
@ZyX-I Your shared-data spec looks something like a subset of the editor "state". What do you think about generalizing this concept as the main (only) serialization format for the Neovim editor state? This would be useful for spawning child |
@justinmk it should already be possible to run vimscript in parallel after #1446 is merged, there's no need to restore editor state. In fact, I think spawning with Besides, saving the editor state is much more complex than what @ZyX-I is proposing, it would involve not only refactoring every global variable, but also static variables in each module(which there are plenty). Not to mention that there things out of our control(file descriptors, child processes...). Still, being able to serialize most editor state is a worthly long term goal. I might open an issue to discuss a plan for distributing work in entry-level tasks. |
Timestamps come into the action when it needs to be deduced which of the items need to be kept during merging. Items in the file are not going to appear sorted because I have no intention in keeping the whole file contents in memory, twice (three times considering that shada file needs to be read back). For this reasons I will either have to add file size and some offsets into the header (works only as long as used approach is that simple one, but currently used format is supposed to be more zsh-history-like with possible support of all its features in the future) or always process the whole file should I need only part of it (note: current code for some reasons defines flags which tell NeoVim to read only marks or only data for v:oldfiles or everything else; did not yet check where such capabilities are actually used so cannot say whether new format will add overhead to some actions or will remove it from startup). |
@tarruda About static variables: I usually omit them as long as possible, keeping only |
Still, external merge sort should be easy enough to implement to avoid this issue of keeping the whole file in memory ;) |
@tarruda If some state is not sent, the results of |
I am wondering how is this supposed to help?
|
@ZyX-I right, it seems I completely misunderstood your intent. E.g. I didn't assume shada won't be shared among parallel neovim instances (I thought, that after adding a single new item, the one shared shada file will get updated and the new parts automatically re-read by other neovim instances as long as monotonic time source is used for time stamps). Regarding the concat, this shouldn't be an issue - when starting a new instance, you anyway need to read the whole shada file to get the full history and therefore it's a perfect time to sort the file if any time-incompatibility is encountered. |
It is never going to happen by default. Even zsh with sharehistory option is not that intrusive: shada not only contains history, but a jumplist and file-local and global marks as well. So even if option like SHARE_HISTORY will be added this will be a list showing what exactly will be read back. And I explicitly said that I am not going to do this in first PR regarding the issue.
I am not going to write file at startup. And especially I am not going to do such possibly time-consuming task as sorting at startup as well. |
Let me clarify some bits. By history I meant everything done/saved in the past. I'm not much familiar with the way how zhs does it under the hood, but I'm using my own setup for shells having the non-POSIX history command and thanks to it, I'm doing it exactly the "intrusive" way and it works well (even on different systems with different shells and configurations). But of course, such central sharing is rather subjective.
I meant something like shada_file = open_file_and_return_stream()
shada_history = {} // empty list
while (r = read_next_record(shada_file)) {
x = shada_history.last_item_index()
while (r.timestamp() < shada_history.at(x)) --x; // this might be a binary search
shada_history.insert_after(x, r)
} I.e. no temporary file, no more-than-once reading of the shada file, logarithmic performance in the worst case. And that's already pretty fast. |
By history I mean only lines (commands, searches, etc) typed in command mode. Zsh history files have only this and thus are not intrusive with
This is not external merge sort. And this is exactly what will leave three instances of shada file: one shada_history from current nvim instance, one is shada_history from current nvim instance expressed in other structures (this one is source of the data), one shada_history from shada file and one is the result of the merge. The result of the merge can be put in either one of shada_histories that are represented as such (in-place list merging), so three. At shutdown I was actually going to pretend that both history in shada file and history in current NeoVim instance are already sorted and thus keep minimum amount of additional memory by using much simpler sorted lists merging (additionally needs an explicit test that this behaves well enough when lists are not actually sorted). (Note: things like marks occupy less memory and are not history lists, so they are not going to cause any problems.) I think this will make code both not allocate much additional memory and use only one iteration to process shada file which needs to be merged with current instance data. Also I thought you meant I need to sort the whole file and then write it. Amount of things that needs to be sorted is less then amount of things which are stored in the shada file and in any case you don’t need to sort the whole file at startup because two shada file entries may contain different type of data: it is useless to first position command history line before search history line and just then put these lines into separate lists without keeping information about their ordering anywhere. Only entries with identical type (for history specifically: and identical history type) need sorting. |
Thanks for explanation of the terms you use. Now I see your point and agree with your conclusions. And yes, the last pseudo code had nothing to do with external merge sort as it was a different situation than I originally understood. |
Found a problem with marks: I added timestamp field to mark definition (including local marks) and modified corresponding code in Persistent undo file is the next think which needs attention because it can’t be extended at all. |
There is a question: &viminfo option contains such an interesting suboption
I am going to extend this option according to its description (“Maximum size of an item in Kbyte”), except that it is ignored for buffer list + description is off by 3 unsigned integers (type, timestamp, length):
Another possible option is narrow it to registers only like it was in Vim, but make this behaviour official. Note that 10240 bytes of an item ≠ register text 10240 bytes long: register is saved as |
Note that one can still limit register size by using |
I would prefer the first solution with a minor change in wording - from |
@dumblob I use |
@ZyX-I correct, but the point with the word content stays the same. |
@dumblob I have no objections to the word “content”, just to |
What works: 1. ShaDa file dumping: header, jump list, history, search patterns, substitute strings. What does not: 1. ShaDa file dumping: registers. 2. ShaDa file reading. Most was not tested. TODO: 1. Merging. 2. Reading (ShaDa file reading is present, it just does not work) history, local marks, jump and buffer lists. 3. Documentation update. 4. Converting some data from &encoding. 5. Safer variant of dumping viminfo (dump to temporary file then rename). 6. Removing old viminfo code (currently masked with `#if 0` in a ShaDa file for reference). Ref neovim#999.
What works: 1. ShaDa file dumping: header, jump list, history, search patterns, substitute strings. What does not: 1. ShaDa file dumping: registers. 2. ShaDa file reading. Most was not tested. TODO: 1. Merging. 2. Reading (ShaDa file reading is present, it just does not work) history, local marks, jump and buffer lists. 3. Documentation update. 4. Converting some data from &encoding. 5. Safer variant of dumping viminfo (dump to temporary file then rename). 6. Removing old viminfo code (currently masked with `#if 0` in a ShaDa file for reference). Ref neovim#999.
What works: 1. ShaDa file dumping: header, jump list, history, search patterns, substitute strings. What does not: 1. ShaDa file dumping: registers. 2. ShaDa file reading. Most was not tested. TODO: 1. Merging. 2. Reading (ShaDa file reading is present, it just does not work) history, local marks, jump and buffer lists. 3. Documentation update. 4. Converting some data from &encoding. 5. Safer variant of dumping viminfo (dump to temporary file then rename). 6. Removing old viminfo code (currently masked with `#if 0` in a ShaDa file for reference). Ref neovim#999.
What works: 1. ShaDa file dumping: header, jump list, history, search patterns, substitute strings. What does not: 1. ShaDa file dumping: registers. 2. ShaDa file reading. Most was not tested. TODO: 1. Merging. 2. Reading (ShaDa file reading is present, it just does not work) history, local marks, jump and buffer lists. 3. Documentation update. 4. Converting some data from &encoding. 5. Safer variant of dumping viminfo (dump to temporary file then rename). 6. Removing old viminfo code (currently masked with `#if 0` in a ShaDa file for reference). Ref neovim#999.
closed by #2506 |
There is one known problem with Vim: if you launched two instances, did some things in both of them and quit only data from last Vim is saved into viminfo: Vim does not bother reading viminfo before writing it (except for marks). Thus my suggestion:
:h viminfo-!
), registers, uppercase marks should be tagged per-element (one tag for one variable, register and mark). Default tag (for unset or converted values) is-inf
.:h viminfo-'
) should be tagged on its own, and each saved lowercase mark should be tagged as well. Tags on edited files are updated whenever file is closed (if file is not closed then it should use exit time).flock()
, but never saw it working. And using lock files is slow and requires detection of the situation “other neovim crashed before releasing lock”.Before implementing any of the above it is good to look at zsh: it has such interesting options as INC_APPEND_HISTORY (update history file as soon as history item appears), APPEND_HISTORY (the most simple way to avoid problem Vim has) and SHARE_HISTORY (history file is read each time it is updated, so all zsh instances have the same history list always).
It may be better to take alternative approach: write viminfo as a log and when it grows too large clean it. This makes it possible to directly take code from zsh and support all of the above options. Log should look like this:
timestamp:action:arguments
:(Note: using NUL to terminate file name. Newline can also be present in the filename and I do not want to bother with escaping. First NUL is there to indicate that newline may be in the argument.)
Though I am not sure whether we should make this log human-readable: using
uint64_t
casted tochar[8]
(need to ensure endianness though), one character for action (withswitch
) and special parser for each of the actions should work much faster and reqire much less on-disk space. Will need a tool to show this log in a human-readable fashion, but this is really simple.By cleaning I mean first removing successive elements that obsolete each other (e.g. two elements setting one uppercase mark). Then remove oldest duplicate elements from history (e.g. two successive appends of identical history lines). Last: remove oldest items that makes some list (of old files, history lines, etc) grow too large.
This should be decided before first release because we do not want to add compatibility code, do we?
Want to back this issue? Place a bounty on it! We accept bounties via Bountysource.
The text was updated successfully, but these errors were encountered: