Skip to content

Commit

Permalink
Preserve trailing slash in maktaba#path#Split (#199)
Browse files Browse the repository at this point in the history
Also improves maktaba#path#MakeRelative to preserve trailing slash and adds a maktaba#path#StripTrailingSlash().

Fixes #137 and #175.
  • Loading branch information
dbarnett authored Apr 29, 2017
1 parent 6cc1f66 commit 9dd8adb
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 35 deletions.
48 changes: 35 additions & 13 deletions autoload/maktaba/path.vim
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ else
endif
let s:trailing_slash = s:unescaped_slash . '$'
let s:trailing_slashes = s:unescaped_slash . '+$'
let s:nontrailing_slash = s:unescaped_slash . '\ze.'

let s:drive_backslash = '\v^\a:\\\\'
let s:drive_frontslash = '\v^\a://'
Expand Down Expand Up @@ -80,6 +81,14 @@ function! maktaba#path#AsDir(path) abort
endfunction


""
" Returns {path} with trailing slashes (if any) stripped (forward or backslash,
" depending on platform).
function! maktaba#path#StripTrailingSlash(path) abort
return substitute(a:path, s:trailing_slashes, '', '')
endfunction


""
" Returns the root component of {path}.
" In unix, / is the only root.
Expand Down Expand Up @@ -153,11 +162,21 @@ endfunction


""
" Splits {path} on the system separator character.
" Splits {path} on the system separator character, preserving root and trailing
" slash, if any.
" For example: >
" :echomsg maktaba#path#Split('relative/path')
" :echomsg maktaba#path#Split('/absolute/path')
" :echomsg maktaba#path#Split('path/to/dir/')
" <
" will echo
" - `['relative', 'path']`
" - `['/absolute', 'path']`
" - `['path', 'to', 'dir/']`
function! maktaba#path#Split(path) abort
" /foo/bar/baz/ splits to root '/' and components ['foo', 'bar', 'baz/'].
let l:root = maktaba#path#RootComponent(a:path)
let l:components = split(a:path[len(l:root):], s:unescaped_slash)
" /foo/bar/baz splits to ['/', 'foo', 'bar', 'baz'].
let l:components = split(a:path[len(l:root):], s:nontrailing_slash, 1)
if !empty(l:root)
call insert(l:components, l:root)
endif
Expand Down Expand Up @@ -226,22 +245,26 @@ function! maktaba#path#MakeRelative(root, path) abort
call s:EnsurePathsHaveSharedRoot(a:root, a:path)

" Starting from the beginning, discard directories common to both.
let l:pathparts = maktaba#path#Split(a:path)
let l:rootparts = maktaba#path#Split(a:root)
let l:is_dir = a:path =~# s:trailing_slash
let l:pathparts = maktaba#path#Split(maktaba#path#StripTrailingSlash(a:path))
let l:rootparts = maktaba#path#Split(maktaba#path#StripTrailingSlash(a:root))
while !empty(l:pathparts) && !empty(l:rootparts) &&
\ l:pathparts[0] ==# l:rootparts[0]
call remove(l:pathparts, 0)
call remove(l:rootparts, 0)
endwhile

if empty(l:rootparts) && empty(l:pathparts)
return '.'
let l:relative_path = '.'
else
" l:rootparts now contains the directories we must traverse to reach the
" common ancestor of root and path. Replacing those with '..' takes us to
" the common ancestor. Then the remaining l:pathparts take us to the
" destination.
let l:relative_path =
\ maktaba#path#Join(map(l:rootparts, '".."') + l:pathparts)
endif

" l:rootparts now contains the directories we must traverse to reach the
" common ancestor of root and path. Replacing those with '..' takes us to the
" common ancestor. Then the remaining l:pathparts take us to the destination.
return maktaba#path#Join(map(l:rootparts, '".."') + l:pathparts)
return l:is_dir ? maktaba#path#AsDir(l:relative_path) : l:relative_path
endfunction


Expand Down Expand Up @@ -277,8 +300,7 @@ function! maktaba#path#MakeDirectory(dir) abort
let l:dir = a:dir
" Vim bug before 7.4 patch 6: mkdir chokes when a path has a trailing slash.
if v:version < 704 || (v:version == 704 && !has('patch6'))
" This is a hackish way to remove a trailing slash.
let l:dir = maktaba#path#Join(maktaba#path#Split(l:dir))
let l:dir = substitute(l:dir, s:trailing_slashes, '', '')
endif

try
Expand Down
12 changes: 8 additions & 4 deletions autoload/maktaba/plugin.vim
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ function! s:PluginNameFromDir(dir) abort
if len(l:splitpath) == 0
throw maktaba#error#BadValue('Found empty path.')
endif
let l:name = maktaba#plugin#CanonicalName(l:splitpath[-1])
let l:name = maktaba#plugin#CanonicalName(
\ maktaba#path#StripTrailingSlash(l:splitpath[-1]))
return l:name
endfunction

Expand Down Expand Up @@ -540,7 +541,8 @@ function! s:GetSubdirs() dict abort
" Glob includes trailing slash, which makes glob() only detect directories.
let l:direct_glob = maktaba#path#Join([self.location, '*', ''])
let l:direct_dirs = split(glob(l:direct_glob, 1), "\n")
let self._dirs = map(l:direct_dirs, 'maktaba#path#Split(v:val)[-1]')
let self._dirs = map(
\ l:direct_dirs, 'maktaba#path#AsDir(maktaba#path#Split(v:val)[-1])')
endif
return self._dirs
endfunction
Expand All @@ -555,7 +557,8 @@ function! s:GetAfterSubdirs() dict abort
" Glob includes trailing slash, which makes glob() only detect directories.
let l:after_glob = maktaba#path#Join([self.location, 'after', '*', ''])
let l:after_dirs = split(glob(l:after_glob, 1), "\n")
let self._after_dirs = map(l:after_dirs, 'maktaba#path#Split(v:val)[-1]')
let self._after_dirs = map(
\ l:after_dirs, 'maktaba#path#AsDir(maktaba#path#Split(v:val)[-1])')
endif
return self._after_dirs
endfunction
Expand Down Expand Up @@ -705,7 +708,8 @@ endfunction
function! maktaba#plugin#HasDir(dir) dict abort
let l:dirs = call('s:GetSubdirs', [], self)
let l:after_dirs = call('s:GetAfterSubdirs', [], self)
return index(l:dirs, a:dir) > -1 || index(l:after_dirs, a:dir) > -1
return index(l:dirs, maktaba#path#AsDir(a:dir)) > -1 ||
\ index(l:after_dirs, maktaba#path#AsDir(a:dir)) > -1
endfunction


Expand Down
18 changes: 5 additions & 13 deletions autoload/maktaba/rtp.vim
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,12 @@ function! s:GetLeafDir(path) abort
if l:leaf isnot 0
return l:leaf
endif
let s:cache_leafdirs[a:path] = maktaba#path#Split(a:path)[-1]
let s:cache_leafdirs[a:path] =
\ maktaba#path#StripTrailingSlash(maktaba#path#Split(a:path)[-1])
return s:cache_leafdirs[a:path]
endfunction


""
" Returns {path} with trailing slashes safely removed.
" Used by @function(#Join) since 'runtimepath' typically stores paths without
" trailing slashes.
function! s:StripTrailingSlash(path) abort
" Uses maktaba#path#AsDir to ensure a single trailing slash, then removes it.
return maktaba#path#AsDir(a:path)[:-2]
endfunction


""
" Split [paths], a string of comma-separated path entries, into a list of paths.
" Handles unescaping the commas.
Expand Down Expand Up @@ -74,8 +65,9 @@ endfunction
" representation, with trailing slashes included.
function! maktaba#rtp#Join(paths) abort
call maktaba#ensure#IsList(a:paths)
return join(
\ map(copy(a:paths), "escape(s:StripTrailingSlash(v:val), '\,')"), ',')
return join(map(
\ copy(a:paths),
\ "escape(maktaba#path#StripTrailingSlash(v:val), '\,')"), ',')
endfunction


Expand Down
16 changes: 15 additions & 1 deletion doc/maktaba.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1245,6 +1245,10 @@ maktaba#path#AsDir({path}) *maktaba#path#AsDir()*
directory paths, so utilities like |maktaba#path#Dirname()| don't try to
interpret them as file paths.

maktaba#path#StripTrailingSlash({path}) *maktaba#path#StripTrailingSlash()*
Returns {path} with trailing slashes (if any) stripped (forward or
backslash, depending on platform).

maktaba#path#RootComponent({path}) *maktaba#path#RootComponent()*
Returns the root component of {path}. In unix, / is the only root. In
windows, the root can be \ (which vim treats as the default drive), a drive
Expand All @@ -1266,7 +1270,17 @@ maktaba#path#Join({components}) *maktaba#path#Join()*
is '/absolute'

maktaba#path#Split({path}) *maktaba#path#Split()*
Splits {path} on the system separator character.
Splits {path} on the system separator character, preserving root and
trailing slash, if any. For example:
>
:echomsg maktaba#path#Split('relative/path')
:echomsg maktaba#path#Split('/absolute/path')
:echomsg maktaba#path#Split('path/to/dir/')
<
will echo
`['relative', 'path']`
`['/absolute', 'path']`
`['path', 'to', 'dir/']`

maktaba#path#Basename({path}) *maktaba#path#Basename()*
The basename of {path}. Trailing slash matters. Consider:
Expand Down
10 changes: 7 additions & 3 deletions vroom/path.vroom
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ helps you recognize relative paths.
:echomsg string(maktaba#path#Split('relative/path'))
~ ['relative', 'path']

Trailing slashes make no difference, though:
Trailing slashes are preserved as well:

:echomsg string(maktaba#path#Split('relative/path/'))
~ ['relative', 'path']
~ ['relative', 'path/']



Expand Down Expand Up @@ -155,8 +155,12 @@ makes one absolute path relative to another.
| '/deep/in/the/heart/of/texas')
~ ../../../../heart/of/texas

:echomsg maktaba#path#MakeRelative('/point/A/', '/point/A/')
:echomsg maktaba#path#MakeRelative('/point/A', '/point/A')
~ .
:echomsg maktaba#path#MakeRelative('/point/A/', '/point/A/')
~ ./
:echomsg maktaba#path#MakeRelative('/point/A', '/point/A/')
~ ./

Your paths had better be absolute before trying this.

Expand Down
3 changes: 2 additions & 1 deletion vroom/plugin.vroom
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,8 @@ and location:

:echomsg g:plugin.name
~ myplugin
:echomsg maktaba#path#Split(g:plugin.location)[-1]
:echomsg maktaba#path#StripTrailingSlash(
|maktaba#path#Split(g:plugin.location)[-1])
~ myplugin

These, too, are somewhat boring (except for the fact that you should NEVER EVER
Expand Down

0 comments on commit 9dd8adb

Please sign in to comment.