1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
|
# Miscellaneous auto-load Vim scripts
The git repository at [github.com/xolox/vim-misc] [repository] contains Vim
scripts that are used by most of the [Vim plug-ins I've written] [plugins] yet
don't really belong with any single one of the plug-ins. Basically it's an
extended standard library of Vim script functions that I wrote during the
development of my Vim plug-ins.
The miscellaneous scripts are bundled with each of my plug-ins using git
merges, so that a repository checkout of a plug-in contains everything that's
needed to get started. This means the git repository of the miscellaneous
scripts is only used to track changes in a central, public place.
## Documentation for the included functions
The documentation of the 41 functions below was extracted from 12 Vim scripts
on May 19, 2013 at 23:27.
### Handling of special buffers
The functions defined here make it easier to deal with special Vim buffers
that contain text generated by a Vim plug-in. For example my [vim-notes
plug-in] [vim-notes] generates several such buffers:
- [:RecentNotes] [RecentNotes] lists recently modified notes
- [:ShowTaggedNotes] [ShowTaggedNotes] lists notes grouped by tags
- etc.
Because the text in these buffers is generated, Vim shouldn't bother with
swap files and it should never prompt the user whether to save changes to
the generated text.
[vim-notes]: http://peterodding.com/code/vim/notes/
[RecentNotes]: http://peterodding.com/code/vim/notes/#recentnotes_command
[ShowTaggedNotes]: http://peterodding.com/code/vim/notes/#showtaggednotes_command
#### The `xolox#misc#buffer#is_empty()` function
Checks if the current buffer is an empty, unchanged buffer which can be
reused. Returns 1 if an empty buffer is found, 0 otherwise.
#### The `xolox#misc#buffer#prepare()` function
Open a special buffer, i.e. a buffer that will hold generated contents,
not directly edited by the user. The buffer can be customized by passing a
dictionary with the following key/value pairs as the first argument:
- **name** (required): The base name of the buffer (i.e. the base name of
the file loaded in the buffer, even though it isn't really a file and
nothing is really 'loaded' :-)
- **path** (required): The pathname of the buffer. May be relevant if
[:lcd] [lcd] or ['autochdir'] [acd] is being used.
[lcd]: http://vimdoc.sourceforge.net/htmldoc/editing.html#:lcd
[acd]: http://vimdoc.sourceforge.net/htmldoc/options.html#'autochdir'
#### The `xolox#misc#buffer#lock()` function
Lock a special buffer so that its contents can no longer be edited.
#### The `xolox#misc#buffer#unlock()` function
Unlock a special buffer so that its content can be updated.
### Compatibility checking
This Vim script defines a version number for the miscellaneous scripts. Each
of my plug-ins compares their expected version of the miscellaneous scripts
against the version number defined inside the miscellaneous scripts.
The version number is incremented whenever a change in the miscellaneous
scripts breaks backwards compatibility. This enables my Vim plug-ins to fail
early when they detect an incompatible version, instead of breaking at the
worst possible moments :-).
#### The `xolox#misc#compat#check()` function
Expects two arguments: The name of a Vim plug-in and the version of the
miscellaneous scripts expected by the plug-in. When the active version of
the miscellaneous scripts has a different version, this will raise an
error message that explains what went wrong.
### Tab completion for user defined commands
#### The `xolox#misc#complete#keywords()` function
This function can be used to perform keyword completion for user defined
Vim commands based on the contents of the current buffer. Here's an
example of how you would use it:
:command -nargs=* -complete=customlist,xolox#misc#complete#keywords MyCmd call s:MyCmd(<f-args>)
### String escaping functions
#### The `xolox#misc#escape#pattern()` function
Takes a single string argument and converts it into a [:substitute]
[subcmd] / [substitute()] [subfun] pattern string that matches the given
string literally.
[subfun]: http://vimdoc.sourceforge.net/htmldoc/eval.html#substitute()
[subcmd]: http://vimdoc.sourceforge.net/htmldoc/change.html#:substitute
#### The `xolox#misc#escape#substitute()` function
Takes a single string argument and converts it into a [:substitute]
[subcmd] / [substitute()] [subfun] replacement string that inserts the
given string literally.
#### The `xolox#misc#escape#shell()` function
Takes a single string argument and converts it into a quoted command line
argument.
I was going to add a long rant here about Vim's ['shellslash' option]
[shellslash], but really, it won't make any difference. Let's just suffice
to say that I have yet to encounter a single person out there who uses
this option for its intended purpose (running a UNIX style shell on
Microsoft Windows).
[shellslash]: http://vimdoc.sourceforge.net/htmldoc/options.html#'shellslash'
### List handling functions
#### The `xolox#misc#list#unique()` function
Remove duplicate values from the given list in-place (preserves order).
#### The `xolox#misc#list#binsert()` function
Performs in-place binary insertion, which depending on your use case can
be more efficient than calling Vim's [sort()] [sort] function after each
insertion (in cases where a single, final sort is not an option). Expects
three arguments:
1. A list
2. A value to insert
3. 1 (true) when case should be ignored, 0 (false) otherwise
[sort]: http://vimdoc.sourceforge.net/htmldoc/eval.html#sort()
### Functions to interact with the user
#### The `xolox#misc#msg#info()` function
Show a formatted informational message to the user. This function has the
same argument handling as Vim's [printf()] [printf] function.
[printf]: http://vimdoc.sourceforge.net/htmldoc/eval.html#printf()
#### The `xolox#misc#msg#warn()` function
Show a formatted warning message to the user. This function has the same
argument handling as Vim's [printf()] [printf] function.
#### The `xolox#misc#msg#debug()` function
Show a formatted debugging message to the user, if the user has enabled
increased verbosity by setting Vim's ['verbose'] [verbose] option to one
(1) or higher. This function has the same argument handling as Vim's
[printf()] [printf] function.
[verbose]: http://vimdoc.sourceforge.net/htmldoc/options.html#'verbose'
### Integration between Vim and its environment
#### The `xolox#misc#open#file()` function
Given a pathname as the first argument, this opens the file with the
program associated with the file type. So for example a text file might
open in Vim, an `*.html` file would probably open in your web browser and
a media file would open in a media player.
This should work on Windows, Mac OS X and most Linux distributions. If
this fails to find a file association, you can pass one or more external
commands to try as additional arguments. For example:
:call xolox#misc#open#file('/path/to/my/file', 'firefox', 'google-chrome')
This generally shouldn't be necessary but it might come in handy now and
then.
#### The `xolox#misc#open#url()` function
Given a URL as the first argument, this opens the URL in your preferred or
best available web browser:
- In GUI environments a graphical web browser will open (or a new tab will
be created in an existing window)
- In console Vim without a GUI environment, when you have any of `lynx`,
`links` or `w3m` installed it will launch a command line web browser in
front of Vim (temporarily suspending Vim)
### Vim and plug-in option handling
#### The `xolox#misc#option#get()` function
Expects one or two arguments: 1. The name of a variable and 2. the default
value if the variable does not exist.
Returns the value of the variable from a buffer local variable, global
variable or the default value, depending on which is defined.
This is used by some of my Vim plug-ins for option handling, so that users
can customize options for specific buffers.
#### The `xolox#misc#option#split()` function
Given a multi-value Vim option like ['runtimepath'] [rtp] this returns a
list of strings. For example:
:echo xolox#misc#option#split(&runtimepath)
['/home/peter/Projects/Vim/misc',
'/home/peter/Projects/Vim/colorscheme-switcher',
'/home/peter/Projects/Vim/easytags',
...]
[rtp]: http://vimdoc.sourceforge.net/htmldoc/options.html#'runtimepath'
#### The `xolox#misc#option#join()` function
Given a list of strings like the ones returned by
`xolox#misc#option#split()`, this joins the strings together into a
single value that can be used to set a Vim option.
#### The `xolox#misc#option#split_tags()` function
Customized version of `xolox#misc#option#split()` with specialized
handling for Vim's ['tags' option] [tags].
[tags]: http://vimdoc.sourceforge.net/htmldoc/options.html#'tags'
#### The `xolox#misc#option#join_tags()` function
Customized version of `xolox#misc#option#join()` with specialized
handling for Vim's ['tags' option] [tags].
#### The `xolox#misc#option#eval_tags()` function
Evaluate Vim's ['tags' option] [tags] without looking at the file
system, i.e. this will report tags files that don't exist yet. Expects
the value of the ['tags' option] [tags] as the first argument. If the
optional second argument is 1 (true) only the first match is returned,
otherwise (so by default) a list with all matches is returned.
### Operating system interfaces
#### The `xolox#misc#os#is_win()` function
Returns 1 (true) when on Microsoft Windows, 0 (false) otherwise.
#### The `xolox#misc#os#exec()` function
Execute an external command (hiding the console on Microsoft Windows when
my [vim-shell plug-in] [vim-shell] is installed).
Expects a dictionary with the following key/value pairs as the first
argument:
- **command** (required): The command line to execute
- **async** (optional): set this to 1 (true) to execute the command in the
background (asynchronously)
- **stdin** (optional): a string or list of strings with the input for the
external command
- **check** (optional): set this to 0 (false) to disable checking of the
exit code of the external command (by default an exception will be
raised when the command fails)
Returns a dictionary with one or more of the following key/value pairs:
- **command** (always available): the generated command line that was used
to run the external command
- **exit_code** (only in synchronous mode): the exit status of the
external command (an integer, zero on success)
- **stdout** (only in synchronous mode): the output of the command on the
standard output stream (a list of strings, one for each line)
- **stderr** (only in synchronous mode): the output of the command on the
standard error stream (as a list of strings, one for each line)
[vim-shell]: http://peterodding.com/code/vim/shell/
### Pathname manipulation functions
#### The `xolox#misc#path#which()` function
Scan the executable search path (`$PATH`) for one or more external
programs. Expects one or more string arguments with program names. Returns
a list with the absolute pathnames of all found programs. Here's an
example:
:echo xolox#misc#path#which('gvim', 'vim')
['/usr/local/bin/gvim',
'/usr/bin/gvim',
'/usr/local/bin/vim',
'/usr/bin/vim']
#### The `xolox#misc#path#split()` function
Split a pathname (the first and only argument) into a list of pathname
components.
On Windows, pathnames starting with two slashes or backslashes are UNC
paths where the leading slashes are significant... In this case we split
like this:
- Input: `'//server/share/directory'`
- Result: `['//server', 'share', 'directory']`
Everything except Windows is treated like UNIX until someone has a better
suggestion :-). In this case we split like this:
- Input: `'/foo/bar/baz'`
- Result: `['/', 'foo', 'bar', 'baz']`
To join a list of pathname components back into a single pathname string,
use the `xolox#misc#path#join()` function.
#### The `xolox#misc#path#join()` function
Join a list of pathname components (the first and only argument) into a
single pathname string. This is the counterpart to the
`xolox#misc#path#split()` function and it expects a list of pathname
components as returned by `xolox#misc#path#split()`.
#### The `xolox#misc#path#directory_separator()` function
Find the preferred directory separator for the platform and settings.
#### The `xolox#misc#path#absolute()` function
Canonicalize and resolve a pathname, *regardless of whether it exists*.
This is intended to support string comparison to determine whether two
pathnames point to the same directory or file.
#### The `xolox#misc#path#relative()` function
Make an absolute pathname (the first argument) relative to a directory
(the second argument).
#### The `xolox#misc#path#merge()` function
Join a directory pathname and filename into a single pathname.
#### The `xolox#misc#path#commonprefix()` function
Find the common prefix of path components in a list of pathnames.
#### The `xolox#misc#path#encode()` function
Encode a pathname so it can be used as a filename. This uses URL encoding
to encode special characters.
#### The `xolox#misc#path#decode()` function
Decode a pathname previously encoded with `xolox#misc#path#encode()`.
#### The `xolox#misc#path#is_relative()` function
Returns true (1) when the pathname given as the first argument is
relative, false (0) otherwise.
#### The `xolox#misc#path#tempdir()` function
Create a temporary directory and return the pathname of the directory.
### String handling
#### The `xolox#misc#str#compact()` function
Compact whitespace in the string given as the first argument.
#### The `xolox#misc#str#trim()` function
Trim all whitespace from the start and end of the string given as the
first argument.
### Timing of long during operations
#### The `xolox#misc#timer#start()` function
Start a timer. This returns a list which can later be passed to
`xolox#misc#timer#stop()`.
#### The `xolox#misc#timer#stop()` function
Show a formatted debugging message to the user, if the user has enabled
increased verbosity by setting Vim's ['verbose'] [verbose] option to one
(1) or higher.
This function has the same argument handling as Vim's [printf()] [printf]
function with one difference: At the point where you want the elapsed time
to be embedded, you write `%s` and you pass the list returned by
`xolox#misc#timer#start()` as an argument.
[verbose]: http://vimdoc.sourceforge.net/htmldoc/options.html#'verbose'
[printf]: http://vimdoc.sourceforge.net/htmldoc/eval.html#printf()
#### The `xolox#misc#timer#format_timespan()` function
Format a time stamp (a string containing a formatted floating point
number) into a human friendly format, for example 70 seconds is phrased as
"1 minute and 10 seconds".
## Management of changes to the miscellaneous scripts
### How does it work?
Here's how I merge the miscellaneous scripts into a Vim plug-in repository:
1. Let git know about the `vim-misc` repository by adding the remote GitHub
repository:
git remote add -f vim-misc https://github.com/xolox/vim-misc.git
2. Merge the two directory trees without clobbering the `README.md` and/or
`.gitignore` files, thanks to the selected merge strategy and options:
git checkout master
git merge --no-commit -s recursive -X ours vim-misc/master
git commit -m "Merge vim-misc repository as overlay"
3. While steps 1 and 2 need to be done only once for a given repository, the
following commands are needed every time I want to pull and merge the latest
changes:
git checkout master
git fetch vim-misc master
git merge --no-commit -s recursive -X ours vim-misc/master
git commit -m "Merged changes to miscellaneous scripts"
### Why make things so complex?
I came up with this solution after multiple years of back and forth between Vim
Online users, the GitHub crowd and my own sanity:
1. When I started publishing my first Vim plug-ins (in June 2010) I would
prepare ZIP archives for Vim Online using makefiles. The makefiles would
make sure the miscellaneous scripts were included in the uploaded
distributions. This had two disadvantages: It lost git history and the
repositories on GitHub were not usable out of the box, so [I got complaints
from GitHub (Pathogen) users] [github-complaints].
2. My second attempt to solve the problem used git submodules which seemed like
the ideal solution until I actually started using them in March 2011:
Submodules are not initialized during a normal `git clone`, you need to use
`git clone --recursive` instead but Vim plug-in managers like [Pathogen]
[pathogen] and [Vundle] [vundle] don't do this (at least [they didn't when I
tried] [vundle-discussion]) so people would end up with broken checkouts.
3. After finding out that git submodules were not going to solve my problems I
searched for other inclusion strategies supported by git. After a while I
came upon the [subtree merge strategy] [merge-strategy] which I started
using in May 2011 and stuck with for more than two years (because it
generally worked fine and seemed quite robust).
4. In April 2013 the flat layout of the repository started bothering me because
it broke my personal workflow, so I changed it to the proper directory
layout of a Vim plug-in. Why did it break my workflow? Because I couldn't
get my [vim-reload] [reload] plug-in to properly reload miscellaneous
scripts without nasty hacks. Note to self: [Dropbox does not respect
symbolic links] [dropbox-vote-350] and Vim doesn't like them either ([E746]
[E746]).
### Compatibility issues
Regardless of the inclusion strategies discussed above, my current scheme has a
flaw: If more than one of my plug-ins are installed in a Vim profile using
[Pathogen] [pathogen] or [Vundle] [vundle], the miscellaneous autoload scripts
will all be loaded from the subdirectory of one single plug-in.
This means that when I break compatibility in the miscellaneous scripts, I have
to make sure to merge the changes into all of my plug-ins. Even then, if a user
has more than one of my plug-ins installed but updates only one of them, the
other plug-ins (that are not yet up to date) can break (because of the
backwards incompatible change).
The `xolox#misc#compat#check()` function makes sure that incompatibilities are
detected early so that the user knows which plug-in to update if
incompatibilities arise.
## Contact
If you have questions, bug reports, suggestions, etc. the author can be
contacted at <peter@peterodding.com>. The latest version is available at
<http://peterodding.com/code/vim/misc> and <http://github.com/xolox/vim-misc>.
## License
This software is licensed under the [MIT license] [mit].
© 2013 Peter Odding <<peter@peterodding.com>>.
[dropbox-vote-350]: https://www.dropbox.com/votebox/350/preserve-implement-symlink-behaviour
[E746]: http://vimdoc.sourceforge.net/htmldoc/eval.html#E746
[github-complaints]: https://github.com/xolox/vim-easytags/issues/1
[merge-strategy]: http://www.kernel.org/pub/software/scm/git/docs/howto/using-merge-subtree.html
[mit]: http://en.wikipedia.org/wiki/MIT_License
[pathogen]: http://www.vim.org/scripts/script.php?script_id=2332
[plugins]: http://peterodding.com/code/vim/
[reload]: http://peterodding.com/code/vim/reload
[repository]: https://github.com/xolox/vim-misc
[vundle-discussion]: https://github.com/gmarik/vundle/pull/41
[vundle]: https://github.com/gmarik/vundle
|