Skip to content

Commit 81fdec8

Browse files
committed
Generalized asynchronous Vim script evaluation (for vim-easytags)
The idea of forking Vim into the background to perform long running tasks that would otherwise block the user's editing session originates from the 'async-cleanup' feature branch of vim-easytags, where I picked it up from a tip by Inko Karkat (@inkarkat). Because I can see this being useful in several of my Vim plug-ins I decided to generalize the concept and implement it in vim-misc. One thing that may not be immediately obvious from the commit is why I implemented two ways for the results of asynchronous processes to feed back into the main Vim process. The reason is twofold, both having to do with Vim's client/server implementation: 1) Reading through the pull request of the mentioned feature branch it looks like Vim's client/server support on Mac OS X is either missing or problematic. 2) Running the 'async-cleanup' branch (which always used client/server communication) I saw several crashes on Linux. I have no proof but suspected the client/server communication to be the cause of this. So now there's an alternative if the client/server mechanism starts to act up (which it may very well do because it's a complex part of Vim). See also the pull request for the mentioned feature branch: xolox/vim-easytags#49
1 parent 8176763 commit 81fdec8

File tree

4 files changed

+615
-44
lines changed

4 files changed

+615
-44
lines changed

README.md

Lines changed: 164 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,168 @@ from the source code of the miscellaneous scripts using the Python module
3737

3838
<!-- Start of generated documentation -->
3939

40-
The documentation of the 85 functions below was extracted from
41-
17 Vim scripts on June 22, 2014 at 01:49.
40+
The documentation of the 89 functions below was extracted from
41+
18 Vim scripts on June 22, 2014 at 02:54.
42+
43+
### Asynchronous Vim script evaluation
44+
45+
The `xolox#misc#async#call()` function builds on top of `xolox#misc#os#exec()`
46+
to support asynchronous evaluation of Vim scripts. The first (and for now
47+
only) use case is my [vim-easytags][] plug-in which has a bunch of
48+
conflicting requirements:
49+
50+
1. I want the [vim-easytags][] plug-in to be as portable as possible.
51+
Ideally everything is implemented in Vim script because that's the only
52+
thing I can rely on to be available for all potential users of the
53+
plug-in!
54+
55+
2. Because of point one I've been forced to implement tags file reading,
56+
parsing, (fold case) sorting and writing in Vim script. This is fine for
57+
small tags files but once they grow to a couple of megabytes it becomes
58+
annoying because Vim is unresponsive during tags file updates (key
59+
presses are fortunately buffered due to Vim's input model but that
60+
doesn't make it a nice user experience :-).
61+
62+
3. I could (and did in the past) come up with all sorts of hacks to speed
63+
things up without switching away from Vim script, but none of them are
64+
going to solve the fundamental problem that Vim's unresponsive hiccups
65+
become longer as tags files grow larger.
66+
67+
By now it should be clear where this is heading: _Why not handle tags file
68+
updates in a Vim process that runs in the background without blocking the
69+
Vim process that the user is interacting with?_ It turns out that there are
70+
quite a few details to take care of, but with those out of the way, it might
71+
just work! I'm actually hoping to make asynchronous updates the default mode
72+
in [vim-easytags][]. This means I need this functionality to be as
73+
portable and robust as possible.
74+
75+
**Status:** This code has seen little testing so I wouldn't trust it too
76+
much just yet. On the other hand, as I said, my intention is to make this
77+
functionality as portable and robust as possible. You be the judge :-).
78+
79+
[vim-easytags]: http://peterodding.com/code/vim/easytags/
80+
81+
#### The `xolox#misc#async#call()` function
82+
83+
Call a Vim script function asynchronously by starting a hidden Vim process
84+
in the background. Once the function returns the hidden Vim process
85+
terminates itself. This function takes a single argument which is a
86+
dictionary with the following key/value pairs:
87+
88+
- **function** (required): The name of the Vim function to call inside
89+
the child process (a string). I suggest using an [autoload][] function
90+
for this, see below.
91+
92+
- **arguments** (optional): A list of arguments to pass to the function.
93+
This list is serialized to a string using [string()][] and deserialized
94+
using [eval()][].
95+
96+
- **callback** (optional): The name of a Vim function to call in the
97+
parent process when the child process has completed (a string).
98+
99+
- **clientserver** (optional): If this is true (1) the child process will
100+
notify the parent process when it has finished (the default is true).
101+
This works using Vim's client/server support which is not always
102+
available. As a fall back Vim's [CursorHold][] automatic command is
103+
also supported (although the effect is not quite as instantaneous :-).
104+
105+
This functionality is experimental and non trivial to use, so consider
106+
yourself warned :-).
107+
108+
**Limitations**
109+
110+
I'm making this functionality available in [vim-misc][] because I think it
111+
can be useful to other plug-ins, however if you are going to use it you
112+
should be aware of the following limitations:
113+
114+
- Because of the use of multiple processes this functionality is only
115+
suitable for 'heavy' tasks.
116+
117+
- The function arguments are serialized to a string which is passed to
118+
the hidden Vim process as a command line argument, so the amount of
119+
data you can pass will be limited by your operating environment.
120+
121+
- The hidden Vim process is explicitly isolated from the user in several
122+
ways (see below for more details). This is to make sure that the hidden
123+
Vim processes are fast and don't clobber the user's editing sessions in
124+
any way.
125+
126+
**Changes to how Vim normally works**
127+
128+
You have to be aware that the hidden Vim process is initialized in a
129+
specific way that is very different from your regular Vim editing
130+
sessions:
131+
132+
- Your [vimrc][] file is ignored using the `-u NONE` command line option.
133+
134+
- Your [gvimrc][] file (if you even knew it existed ;-) is ignored using
135+
the `-U NONE` command line option.
136+
137+
- Plug-in loading is skipped using the `--noplugin` command line option.
138+
139+
- Swap files (see [swap-file][]) are disabled using the `-n` command line
140+
option. This makes sure asynchronous Vim processes don't disturb the
141+
user's editing session.
142+
143+
- Your [viminfo][] file is ignored using the `-i NONE` command line
144+
option. Just like with swap files this makes sure asynchronous Vim
145+
processes don't disturb the user's editing session.
146+
147+
- No-compatible mode is enabled using the `-N` command line option
148+
(usually the existence of your vimrc script would have achieved the
149+
same effect but since we disable loading of your vimrc we need to spell
150+
things out for Vim).
151+
152+
**Use an auto-load function**
153+
154+
The function you want to call is identified by its name which has to be
155+
defined, but I just explained above that all regular initialization is
156+
disabled for asynchronous Vim processes, so what gives? The answer is to
157+
use an [autoload][] function. This should work fine because the
158+
asynchronous Vim process 'inherits' the value of the ['runtimepath'][]
159+
option from your editing session.
160+
161+
['runtimepath']: http://vimdoc.sourceforge.net/htmldoc/options.html#'runtimepath'
162+
[autoload]: http://vimdoc.sourceforge.net/htmldoc/eval.html#autoload
163+
[CursorHold]: http://vimdoc.sourceforge.net/htmldoc/autocmd.html#CursorHold
164+
[eval()]: http://vimdoc.sourceforge.net/htmldoc/eval.html#eval()
165+
[gvimrc]: http://vimdoc.sourceforge.net/htmldoc/gui.html#gvimrc
166+
[string()]: http://vimdoc.sourceforge.net/htmldoc/eval.html#string()
167+
[swap-file]: http://vimdoc.sourceforge.net/htmldoc/recover.html#swap-file
168+
[vim-misc]: http://peterodding.com/code/vim/misc/
169+
[viminfo]: http://vimdoc.sourceforge.net/htmldoc/starting.html#viminfo
170+
[vimrc]: http://vimdoc.sourceforge.net/htmldoc/starting.html#vimrc
171+
172+
#### The `xolox#misc#async#inside_child()` function
173+
174+
Entry point inside the hidden Vim process that runs in the background.
175+
Invoked indirectly by `xolox#misc#async#call()` because it runs a command
176+
similar to the following:
177+
178+
vim --cmd 'call xolox#misc#async#inside_child(...)'
179+
180+
This function is responsible for calling the user defined function,
181+
capturing exceptions and reporting the results back to the parent Vim
182+
process using Vim's client/server support or a temporary file.
183+
184+
#### The `xolox#misc#async#callback_to_parent()` function
185+
186+
When Vim was compiled with client/server support this function (in the
187+
parent process) will be called by `xolox#misc#async#inside_child()` (in
188+
the child process) after the user defined function has returned. This
189+
enables more or less instant callbacks after running an asynchronous
190+
function.
191+
192+
#### The `xolox#misc#async#periodic_callback()` function
193+
194+
When client/server support is not being used the vim-misc plug-in
195+
improvises: It uses Vim's [CursorHold][] event to periodically check if an
196+
asynchronous process has written its results to one of the expected
197+
temporary files. If a response is found the temporary file is read and
198+
deleted and then `xolox#misc#async#callback_to_parent()` is called to
199+
process the response.
200+
201+
[CursorHold]: http://vimdoc.sourceforge.net/htmldoc/autocmd.html#CursorHold
42202

43203
### Handling of special buffers
44204

@@ -745,8 +905,8 @@ first version string. Returns 1 (true) when it is, 0 (false) otherwise.
745905

746906
If you have questions, bug reports, suggestions, etc. please open an issue or
747907
pull request on [GitHub] []. Download links and documentation can be found on
748-
the plug-in's [homepage] []. If you like the script please vote for it on [Vim
749-
Online] [].
908+
the plug-in's [homepage] []. If you like the script please vote for it on
909+
[Vim Online] [].
750910

751911
## License
752912

autoload/xolox/misc.vim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@
44
" Last Change: June 22, 2014
55
" URL: http://peterodding.com/code/vim/misc/
66

7-
let g:xolox#misc#version = '1.10'
7+
let g:xolox#misc#version = '1.11'

0 commit comments

Comments
 (0)