mirror of
https://github.com/inkarkat/vim-ingo-library.git
synced 2026-05-29 11:18:51 +02:00
176 lines
6.4 KiB
VimL
176 lines
6.4 KiB
VimL
" ingo/fs/path.vim: Functions for manipulating a file system path.
|
|
"
|
|
" DEPENDENCIES:
|
|
" - ingo/compat.vim autoload script
|
|
" - ingo/os.vim autoload script
|
|
" - ingo/escape/file.vim autoload script
|
|
"
|
|
" Copyright: (C) 2012-2014 Ingo Karkat
|
|
" The VIM LICENSE applies to this script; see ':help copyright'.
|
|
"
|
|
" Maintainer: Ingo Karkat <ingo@karkat.de>
|
|
"
|
|
" REVISION DATE REMARKS
|
|
" 1.022.010 22-Sep-2014 Use new ingo#compat#glob().
|
|
" 1.019.009 23-May-2014 Add ingo#fs#path#Exists().
|
|
" 1.019.008 21-May-2014 Add ingo#fs#path#IsCaseInsensitive().
|
|
" 1.019.007 07-May-2014 ingo#fs#path#Normalize(): Don't normalize to
|
|
" Cygwin /cygdrive/x/... when the chosen path
|
|
" separator is "\". This would result in a mixed
|
|
" separator style that is not actually handled.
|
|
" Add special normalization to "C:/" on Cygwin via
|
|
" ":/" path separator argument.
|
|
" 1.014.006 26-Sep-2013 ingo#fs#path#Normalize(): Also convert between
|
|
" the different D:\ and /cygdrive/d/ notations on
|
|
" Windows and Cygwin.
|
|
" 1.013.005 13-Sep-2013 Use operating system detection functions from
|
|
" ingo/os.vim.
|
|
" 1.011.004 01-Aug-2013 Extract ingo#fs#path#IsUncPathRoot().
|
|
" 1.010.003 08-Jul-2013 Add prefix to exception thrown from
|
|
" ingo#fs#path#GetRootDir().
|
|
" 1.009.002 26-Jun-2013 Add ingo#fs#path#Equals().
|
|
" Minor: Remove duplication.
|
|
" 1.007.001 01-Jun-2013 file creation from ingofile.vim
|
|
|
|
function! ingo#fs#path#Separator()
|
|
return (exists('+shellslash') && ! &shellslash ? '\' : '/')
|
|
endfunction
|
|
|
|
function! ingo#fs#path#Normalize( filespec, ... )
|
|
"******************************************************************************
|
|
"* PURPOSE:
|
|
" Change all path separators in a:filespec to the passed or the typical format
|
|
" for the current platform.
|
|
" On Windows and Cygwin, also converts between the different D:\ and
|
|
" /cygdrive/d/ notations.
|
|
"* ASSUMPTIONS / PRECONDITIONS:
|
|
" None.
|
|
"* EFFECTS / POSTCONDITIONS:
|
|
" None.
|
|
"* INPUTS:
|
|
" a:filespec Filespec, potentially with mixed / and \ path separators.
|
|
" a:pathSeparator Optional path separator to be used. With the special value
|
|
" of ":/", normalizes to "/", but keeps a "C:/" drive letter
|
|
" prefix instead of translating to "/cygdrive/c/".
|
|
"* RETURN VALUES:
|
|
" a:filespec with uniform path separators, according to the platform.
|
|
"******************************************************************************
|
|
let l:pathSeparator = (a:0 ? (a:1 ==# ':/' ? '/' : a:1) : ingo#fs#path#Separator())
|
|
let l:badSeparator = (l:pathSeparator ==# '/' ? '\' : '/')
|
|
let l:result = tr(a:filespec, l:badSeparator, l:pathSeparator)
|
|
|
|
if ingo#os#IsWinOrDos()
|
|
let l:result = substitute(l:result, '^[/\\]cygdrive[/\\]\(\a\)\ze[/\\]', '\u\1:', '')
|
|
elseif ingo#os#IsCygwin() && l:pathSeparator ==# '/' && ! (a:0 && a:1 ==# ':/')
|
|
let l:result = substitute(l:result, '^\(\a\):', '/cygdrive/\l\1', '')
|
|
endif
|
|
|
|
return l:result
|
|
endfunction
|
|
|
|
function! ingo#fs#path#Combine( first, ... )
|
|
"******************************************************************************
|
|
"* PURPOSE:
|
|
" Concatenate the passed filespec fragments into a filespec, ensuring that all
|
|
" fragments are combined with proper path separators.
|
|
"* ASSUMPTIONS / PRECONDITIONS:
|
|
" None.
|
|
"* EFFECTS / POSTCONDITIONS:
|
|
" None.
|
|
"* INPUTS:
|
|
" Either pass a dirspec and one or many filenames:
|
|
" a:dirspec, a:filename [, a:filename2, ...]
|
|
" Or a single list containing all filespec fragments.
|
|
" [a:dirspec, a:filename, ...]
|
|
"* RETURN VALUES:
|
|
" Combined filespec.
|
|
"******************************************************************************
|
|
if type(a:first) == type([])
|
|
let l:dirspec = a:first[0]
|
|
let l:filenames = a:first[1:]
|
|
else
|
|
let l:dirspec = a:first
|
|
let l:filenames = a:000
|
|
endif
|
|
|
|
" Use path separator as exemplified by the passed dirspec.
|
|
if l:dirspec =~# '\' && l:dirspec !~# '/'
|
|
let l:pathSeparator = '\'
|
|
elseif l:dirspec =~# '/'
|
|
let l:pathSeparator = '/'
|
|
else
|
|
" The dirspec doesn't contain a path separator, fall back to the
|
|
" system's default.
|
|
let l:pathSeparator = ingo#fs#path#Separator()
|
|
endif
|
|
|
|
let l:filespec = l:dirspec
|
|
for l:filename in l:filenames
|
|
let l:filename = substitute(l:filename, '^[/\\]', '', '')
|
|
let l:filespec .= (l:filespec =~# '^$\|[/\\]$' ? '' : l:pathSeparator) . l:filename
|
|
endfor
|
|
|
|
return l:filespec
|
|
endfunction
|
|
|
|
function! ingo#fs#path#IsUncPathRoot( filespec )
|
|
let l:ps = escape(ingo#fs#path#Separator(), '\')
|
|
let l:uncPathPattern = printf('^%s%s[^%s]\+%s[^%s]\+$', l:ps, l:ps, l:ps, l:ps, l:ps)
|
|
return (a:filespec =~# l:uncPathPattern)
|
|
endfunction
|
|
function! ingo#fs#path#GetRootDir( filespec )
|
|
if ! ingo#os#IsWinOrDos()
|
|
return '/'
|
|
endif
|
|
|
|
let l:dir = a:filespec
|
|
while fnamemodify(l:dir, ':h') !=# l:dir && ! ingo#fs#path#IsUncPathRoot(l:dir)
|
|
let l:dir = fnamemodify(l:dir, ':h')
|
|
endwhile
|
|
|
|
if empty(l:dir)
|
|
throw 'GetRootDir: Could not determine root dir!'
|
|
endif
|
|
|
|
return l:dir
|
|
endfunction
|
|
|
|
function! ingo#fs#path#IsCaseInsensitive( ... )
|
|
return ingo#os#IsWinOrDos() " Note: Check based on path not yet implemented.
|
|
endfunction
|
|
|
|
function! ingo#fs#path#Equals( p1, p2 )
|
|
if ingo#fs#path#IsCaseInsensitive(a:p1) || ingo#fs#path#IsCaseInsensitive(a:p2)
|
|
return a:p1 ==? a:p2 || ingo#fs#path#Normalize(fnamemodify(a:p1, ':p')) ==? ingo#fs#path#Normalize(fnamemodify(a:p2, ':p'))
|
|
else
|
|
return a:p1 ==# a:p2 || ingo#fs#path#Normalize(fnamemodify(resolve(a:p1), ':p')) ==# ingo#fs#path#Normalize(fnamemodify(resolve(a:p2), ':p'))
|
|
endif
|
|
endfunction
|
|
|
|
function! ingo#fs#path#Exists( filespec )
|
|
"******************************************************************************
|
|
"* PURPOSE:
|
|
" Test whether the passed a:filespec exists (as a file or directory). This is
|
|
" like the combination of filereadable() and isdirectory(), but without the
|
|
" requirement that the file must be readable.
|
|
"* ASSUMPTIONS / PRECONDITIONS:
|
|
" None.
|
|
"* EFFECTS / POSTCONDITIONS:
|
|
" None.
|
|
"* INPUTS:
|
|
" a:filespec Filespec or dirspec.
|
|
"* RETURN VALUES:
|
|
" 0 if there's no such file or directory, 1 if it exists.
|
|
"******************************************************************************
|
|
" I suppose these are faster than the glob(), and this avoids any escaping
|
|
" issues, too, so it is more robust.
|
|
if filereadable(a:filespec) || isdirectory(a:filespec)
|
|
return 1
|
|
endif
|
|
|
|
let l:filespec = ingo#escape#file#wildcardescape(a:filespec)
|
|
return ! empty(ingo#compat#glob(l:filespec, 1))
|
|
endfunction
|
|
|
|
" vim: set ts=8 sts=4 sw=4 noexpandtab ff=unix fdm=syntax :
|