From ee4c247df124b38b478f8ca0f19d78fdc95ca042 Mon Sep 17 00:00:00 2001 From: Peter Odding Date: Sun, 19 May 2013 17:47:16 +0200 Subject: [PATCH 1/8] Improve reporting of multi-line messages (now split over lines) --- autoload/xolox/misc/msg.vim | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/autoload/xolox/misc/msg.vim b/autoload/xolox/misc/msg.vim index 9ba2b7c..74657c8 100644 --- a/autoload/xolox/misc/msg.vim +++ b/autoload/xolox/misc/msg.vim @@ -1,6 +1,6 @@ " Vim auto-load script " Author: Peter Odding -" Last Change: March 15, 2011 +" Last Change: May 19, 2013 " URL: http://peterodding.com/code/vim/misc/ if !exists('g:xolox_message_buffer') @@ -56,7 +56,10 @@ function! s:show_message(hlgroup, args) augroup END execute 'echohl' a:hlgroup " Redraw to avoid |hit-enter| prompt. - redraw | echomsg message + redraw + for line in split(message, "\n") + echomsg line + endfor if g:xolox_message_buffer > 0 call add(g:xolox_messages, message) if len(g:xolox_messages) > g:xolox_message_buffer From 65cf9e09ad9db4e1d773a54318fdc49374fa923c Mon Sep 17 00:00:00 2001 From: Peter Odding Date: Sun, 19 May 2013 23:33:53 +0200 Subject: [PATCH 2/8] Bug fix for xolox#misc#option#eval_tags() option --- autoload/xolox/misc/option.vim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/autoload/xolox/misc/option.vim b/autoload/xolox/misc/option.vim index efaf1bc..2912967 100644 --- a/autoload/xolox/misc/option.vim +++ b/autoload/xolox/misc/option.vim @@ -58,7 +58,7 @@ endfunction function! xolox#misc#option#eval_tags(value, ...) let pathnames = [] - let first_only = exists('a:1') && a:1 + let first_only = exists('a:1') ? a:1 : 0 for pattern in xolox#misc#option#split_tags(a:value) " Make buffer relative pathnames absolute. if pattern =~ '^\./' @@ -78,7 +78,7 @@ function! xolox#misc#option#eval_tags(value, ...) return pathnames[0] endif endfor - return firstonly ? '' : pathnames + return first_only ? '' : pathnames endfunction " vim: ts=2 sw=2 et From 3dffb26ff1fe27a4a1bb2e10abc46449ae17e4b5 Mon Sep 17 00:00:00 2001 From: Peter Odding Date: Sun, 19 May 2013 23:36:02 +0200 Subject: [PATCH 3/8] Apply hard wrapping to README.md --- README.md | 93 ++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 68 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 685d9c6..97d64b5 100644 --- a/README.md +++ b/README.md @@ -1,53 +1,96 @@ # 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 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. +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. ## 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: +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: +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: +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. +### 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 . The latest version is available at and . +If you have questions, bug reports, suggestions, etc. the author can be +contacted at . The latest version is available at + and . ## License From f7ed989081e98df4eceaf21bc23197abeba332ef Mon Sep 17 00:00:00 2001 From: Peter Odding Date: Sun, 19 May 2013 23:42:08 +0200 Subject: [PATCH 4/8] Bug fix: Make xolox#misc#os#exec() compatible with (t)csh (reported by Armin Widegreen) Original report: https://github.com/xolox/vim-easytags/pull/48 --- autoload/xolox/misc/compat.vim | 4 ++-- autoload/xolox/misc/os.vim | 12 ++++++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/autoload/xolox/misc/compat.vim b/autoload/xolox/misc/compat.vim index 70a4287..c950ad6 100644 --- a/autoload/xolox/misc/compat.vim +++ b/autoload/xolox/misc/compat.vim @@ -1,13 +1,13 @@ " Vim auto-load script " Author: Peter Odding -" Last Change: May 13, 2013 +" Last Change: May 19, 2013 " URL: http://peterodding.com/code/vim/misc/ " The following integer will be bumped 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 :-). -let g:xolox#misc#compat#version = 3 +let g:xolox#misc#compat#version = 4 " Remember the directory where the miscellaneous scripts are loaded from " so the user knows which plug-in to update if incompatibilities arise. diff --git a/autoload/xolox/misc/os.vim b/autoload/xolox/misc/os.vim index ef036df..8add797 100644 --- a/autoload/xolox/misc/os.vim +++ b/autoload/xolox/misc/os.vim @@ -1,9 +1,9 @@ " Vim auto-load script " Author: Peter Odding -" Last Change: May 13, 2013 +" Last Change: May 19, 2013 " URL: http://peterodding.com/code/vim/misc/ -let g:xolox#misc#os#version = '0.2' +let g:xolox#misc#os#version = '0.3' function! xolox#misc#os#is_win() " {{{1 " Check whether Vim is running on Microsoft Windows. @@ -72,6 +72,14 @@ function! xolox#misc#os#exec(options) " {{{1 endif endif + " Execute the command line using 'sh' instead of the default shell, + " because we assume that standard output and standard error can be + " redirected separately, but (t)csh does not support this. + if has('unix') + call xolox#misc#msg#debug("os.vim %s: Generated shell expression: %s", g:xolox#misc#os#version, cmd) + let cmd = printf('sh -c %s', xolox#misc#escape#shell(cmd)) + endif + " Let the user know what's happening (in case they're interested). call xolox#misc#msg#debug("os.vim %s: Executing external command using system() function: %s", g:xolox#misc#os#version, cmd) call system(cmd) From ef2006272479307f86e45a93b55fee4c0f798580 Mon Sep 17 00:00:00 2001 From: Peter Odding Date: Sun, 19 May 2013 23:43:19 +0200 Subject: [PATCH 5/8] Improve error reporting in xolox#misc#os#exec() --- autoload/xolox/misc/compat.vim | 2 +- autoload/xolox/misc/os.vim | 24 +++++++++++++++++------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/autoload/xolox/misc/compat.vim b/autoload/xolox/misc/compat.vim index c950ad6..56aff62 100644 --- a/autoload/xolox/misc/compat.vim +++ b/autoload/xolox/misc/compat.vim @@ -7,7 +7,7 @@ " 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 :-). -let g:xolox#misc#compat#version = 4 +let g:xolox#misc#compat#version = 5 " Remember the directory where the miscellaneous scripts are loaded from " so the user knows which plug-in to update if incompatibilities arise. diff --git a/autoload/xolox/misc/os.vim b/autoload/xolox/misc/os.vim index 8add797..032d2c8 100644 --- a/autoload/xolox/misc/os.vim +++ b/autoload/xolox/misc/os.vim @@ -87,19 +87,29 @@ function! xolox#misc#os#exec(options) " {{{1 endif - let result = {} + " Return the results as a dictionary with one or more key/value pairs. + let result = {'command': cmd} if !async + let result['exit_code'] = exit_code + let result['stdout'] = s:readfile(tempout) + let result['stderr'] = s:readfile(temperr) " If we just executed a synchronous command and the caller didn't " specifically ask us *not* to check the exit code of the external " command, we'll do so now. if get(a:options, 'check', 1) && exit_code != 0 - let msg = "os.vim %s: External command failed with exit code %d: %s" - throw printf(msg, g:xolox#misc#os#version, result['exit_code'], result['command']) + " Prepare an error message with enough details so the user can investigate. + let msg = printf("os.vim %s: External command failed with exit code %d!", g:xolox#misc#os#version, result['exit_code']) + let msg .= printf("\nCommand line: %s", result['command']) + " If the external command reported an error, we'll include it in our message. + if !empty(result['stderr']) + " This is where we would normally expect to find an error message. + let msg .= printf("\nOutput on standard output stream:\n%s", join(result['stderr'], "\n")) + elseif !empty(result['stdout']) + " Exuberant Ctags on Windows XP reports errors on standard output :-x. + let msg .= printf("\nOutput on standard error stream:\n%s", join(result['stdout'], "\n")) + endif + throw msg endif - " Return the results as a dictionary with three key/value pairs. - let result['exit_code'] = exit_code - let result['stdout'] = s:readfile(tempout) - let result['stderr'] = s:readfile(temperr) endif return result From 2cad05a933ade05c8540f787f0bd7d5e76ae5db2 Mon Sep 17 00:00:00 2001 From: Peter Odding Date: Sun, 19 May 2013 23:44:55 +0200 Subject: [PATCH 6/8] Bug fix: Correct wrong expansion order in xolox#misc#path#which() --- autoload/xolox/misc/compat.vim | 2 +- autoload/xolox/misc/path.vim | 29 +++++++++++++---------------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/autoload/xolox/misc/compat.vim b/autoload/xolox/misc/compat.vim index 56aff62..84f0a5f 100644 --- a/autoload/xolox/misc/compat.vim +++ b/autoload/xolox/misc/compat.vim @@ -7,7 +7,7 @@ " 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 :-). -let g:xolox#misc#compat#version = 5 +let g:xolox#misc#compat#version = 6 " Remember the directory where the miscellaneous scripts are loaded from " so the user knows which plug-in to update if incompatibilities arise. diff --git a/autoload/xolox/misc/path.vim b/autoload/xolox/misc/path.vim index 6ad8be4..60d3cbb 100644 --- a/autoload/xolox/misc/path.vim +++ b/autoload/xolox/misc/path.vim @@ -1,6 +1,6 @@ " Vim auto-load script " Author: Peter Odding -" Last Change: April 28, 2013 +" Last Change: May 19, 2013 " URL: http://peterodding.com/code/vim/misc/ let s:windows_compatible = has('win32') || has('win64') @@ -11,21 +11,18 @@ function! xolox#misc#path#which(...) " {{{1 let extensions = s:windows_compatible ? split($PATHEXT, ';') : [''] let matches = [] let checked = {} - for directory in split($PATH, s:windows_compatible ? ';' : ':') - let directory = xolox#misc#path#absolute(directory) - if !has_key(checked, directory) - if isdirectory(directory) - for program in a:000 - for extension in extensions - let path = xolox#misc#path#merge(directory, program . extension) - if executable(path) - call add(matches, path) - endif - endfor - endfor - endif - let checked[directory] = 1 - endif + for program in a:000 + for extension in extensions + for directory in split($PATH, s:windows_compatible ? ';' : ':') + let directory = xolox#misc#path#absolute(directory) + if isdirectory(directory) + let path = xolox#misc#path#merge(directory, program . extension) + if executable(path) + call add(matches, path) + endif + endif + endfor + endfor endfor return matches endfunction From ed942d165f9e2b49666e5e621663cfbf45d3a5e0 Mon Sep 17 00:00:00 2001 From: Peter Odding Date: Sun, 19 May 2013 23:49:21 +0200 Subject: [PATCH 7/8] Added inline documentation everywhere, also included in README.md* * Using a new tool I'm working on: A Python script that scans a Vim script project for global function definitions and their documentation comments (in Markdown format) and generates a single Markdown document that provides an overview of the functions defined in the scripts. --- README.md | 394 ++++++++++++++++++++++++++++++- autoload/xolox/misc/buffer.vim | 39 ++- autoload/xolox/misc/compat.vim | 15 +- autoload/xolox/misc/complete.vim | 12 +- autoload/xolox/misc/escape.vim | 40 ++-- autoload/xolox/misc/list.vim | 24 +- autoload/xolox/misc/msg.vim | 35 +-- autoload/xolox/misc/open.vim | 32 ++- autoload/xolox/misc/option.vim | 51 +++- autoload/xolox/misc/os.vim | 35 ++- autoload/xolox/misc/path.vim | 70 ++++-- autoload/xolox/misc/str.vim | 12 +- autoload/xolox/misc/timer.vim | 33 ++- 13 files changed, 688 insertions(+), 104 deletions(-) diff --git a/README.md b/README.md index 97d64b5..e1d97f8 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,399 @@ 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. -## How does it work? +## 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() + +### 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: diff --git a/autoload/xolox/misc/buffer.vim b/autoload/xolox/misc/buffer.vim index 3597cc2..01dca6e 100644 --- a/autoload/xolox/misc/buffer.vim +++ b/autoload/xolox/misc/buffer.vim @@ -1,15 +1,44 @@ -" Vim auto-load script +" Handling of special buffers +" " Author: Peter Odding -" Last Change: April 18, 2013 +" Last Change: May 19, 2013 " URL: http://peterodding.com/code/vim/misc/ +" +" 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 function! xolox#misc#buffer#is_empty() " {{{1 - " Check if the current buffer is an empty, unchanged buffer which can be reused. + " Checks if the current buffer is an empty, unchanged buffer which can be + " reused. Returns 1 if an empty buffer is found, 0 otherwise. return !&modified && expand('%') == '' && line('$') <= 1 && getline(1) == '' endfunction function! xolox#misc#buffer#prepare(...) " {{{1 - " Open a special buffer (with generated contents, not directly edited by the user). + " 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' if a:0 == 1 && type(a:1) == type('') " Backwards compatibility with old interface. let options = {'name': a:1, 'path': a:1} @@ -41,7 +70,7 @@ function! xolox#misc#buffer#prepare(...) " {{{1 endfunction function! xolox#misc#buffer#lock() " {{{1 - " Lock a special buffer so it can no longer be edited. + " Lock a special buffer so that its contents can no longer be edited. setlocal readonly nomodifiable nomodified endfunction diff --git a/autoload/xolox/misc/compat.vim b/autoload/xolox/misc/compat.vim index 84f0a5f..3cf1f1f 100644 --- a/autoload/xolox/misc/compat.vim +++ b/autoload/xolox/misc/compat.vim @@ -1,9 +1,14 @@ -" Vim auto-load script +" Compatibility checking. +" " Author: Peter Odding " Last Change: May 19, 2013 " URL: http://peterodding.com/code/vim/misc/ - -" The following integer will be bumped whenever a change in the miscellaneous +" +" 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 :-). @@ -14,6 +19,10 @@ let g:xolox#misc#compat#version = 6 let s:misc_directory = fnamemodify(expand(''), ':p:h') function! xolox#misc#compat#check(plugin_name, required_version) + " 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. if a:required_version != g:xolox#misc#compat#version let msg = "The %s plug-in requires version %i of the miscellaneous scripts, however version %i was loaded from %s!" throw printf(msg, a:plugin_name, a:required_version, g:xolox#misc#compat#version, s:misc_directory) diff --git a/autoload/xolox/misc/complete.vim b/autoload/xolox/misc/complete.vim index 2ada676..763e0b9 100644 --- a/autoload/xolox/misc/complete.vim +++ b/autoload/xolox/misc/complete.vim @@ -1,11 +1,15 @@ -" Vim auto-load script +" Tab completion for user defined commands. +" " Author: Peter Odding -" Last Change: March 15, 2011 +" Last Change: May 19, 2013 " URL: http://peterodding.com/code/vim/misc/ -" Keyword completion from the current buffer for user defined commands. - function! xolox#misc#complete#keywords(arglead, cmdline, cursorpos) + " 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() let words = {} for line in getline(1, '$') for word in split(line, '\W\+') diff --git a/autoload/xolox/misc/escape.vim b/autoload/xolox/misc/escape.vim index 1dd1838..29a16ca 100644 --- a/autoload/xolox/misc/escape.vim +++ b/autoload/xolox/misc/escape.vim @@ -1,11 +1,16 @@ -" Vim auto-load script +" String escaping functions. +" " Author: Peter Odding -" Last Change: November 21, 2011 +" Last Change: May 19, 2013 " URL: http://peterodding.com/code/vim/misc/ -" Convert a string into a :substitute pattern that matches the string literally. - -function! xolox#misc#escape#pattern(string) +function! xolox#misc#escape#pattern(string) " {{{1 + " 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 if type(a:string) == type('') let string = escape(a:string, '^$.*\~[]') return substitute(string, '\n', '\\n', 'g') @@ -13,9 +18,10 @@ function! xolox#misc#escape#pattern(string) return '' endfunction -" Convert a string into a :substitute replacement that inserts the string literally. - -function! xolox#misc#escape#substitute(string) +function! xolox#misc#escape#substitute(string) " {{{1 + " Takes a single string argument and converts it into a [:substitute] + " [subcmd] / [substitute()] [subfun] replacement string that inserts the + " given string literally. if type(a:string) == type('') let string = escape(a:string, '\&~%') return substitute(string, '\n', '\\r', 'g') @@ -23,13 +29,17 @@ function! xolox#misc#escape#substitute(string) return '' endfunction -" Convert a string into a quoted command line argument. I was going to add a -" long rant here about &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 Windows). - -function! xolox#misc#escape#shell(string) +function! xolox#misc#escape#shell(string) " {{{1 + " 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' if xolox#misc#os#is_win() try let ssl_save = &shellslash diff --git a/autoload/xolox/misc/list.vim b/autoload/xolox/misc/list.vim index ee243d4..13dfb43 100644 --- a/autoload/xolox/misc/list.vim +++ b/autoload/xolox/misc/list.vim @@ -1,19 +1,27 @@ -" Vim auto-load script +" List handling functions. +" " Author: Peter Odding -" Last Change: August 31, 2011 +" Last Change: May 19, 2013 " URL: http://peterodding.com/code/vim/misc/ -" Remove duplicate values from {list} in-place (preserves order). - -function! xolox#misc#list#unique(list) +function! xolox#misc#list#unique(list) " {{{1 + " Remove duplicate values from the given list in-place (preserves order). call reverse(a:list) call filter(a:list, 'count(a:list, v:val) == 1') return reverse(a:list) endfunction -" Binary insertion (more efficient than calling sort() after each insertion). - -function! xolox#misc#list#binsert(list, value, ...) +function! xolox#misc#list#binsert(list, value, ...) " {{{1 + " 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() let idx = s:binsert_r(a:list, 0, len(a:list), a:value, exists('a:1') && a:1) return insert(a:list, a:value, idx) endfunction diff --git a/autoload/xolox/misc/msg.vim b/autoload/xolox/misc/msg.vim index 74657c8..38eb474 100644 --- a/autoload/xolox/misc/msg.vim +++ b/autoload/xolox/misc/msg.vim @@ -1,4 +1,5 @@ -" Vim auto-load script +" Functions to interact with the user. +" " Author: Peter Odding " Last Change: May 19, 2013 " URL: http://peterodding.com/code/vim/misc/ @@ -12,29 +13,34 @@ if !exists('g:xolox_messages') let g:xolox_messages = [] endif -" Show a formatted informational message to the user. - -function! xolox#misc#msg#info(...) +function! xolox#misc#msg#info(...) " {{{1 + " 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() call s:show_message('title', a:000) endfunction -" Show a formatted warning message to the user. - -function! xolox#misc#msg#warn(...) +function! xolox#misc#msg#warn(...) " {{{1 + " Show a formatted warning message to the user. This function has the same + " argument handling as Vim's [printf()] [printf] function. call s:show_message('warningmsg', a:000) endfunction -" Show a formatted debugging message to the user? - -function! xolox#misc#msg#debug(...) +function! xolox#misc#msg#debug(...) " {{{1 + " 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' if &vbs >= 1 call s:show_message('question', a:000) endif endfunction -" The implementation of info() and warn(). - -function! s:show_message(hlgroup, args) +function! s:show_message(hlgroup, args) " {{{1 + " The implementation of info() and warn(). let nargs = len(a:args) if nargs == 1 let message = a:args[0] @@ -73,7 +79,8 @@ function! s:show_message(hlgroup, args) endif endfunction -function! s:clear_message() +function! s:clear_message() " {{{1 + " Callback to clear message after some time has passed. echo '' let &more = s:more_save let &showmode = s:smd_save diff --git a/autoload/xolox/misc/open.vim b/autoload/xolox/misc/open.vim index 1fb24e0..b369f4f 100644 --- a/autoload/xolox/misc/open.vim +++ b/autoload/xolox/misc/open.vim @@ -1,6 +1,7 @@ -" Vim auto-load script +" Integration between Vim and its environment. +" " Author: Peter Odding -" Last Change: November 21, 2011 +" Last Change: May 19, 2013 " URL: http://peterodding.com/code/vim/misc/ if !exists('s:version') @@ -9,7 +10,20 @@ if !exists('s:version') let s:handlers = ['gnome-open', 'kde-open', 'exo-open', 'xdg-open'] endif -function! xolox#misc#open#file(path, ...) +function! xolox#misc#open#file(path, ...) " {{{1 + " 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. if xolox#misc#os#is_win() try call xolox#shell#open_with_windows_shell(a:path) @@ -35,7 +49,15 @@ function! xolox#misc#open#file(path, ...) throw printf(s:enoimpl, s:script, 'xolox#misc#open#file') endfunction -function! xolox#misc#open#url(url) +function! xolox#misc#open#url(url) " {{{1 + " 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) let url = a:url if url !~ '^\w\+://' if url !~ '@' @@ -56,7 +78,7 @@ function! xolox#misc#open#url(url) call xolox#misc#open#file(url, 'firefox', 'google-chrome') endfunction -function! s:handle_error(cmd, output) +function! s:handle_error(cmd, output) " {{{1 if v:shell_error let message = "open.vim %s: Failed to execute program! (command line: %s%s)" let output = strtrans(xolox#misc#str#trim(a:output)) diff --git a/autoload/xolox/misc/option.vim b/autoload/xolox/misc/option.vim index 2912967..fce155c 100644 --- a/autoload/xolox/misc/option.vim +++ b/autoload/xolox/misc/option.vim @@ -1,9 +1,18 @@ -" Vim auto-load script +" Vim and plug-in option handling. +" " Author: Peter Odding -" Last Change: August 31, 2011 +" Last Change: May 19, 2013 " URL: http://peterodding.com/code/vim/misc/ -function! xolox#misc#option#get(name, ...) +function! xolox#misc#option#get(name, ...) " {{{1 + " 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. if exists('b:' . a:name) " Buffer local variable. return eval('b:' . a:name) @@ -16,9 +25,17 @@ function! xolox#misc#option#get(name, ...) endif endfunction -" Functions to parse multi-valued Vim options like &tags and &runtimepath. - -function! xolox#misc#option#split(value) +function! xolox#misc#option#split(value) " {{{1 + " 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' let values = split(a:value, '[^\\]\zs,') return map(values, 's:unescape(v:val)') endfunction @@ -27,7 +44,10 @@ function! s:unescape(s) return substitute(a:s, '\\\([\\,]\)', '\1', 'g') endfunction -function! xolox#misc#option#join(values) +function! xolox#misc#option#join(values) " {{{1 + " 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. let values = copy(a:values) call map(values, 's:escape(v:val)') return join(values, ',') @@ -37,7 +57,11 @@ function! s:escape(s) return escape(a:s, ',\') endfunction -function! xolox#misc#option#split_tags(value) +function! xolox#misc#option#split_tags(value) " {{{1 + " 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' let values = split(a:value, '[^\\]\zs,') return map(values, 's:unescape_tags(v:val)') endfunction @@ -46,7 +70,9 @@ function! s:unescape_tags(s) return substitute(a:s, '\\\([\\, ]\)', '\1', 'g') endfunction -function! xolox#misc#option#join_tags(values) +function! xolox#misc#option#join_tags(values) " {{{1 + " Customized version of `xolox#misc#option#join()` with specialized + " handling for Vim's ['tags' option] [tags]. let values = copy(a:values) call map(values, 's:escape_tags(v:val)') return join(values, ',') @@ -56,7 +82,12 @@ function! s:escape_tags(s) return escape(a:s, ', ') endfunction -function! xolox#misc#option#eval_tags(value, ...) +function! xolox#misc#option#eval_tags(value, ...) " {{{1 + " 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. let pathnames = [] let first_only = exists('a:1') ? a:1 : 0 for pattern in xolox#misc#option#split_tags(a:value) diff --git a/autoload/xolox/misc/os.vim b/autoload/xolox/misc/os.vim index 032d2c8..4dcf64d 100644 --- a/autoload/xolox/misc/os.vim +++ b/autoload/xolox/misc/os.vim @@ -1,4 +1,5 @@ -" Vim auto-load script +" Operating system interfaces. +" " Author: Peter Odding " Last Change: May 19, 2013 " URL: http://peterodding.com/code/vim/misc/ @@ -6,14 +7,38 @@ let g:xolox#misc#os#version = '0.3' function! xolox#misc#os#is_win() " {{{1 - " Check whether Vim is running on Microsoft Windows. + " Returns 1 (true) when on Microsoft Windows, 0 (false) otherwise. return has('win16') || has('win32') || has('win64') endfunction function! xolox#misc#os#exec(options) " {{{1 - " Execute an external command (hiding the console on Windows when possible). - " NB: Everything below is wrapped in a try/finally block to guarantee - " cleanup of temporary files. + " 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/ try " Unpack the options. diff --git a/autoload/xolox/misc/path.vim b/autoload/xolox/misc/path.vim index 60d3cbb..15e11d6 100644 --- a/autoload/xolox/misc/path.vim +++ b/autoload/xolox/misc/path.vim @@ -1,4 +1,5 @@ -" Vim auto-load script +" Pathname manipulation functions. +" " Author: Peter Odding " Last Change: May 19, 2013 " URL: http://peterodding.com/code/vim/misc/ @@ -7,7 +8,16 @@ let s:windows_compatible = has('win32') || has('win64') let s:mac_os_x_compatible = has('macunix') function! xolox#misc#path#which(...) " {{{1 - " Scan the executable search path for programs. + " 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'] let extensions = s:windows_compatible ? split($PATHEXT, ';') : [''] let matches = [] let checked = {} @@ -28,23 +38,35 @@ function! xolox#misc#path#which(...) " {{{1 endfunction function! xolox#misc#path#split(path) " {{{1 - " Split a pathname into a list of path components. + " 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. if type(a:path) == type('') if s:windows_compatible if a:path =~ '^[\/][\/]' - " 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: - " '//server/share/directory' -> ['//server', 'share', 'directory'] + " UNC pathname. return split(a:path, '\%>2c[\/]\+') else " If it's not a UNC path we can simply split on slashes & backslashes. return split(a:path, '[\/]\+') endif else - " Everything except Windows is treated like UNIX until someone - " has a better suggestion :-). In this case we split like this: - " '/foo/bar/baz' -> ['/', 'foo', 'bar', 'baz'] + " Everything else is treated as UNIX. let absolute = (a:path =~ '^/') let segments = split(a:path, '/\+') return absolute ? insert(segments, '/') : segments @@ -54,7 +76,10 @@ function! xolox#misc#path#split(path) " {{{1 endfunction function! xolox#misc#path#join(parts) " {{{1 - " Join a list of pathname components into a single pathname. + " 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()`. if type(a:parts) == type([]) if s:windows_compatible return join(a:parts, xolox#misc#path#directory_separator()) @@ -75,9 +100,9 @@ function! xolox#misc#path#directory_separator() " {{{1 endfunction function! xolox#misc#path#absolute(path) " {{{1 - " 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. + " 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. if type(a:path) == type('') let path = a:path " Make the pathname absolute. @@ -107,7 +132,8 @@ function! xolox#misc#path#absolute(path) " {{{1 endfunction function! xolox#misc#path#relative(path, base) " {{{1 - " Make an absolute pathname relative. + " Make an absolute pathname (the first argument) relative to a directory + " (the second argument). let path = xolox#misc#path#split(a:path) let base = xolox#misc#path#split(a:base) while path != [] && base != [] && path[0] == base[0] @@ -120,9 +146,9 @@ endfunction function! xolox#misc#path#merge(parent, child, ...) " {{{1 - " Join a directory and filename into a single pathname. - " TODO Use isabs()! + " Join a directory pathname and filename into a single pathname. if type(a:parent) == type('') && type(a:child) == type('') + " TODO Use xolox#misc#path#is_relative()? if s:windows_compatible let parent = substitute(a:parent, '[\\/]\+$', '', '') let child = substitute(a:child, '^[\\/]\+', '', '') @@ -155,7 +181,8 @@ function! xolox#misc#path#commonprefix(paths) " {{{1 endfunction function! xolox#misc#path#encode(path) " {{{1 - " Encode a pathname so it can be used as a filename. + " Encode a pathname so it can be used as a filename. This uses URL encoding + " to encode special characters. if s:windows_compatible let mask = '[*|\\/:"<>?%]' elseif s:mac_os_x_compatible @@ -168,7 +195,7 @@ endfunction function! xolox#misc#path#decode(encoded_path) " {{{1 - " Decode a pathname previously encoded with xolox#misc#path#encode(). + " Decode a pathname previously encoded with `xolox#misc#path#encode()`. return substitute(a:encoded_path, '%\(\x\x\?\)', '\=nr2char("0x" . submatch(1))', 'g') endfunction @@ -185,7 +212,8 @@ else endif function! xolox#misc#path#is_relative(path) " {{{1 - " Check whether a path is relative. + " Returns true (1) when the pathname given as the first argument is + " relative, false (0) otherwise. if a:path =~ '^\w\+://' return 0 elseif s:windows_compatible @@ -196,7 +224,7 @@ function! xolox#misc#path#is_relative(path) " {{{1 endfunction function! xolox#misc#path#tempdir() " {{{1 - " Create a temporary directory and return the path. + " Create a temporary directory and return the pathname of the directory. if !exists('s:tempdir_counter') let s:tempdir_counter = 1 endif diff --git a/autoload/xolox/misc/str.vim b/autoload/xolox/misc/str.vim index 74a05aa..901696c 100644 --- a/autoload/xolox/misc/str.vim +++ b/autoload/xolox/misc/str.vim @@ -1,11 +1,17 @@ -" Vim auto-load script +" String handling. +" " Author: Peter Odding -" Last Change: June 14, 2011 +" Last Change: May 19, 2013 " URL: http://peterodding.com/code/vim/misc/ -" Trim whitespace from start and end of string. +function! xolox#misc#str#compact(s) + " Compact whitespace in the string given as the first argument. + return join(split(a:s), " ") +endfunction function! xolox#misc#str#trim(s) + " Trim all whitespace from the start and end of the string given as the + " first argument. return substitute(a:s, '^\_s*\(.\{-}\)\_s*$', '\1', '') endfunction diff --git a/autoload/xolox/misc/timer.vim b/autoload/xolox/misc/timer.vim index 151972d..d7fc32d 100644 --- a/autoload/xolox/misc/timer.vim +++ b/autoload/xolox/misc/timer.vim @@ -1,6 +1,7 @@ -" Vim auto-load script +" Timing of long during operations. +" " Author: Peter Odding -" Last Change: March 15, 2011 +" Last Change: May 19, 2013 " URL: http://peterodding.com/code/vim/misc/ if !exists('g:timer_enabled') @@ -13,24 +14,33 @@ endif let s:has_reltime = has('reltime') -" Start a timer. - -function! xolox#misc#timer#start() +function! xolox#misc#timer#start() " {{{1 + " Start a timer. This returns a list which can later be passed to + " `xolox#misc#timer#stop()`. if g:timer_enabled || &verbose >= g:timer_verbosity return s:has_reltime ? reltime() : [localtime()] endif return [] endfunction -" Stop a timer and print the elapsed time (only if the user is interested). - -function! xolox#misc#timer#stop(...) +function! xolox#misc#timer#stop(...) " {{{1 + " 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() if (g:timer_enabled || &verbose >= g:timer_verbosity) call call('xolox#misc#msg#info', map(copy(a:000), 's:convert_value(v:val)')) endif endfunction -function! s:convert_value(value) +function! s:convert_value(value) " {{{1 if type(a:value) != type([]) return a:value elseif !empty(a:value) @@ -49,7 +59,10 @@ endfunction let s:units = [['day', 60 * 60 * 24], ['hour', 60 * 60], ['minute', 60], ['second', 1]] -function! xolox#misc#timer#format_timespan(ts) +function! xolox#misc#timer#format_timespan(ts) " {{{1 + " 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". " Convert timespan to integer. let seconds = a:ts + 0 From 37a9bf46a6ca2bb702c3cbaa7e6dad5cc1726409 Mon Sep 17 00:00:00 2001 From: Peter Odding Date: Mon, 20 May 2013 00:22:36 +0200 Subject: [PATCH 8/8] Improve the error message thrown by xolox#misc#compat#check() --- autoload/xolox/misc/compat.vim | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/autoload/xolox/misc/compat.vim b/autoload/xolox/misc/compat.vim index 3cf1f1f..3f3a42c 100644 --- a/autoload/xolox/misc/compat.vim +++ b/autoload/xolox/misc/compat.vim @@ -1,7 +1,7 @@ " Compatibility checking. " " Author: Peter Odding -" Last Change: May 19, 2013 +" Last Change: May 20, 2013 " URL: http://peterodding.com/code/vim/misc/ " " This Vim script defines a version number for the miscellaneous scripts. Each @@ -12,20 +12,25 @@ " 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 :-). -let g:xolox#misc#compat#version = 6 +let g:xolox#misc#compat#version = 7 " Remember the directory where the miscellaneous scripts are loaded from " so the user knows which plug-in to update if incompatibilities arise. -let s:misc_directory = fnamemodify(expand(''), ':p:h') +let s:misc_directory = fnamemodify(expand(''), ':~:h') -function! xolox#misc#compat#check(plugin_name, required_version) - " 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. +function! xolox#misc#compat#check(plugin_name, plugin_version, required_version) + " Expects three arguments: + " + " 1. The name of the Vim plug-in that is using the miscellaneous scripts + " 2. The version of the Vim plug-in that is using the miscellaneous scripts + " 3. The version of the miscellaneous scripts expected by the plug-in + " + " When the loaded version of the miscellaneous scripts is different from the + " version expected by the plug-in, this function will raise an error message + " that explains what went wrong. if a:required_version != g:xolox#misc#compat#version - let msg = "The %s plug-in requires version %i of the miscellaneous scripts, however version %i was loaded from %s!" - throw printf(msg, a:plugin_name, a:required_version, g:xolox#misc#compat#version, s:misc_directory) + let msg = "The %s %s plug-in expects version %i of the miscellaneous scripts, however version %i was loaded from the directory %s! Please upgrade your plug-ins to the latest releases to resolve this problem." + throw printf(msg, a:plugin_name, a:plugin_version, a:required_version, g:xolox#misc#compat#version, s:misc_directory) endif endfunction