11" Vim auto-load script
22" Author: Peter Odding <peter@peterodding.com>
3- " Last Change: April 18 , 2013
3+ " Last Change: April 28 , 2013
44" URL: http://peterodding.com/code/vim/misc/
55
66let s: windows_compatible = has (' win32' ) || has (' win64' )
77let s: mac_os_x_compatible = has (' macunix' )
88
9- function ! xolox#misc#path#which (... )
9+ function ! xolox#misc#path#which (... ) " {{{1
10+ " Scan the executable search path for programs.
1011 let extensions = s: windows_compatible ? split ($PATHEXT , ' ;' ) : [' ' ]
1112 let matches = []
1213 let checked = {}
@@ -29,13 +30,24 @@ function! xolox#misc#path#which(...)
2930 return matches
3031endfunction
3132
32- " Split a pathname into a list of path components.
33-
34- function ! xolox#misc#path#split (path )
33+ function ! xolox#misc#path#split (path ) " {{{1
34+ " Split a pathname into a list of path components.
3535 if type (a: path ) == type (' ' )
3636 if s: windows_compatible
37- return split (a: path , ' [\/]\+' )
37+ if a: path = ~ ' ^[\/][\/]'
38+ " On Windows, pathnames starting with two slashes or backslashes are
39+ " UNC paths where the leading slashes are significant... In this case
40+ " we split like this:
41+ " '//server/share/directory' -> ['//server', 'share', 'directory']
42+ return split (a: path , ' \%>2c[\/]\+' )
43+ else
44+ " If it's not a UNC path we can simply split on slashes & backslashes.
45+ return split (a: path , ' [\/]\+' )
46+ endif
3847 else
48+ " Everything except Windows is treated like UNIX until someone
49+ " has a better suggestion :-). In this case we split like this:
50+ " '/foo/bar/baz' -> ['/', 'foo', 'bar', 'baz']
3951 let absolute = (a: path = ~ ' ^/' )
4052 let segments = split (a: path , ' /\+' )
4153 return absolute ? insert (segments, ' /' ) : segments
@@ -44,42 +56,61 @@ function! xolox#misc#path#split(path)
4456 return []
4557endfunction
4658
47- " Join a list of path components into a pathname.
48-
49- function ! xolox#misc#path#join (parts)
59+ function ! xolox#misc#path#join (parts) " {{{1
60+ " Join a list of pathname components into a single pathname.
5061 if type (a: parts ) == type ([])
51- if ! s: windows_compatible && a: parts [0 ] == ' /'
52- return join (a: parts , ' /' )[1 : -1 ]
62+ if s: windows_compatible
63+ return join (a: parts , xolox#misc#path#directory_separator ())
64+ elseif a: parts [0 ] == ' /'
65+ " Absolute path on UNIX (non-Windows).
66+ return ' /' . join (a: parts [1 :], ' /' )
5367 else
68+ " Relative path on UNIX (non-Windows).
5469 return join (a: parts , ' /' )
5570 endif
5671 endif
5772 return ' '
5873endfunction
5974
60- " Canonicalize and resolve a pathname.
75+ function ! xolox#misc#path#directory_separator () " {{{1
76+ " Find the preferred directory separator for the platform and settings.
77+ return exists (' +shellslash' ) && &shellslash ? ' /' : ' \'
78+ endfunction
6179
62- function ! xolox#misc#path#absolute (path )
80+ function ! xolox#misc#path#absolute (path ) " {{{1
81+ " Canonicalize and resolve a pathname, regardless of whether it exists. This
82+ " is intended to support string comparison to determine whether two pathnames
83+ " point to the same directory or file.
6384 if type (a: path ) == type (' ' )
64- let path = fnamemodify (a: path , ' :p' )
65- " resolve() doesn't work when there's a trailing path separator.
66- if path = ~ ' /$'
67- let stripped_slash = 1
68- let path = substitute (path , ' /$' , ' ' , ' ' )
85+ let path = a: path
86+ " Make the pathname absolute.
87+ if path = ~ ' ^\~'
88+ " Expand ~ to $HOME.
89+ let path = $HOME . ' /' . path [1 :]
90+ elseif xolox#misc#path#is_relative (path )
91+ " Make relative pathnames absolute.
92+ let path = getcwd () . ' /' . path
6993 endif
70- let path = resolve (path )
71- " Restore the path separator after calling resolve().
72- if exists (' stripped_slash' ) && path !~ ' /$'
73- let path .= ' /'
94+ " Resolve symbolic links to find the canonical pathname. In my tests this
95+ " also removes all symbolic pathname segments (`.' and `..'), even when
96+ " the pathname does not exist. Also there used to be a bug in resolve()
97+ " where it wouldn't resolve pathnames ending in a directory separator.
98+ " Since it's not much trouble to work around, that's what we do.
99+ let path = resolve (substitute (path , s: windows_compatible ? ' [\/]\+$' : ' /\+$' , ' ' , ' ' ))
100+ " Normalize directory separators (especially relevant on Windows).
101+ let parts = xolox#misc#path#split (path )
102+ if s: windows_compatible && parts[0 ] = ~ ' ^[\/][\/]'
103+ " Also normalize the two leading "directory separators" (I'm not
104+ " sure what else to call them :-) in Windows UNC pathnames.
105+ let parts[0 ] = repeat (xolox#misc#path#directory_separator (), 2 ) . parts[0 ][2 :]
74106 endif
75- return path
107+ return xolox#misc# path#join (parts)
76108 endif
77109 return ' '
78110endfunction
79111
80- " Make an absolute pathname relative.
81-
82- function ! xolox#misc#path#relative (path , base)
112+ function ! xolox#misc#path#relative (path , base) " {{{1
113+ " Make an absolute pathname relative.
83114 let path = xolox#misc#path#split (a: path )
84115 let base = xolox#misc#path#split (a: base )
85116 while path != [] && base != [] && path [0 ] == base[0 ]
@@ -90,9 +121,9 @@ function! xolox#misc#path#relative(path, base)
90121 return xolox#misc#path#join (distance + path )
91122endfunction
92123
93- " Join a directory and filename into a single pathname.
94124
95- function ! xolox#misc#path#merge (parent, child, ... )
125+ function ! xolox#misc#path#merge (parent, child, ... ) " {{{1
126+ " Join a directory and filename into a single pathname.
96127 " TODO Use isabs()!
97128 if type (a: parent ) == type (' ' ) && type (a: child ) == type (' ' )
98129 if s: windows_compatible
@@ -108,9 +139,8 @@ function! xolox#misc#path#merge(parent, child, ...)
108139 return ' '
109140endfunction
110141
111- " Find the common prefix of path components in a list of pathnames.
112-
113- function ! xolox#misc#path#commonprefix (paths)
142+ function ! xolox#misc#path#commonprefix (paths) " {{{1
143+ " Find the common prefix of path components in a list of pathnames.
114144 let common = xolox#misc#path#split (a: paths [0 ])
115145 for path in a: paths
116146 let index = 0
@@ -127,9 +157,8 @@ function! xolox#misc#path#commonprefix(paths)
127157 return xolox#misc#path#join (common)
128158endfunction
129159
130- " Encode a pathname so it can be used as a filename.
131-
132- function ! xolox#misc#path#encode (path )
160+ function ! xolox#misc#path#encode (path ) " {{{1
161+ " Encode a pathname so it can be used as a filename.
133162 if s: windows_compatible
134163 let mask = ' [*|\\/:"<>?%]'
135164 elseif s: mac_os_x_compatible
@@ -140,13 +169,13 @@ function! xolox#misc#path#encode(path)
140169 return substitute (a: path , mask, ' \=printf("%%%x", char2nr(submatch(0)))' , ' g' )
141170endfunction
142171
143- " Decode a pathname previously encoded with xolox#misc#path#encode().
144172
145- function ! xolox#misc#path#decode (encoded_path)
173+ function ! xolox#misc#path#decode (encoded_path) " {{{1
174+ " Decode a pathname previously encoded with xolox#misc#path#encode().
146175 return substitute (a: encoded_path , ' %\(\x\x\?\)' , ' \=nr2char("0x" . submatch(1))' , ' g' )
147176endfunction
148177
149- " Check whether two pathnames point to the same file.
178+ " xolox#misc#path#equals(a, b) - Check whether two pathnames point to the same file. {{{1
150179
151180if s: windows_compatible
152181 function ! xolox#misc#path#equals (a , b )
158187 endfunction
159188endif
160189
161- " Check whether a path is relative.
162-
163- function ! xolox#misc#path#is_relative (path )
190+ function ! xolox#misc#path#is_relative (path ) " {{{1
191+ " Check whether a path is relative.
164192 if a: path = ~ ' ^\w\+://'
165193 return 0
166194 elseif s: windows_compatible
@@ -170,9 +198,8 @@ function! xolox#misc#path#is_relative(path)
170198 endif
171199endfunction
172200
173- " Create a temporary directory and return the path.
174-
175- function ! xolox#misc#path#tempdir ()
201+ function ! xolox#misc#path#tempdir () " {{{1
202+ " Create a temporary directory and return the path.
176203 if ! exists (' s:tempdir_counter' )
177204 let s: tempdir_counter = 1
178205 endif
0 commit comments