From a7ed49ed589cfe5f84bfeda317d03a874002e1a9 Mon Sep 17 00:00:00 2001 From: Peter Odding Date: Sun, 28 Apr 2013 15:08:19 +0200 Subject: [PATCH 1/2] Change the repository layout to that of a proper Vim plug-in --- README.md | 39 +++++++++---- buffer.vim => autoload/xolox/misc/buffer.vim | 0 compat.vim => autoload/xolox/misc/compat.vim | 0 .../xolox/misc/complete.vim | 0 escape.vim => autoload/xolox/misc/escape.vim | 0 list.vim => autoload/xolox/misc/list.vim | 0 msg.vim => autoload/xolox/misc/msg.vim | 0 open.vim => autoload/xolox/misc/open.vim | 0 option.vim => autoload/xolox/misc/option.vim | 0 os.vim => autoload/xolox/misc/os.vim | 0 path.vim => autoload/xolox/misc/path.vim | 56 +++++++++---------- str.vim => autoload/xolox/misc/str.vim | 0 timer.vim => autoload/xolox/misc/timer.vim | 0 13 files changed, 55 insertions(+), 40 deletions(-) rename buffer.vim => autoload/xolox/misc/buffer.vim (100%) rename compat.vim => autoload/xolox/misc/compat.vim (100%) rename complete.vim => autoload/xolox/misc/complete.vim (100%) rename escape.vim => autoload/xolox/misc/escape.vim (100%) rename list.vim => autoload/xolox/misc/list.vim (100%) rename msg.vim => autoload/xolox/misc/msg.vim (100%) rename open.vim => autoload/xolox/misc/open.vim (100%) rename option.vim => autoload/xolox/misc/option.vim (100%) rename os.vim => autoload/xolox/misc/os.vim (100%) rename path.vim => autoload/xolox/misc/path.vim (76%) rename str.vim => autoload/xolox/misc/str.vim (100%) rename timer.vim => autoload/xolox/misc/timer.vim (100%) diff --git a/README.md b/README.md index f375fe6..685d9c6 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,41 @@ # 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. I include this repository as a subdirectory of my plug-in repositories using the following commands: +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. - $ git remote add -f vim-misc https://github.com/xolox/vim-misc.git - $ git merge -s ours --no-commit vim-misc/master - $ git read-tree --prefix=autoload/xolox/misc/ -u vim-misc/master - $ git commit -m "Merge vim-misc repository as subdirectory" +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 above trick is called the [subtree merge strategy] [merge-strategy]. To update a plug-in repository to the latest version of the miscellaneous auto-load scripts I execute the following command: +## How does it work? - $ git pull -s subtree vim-misc master +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 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]. +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. -2. My second attempt to solve the problem used git submodules which seemed like the ideal solution until I actually started doing it. 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). -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 have been using for more than two years now. +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 @@ -39,11 +55,14 @@ This software is licensed under the [MIT license] [mit]. © 2013 Peter Odding <>. +[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 diff --git a/buffer.vim b/autoload/xolox/misc/buffer.vim similarity index 100% rename from buffer.vim rename to autoload/xolox/misc/buffer.vim diff --git a/compat.vim b/autoload/xolox/misc/compat.vim similarity index 100% rename from compat.vim rename to autoload/xolox/misc/compat.vim diff --git a/complete.vim b/autoload/xolox/misc/complete.vim similarity index 100% rename from complete.vim rename to autoload/xolox/misc/complete.vim diff --git a/escape.vim b/autoload/xolox/misc/escape.vim similarity index 100% rename from escape.vim rename to autoload/xolox/misc/escape.vim diff --git a/list.vim b/autoload/xolox/misc/list.vim similarity index 100% rename from list.vim rename to autoload/xolox/misc/list.vim diff --git a/msg.vim b/autoload/xolox/misc/msg.vim similarity index 100% rename from msg.vim rename to autoload/xolox/misc/msg.vim diff --git a/open.vim b/autoload/xolox/misc/open.vim similarity index 100% rename from open.vim rename to autoload/xolox/misc/open.vim diff --git a/option.vim b/autoload/xolox/misc/option.vim similarity index 100% rename from option.vim rename to autoload/xolox/misc/option.vim diff --git a/os.vim b/autoload/xolox/misc/os.vim similarity index 100% rename from os.vim rename to autoload/xolox/misc/os.vim diff --git a/path.vim b/autoload/xolox/misc/path.vim similarity index 76% rename from path.vim rename to autoload/xolox/misc/path.vim index 6f8fe44..67ef8d8 100644 --- a/path.vim +++ b/autoload/xolox/misc/path.vim @@ -1,12 +1,13 @@ " Vim auto-load script " Author: Peter Odding -" Last Change: April 18, 2013 +" Last Change: April 22, 2013 " URL: http://peterodding.com/code/vim/misc/ let s:windows_compatible = has('win32') || has('win64') let s:mac_os_x_compatible = has('macunix') -function! xolox#misc#path#which(...) +function! xolox#misc#path#which(...) " {{{1 + " Scan the executable search path for programs. let extensions = s:windows_compatible ? split($PATHEXT, ';') : [''] let matches = [] let checked = {} @@ -29,9 +30,8 @@ function! xolox#misc#path#which(...) return matches endfunction -" Split a pathname into a list of path components. - -function! xolox#misc#path#split(path) +function! xolox#misc#path#split(path) " {{{1 + " Split a pathname into a list of path components. if type(a:path) == type('') if s:windows_compatible return split(a:path, '[\/]\+') @@ -44,9 +44,8 @@ function! xolox#misc#path#split(path) return [] endfunction -" Join a list of path components into a pathname. - -function! xolox#misc#path#join(parts) +function! xolox#misc#path#join(parts) " {{{1 + " Join a list of path components into a pathname. if type(a:parts) == type([]) if !s:windows_compatible && a:parts[0] == '/' return join(a:parts, '/')[1 : -1] @@ -57,9 +56,10 @@ function! xolox#misc#path#join(parts) return '' endfunction -" Canonicalize and resolve a pathname. - -function! xolox#misc#path#absolute(path) +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. if type(a:path) == type('') let path = fnamemodify(a:path, ':p') " resolve() doesn't work when there's a trailing path separator. @@ -77,9 +77,8 @@ function! xolox#misc#path#absolute(path) return '' endfunction -" Make an absolute pathname relative. - -function! xolox#misc#path#relative(path, base) +function! xolox#misc#path#relative(path, base) " {{{1 + " Make an absolute pathname relative. let path = xolox#misc#path#split(a:path) let base = xolox#misc#path#split(a:base) while path != [] && base != [] && path[0] == base[0] @@ -90,9 +89,9 @@ function! xolox#misc#path#relative(path, base) return xolox#misc#path#join(distance + path) endfunction -" Join a directory and filename into a single pathname. -function! xolox#misc#path#merge(parent, child, ...) +function! xolox#misc#path#merge(parent, child, ...) " {{{1 + " Join a directory and filename into a single pathname. " TODO Use isabs()! if type(a:parent) == type('') && type(a:child) == type('') if s:windows_compatible @@ -108,9 +107,8 @@ function! xolox#misc#path#merge(parent, child, ...) return '' endfunction -" Find the common prefix of path components in a list of pathnames. - -function! xolox#misc#path#commonprefix(paths) +function! xolox#misc#path#commonprefix(paths) " {{{1 + " Find the common prefix of path components in a list of pathnames. let common = xolox#misc#path#split(a:paths[0]) for path in a:paths let index = 0 @@ -127,9 +125,8 @@ function! xolox#misc#path#commonprefix(paths) return xolox#misc#path#join(common) endfunction -" Encode a pathname so it can be used as a filename. - -function! xolox#misc#path#encode(path) +function! xolox#misc#path#encode(path) " {{{1 + " Encode a pathname so it can be used as a filename. if s:windows_compatible let mask = '[*|\\/:"<>?%]' elseif s:mac_os_x_compatible @@ -140,12 +137,13 @@ function! xolox#misc#path#encode(path) return substitute(a:path, mask, '\=printf("%%%x", char2nr(submatch(0)))', 'g') endfunction -" Decode a pathname previously encoded with xolox#misc#path#encode(). -function! xolox#misc#path#decode(encoded_path) +function! xolox#misc#path#decode(encoded_path) " {{{1 + " Decode a pathname previously encoded with xolox#misc#path#encode(). return substitute(a:encoded_path, '%\(\x\x\?\)', '\=nr2char("0x" . submatch(1))', 'g') endfunction +" xolox#misc#path#equals(a, b) {{{1 " Check whether two pathnames point to the same file. if s:windows_compatible @@ -158,9 +156,8 @@ else endfunction endif -" Check whether a path is relative. - -function! xolox#misc#path#is_relative(path) +function! xolox#misc#path#is_relative(path) " {{{1 + " Check whether a path is relative. if a:path =~ '^\w\+://' return 0 elseif s:windows_compatible @@ -170,9 +167,8 @@ function! xolox#misc#path#is_relative(path) endif endfunction -" Create a temporary directory and return the path. - -function! xolox#misc#path#tempdir() +function! xolox#misc#path#tempdir() " {{{1 + " Create a temporary directory and return the path. if !exists('s:tempdir_counter') let s:tempdir_counter = 1 endif diff --git a/str.vim b/autoload/xolox/misc/str.vim similarity index 100% rename from str.vim rename to autoload/xolox/misc/str.vim diff --git a/timer.vim b/autoload/xolox/misc/timer.vim similarity index 100% rename from timer.vim rename to autoload/xolox/misc/timer.vim From 38ded48ec0430c360abcdecd4e23cd21ea2779f8 Mon Sep 17 00:00:00 2001 From: Peter Odding Date: Sun, 28 Apr 2013 21:56:10 +0200 Subject: [PATCH 2/2] Improve the xolox#misc#path#* functions - xolox#misc#path#absolute() was completely rewritten. It no longer uses fnamemodify() which doesn't work on non-existing pathnames. It's now written on top of xolox#misc#path#split() and xolox#misc#path#join(). It should now properly normalize directory separators as suggested in this pull request: xolox/vim-session#29 - xolox#misc#path#split() and xolox#misc#path#join() were enhanced with proper handling of UNC paths and respect for the 'shellslash' option on Windows (tested on Windows XP SP2, the only Windows I have available) --- autoload/xolox/misc/compat.vim | 4 +-- autoload/xolox/misc/path.vim | 65 +++++++++++++++++++++++++--------- 2 files changed, 50 insertions(+), 19 deletions(-) diff --git a/autoload/xolox/misc/compat.vim b/autoload/xolox/misc/compat.vim index 83d00a0..52c5c28 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: April 20, 2013 +" Last Change: April 28, 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 = 1 +let g:xolox#misc#compat#version = 2 " 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 67ef8d8..6ad8be4 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 22, 2013 +" Last Change: April 28, 2013 " URL: http://peterodding.com/code/vim/misc/ let s:windows_compatible = has('win32') || has('win64') @@ -34,8 +34,20 @@ function! xolox#misc#path#split(path) " {{{1 " Split a pathname into a list of path components. if type(a:path) == type('') if s:windows_compatible - return split(a:path, '[\/]\+') + 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'] + 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'] let absolute = (a:path =~ '^/') let segments = split(a:path, '/\+') return absolute ? insert(segments, '/') : segments @@ -45,34 +57,54 @@ function! xolox#misc#path#split(path) " {{{1 endfunction function! xolox#misc#path#join(parts) " {{{1 - " Join a list of path components into a pathname. + " Join a list of pathname components into a single pathname. if type(a:parts) == type([]) - if !s:windows_compatible && a:parts[0] == '/' - return join(a:parts, '/')[1 : -1] + if s:windows_compatible + return join(a:parts, xolox#misc#path#directory_separator()) + elseif a:parts[0] == '/' + " Absolute path on UNIX (non-Windows). + return '/' . join(a:parts[1:], '/') else + " Relative path on UNIX (non-Windows). return join(a:parts, '/') endif endif return '' endfunction +function! xolox#misc#path#directory_separator() " {{{1 + " Find the preferred directory separator for the platform and settings. + return exists('+shellslash') && &shellslash ? '/' : '\' +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. if type(a:path) == type('') - let path = fnamemodify(a:path, ':p') - " resolve() doesn't work when there's a trailing path separator. - if path =~ '/$' - let stripped_slash = 1 - let path = substitute(path, '/$', '', '') + let path = a:path + " Make the pathname absolute. + if path =~ '^\~' + " Expand ~ to $HOME. + let path = $HOME . '/' . path[1:] + elseif xolox#misc#path#is_relative(path) + " Make relative pathnames absolute. + let path = getcwd() . '/' . path endif - let path = resolve(path) - " Restore the path separator after calling resolve(). - if exists('stripped_slash') && path !~ '/$' - let path .= '/' + " Resolve symbolic links to find the canonical pathname. In my tests this + " also removes all symbolic pathname segments (`.' and `..'), even when + " the pathname does not exist. Also there used to be a bug in resolve() + " where it wouldn't resolve pathnames ending in a directory separator. + " Since it's not much trouble to work around, that's what we do. + let path = resolve(substitute(path, s:windows_compatible ? '[\/]\+$' : '/\+$', '', '')) + " Normalize directory separators (especially relevant on Windows). + let parts = xolox#misc#path#split(path) + if s:windows_compatible && parts[0] =~ '^[\/][\/]' + " Also normalize the two leading "directory separators" (I'm not + " sure what else to call them :-) in Windows UNC pathnames. + let parts[0] = repeat(xolox#misc#path#directory_separator(), 2) . parts[0][2:] endif - return path + return xolox#misc#path#join(parts) endif return '' endfunction @@ -143,8 +175,7 @@ function! xolox#misc#path#decode(encoded_path) " {{{1 return substitute(a:encoded_path, '%\(\x\x\?\)', '\=nr2char("0x" . submatch(1))', 'g') endfunction -" xolox#misc#path#equals(a, b) {{{1 -" Check whether two pathnames point to the same file. +" xolox#misc#path#equals(a, b) - Check whether two pathnames point to the same file. {{{1 if s:windows_compatible function! xolox#misc#path#equals(a, b)