tests/bundled/lh-vim-lib/autoload/lh/path.vim @ c839b22aa015

defer "can't load gundo" warning
Only inform that gundo didn't load when an attempt is made to invoke it.
Too many hosts still don't have vim 7.3, but I'd like to do a straight
update of my vimfiles from my dvcs without needing any fiddly
conditionals in order to load vim without a warning message.
author Seth Milliken <seth@janrain.com>
date Wed, 29 Dec 2010 15:16:33 -0800
parents 2b3d5ee5c4a4
children (none)
"=============================================================================
" $Id: path.vim 237 2010-06-01 00:44:35Z luc.hermitte $
" File:		autoload/lh/path.vim                               {{{1
" Author:	Luc Hermitte <EMAIL:hermitte {at} free {dot} fr>
"		<URL:http://code.google.com/p/lh-vim/>
" Version:	2.2.1
" Created:	23rd Jan 2007
" Last Update:	11th Feb 2008
"------------------------------------------------------------------------
" Description:	
"       Functions related to the handling of pathnames
" 
"------------------------------------------------------------------------
" Installation:	
" 	Drop this file into {rtp}/autoload/lh
" 	Requires Vim7+
" History:	
"	v 1.0.0 First Version
" 	(*) Functions moved from searchInRuntimeTime  
" 	v 2.0.1
" 	(*) lh#path#Simplify() becomes like |simplify()| except for trailing
" 	v 2.0.2
" 	(*) lh#path#SelectOne() 
" 	(*) lh#path#ToRelative() 
" 	v 2.0.3
" 	(*) lh#path#GlobAsList() 
" 	v 2.0.4
" 	(*) lh#path#StripStart()
" 	v 2.0.5
" 	(*) lh#path#StripStart() interprets '.' as getcwd()
" 	v 2.2.0
" 	(*) new functions: lh#path#common(), lh#path#to_dirname(),
" 	    lh#path#depth(), lh#path#relative_to(), lh#path#to_regex(),
" 	    lh#path#find()
" 	(*) lh#path#simplify() fixed
" 	(*) lh#path#to_relative() use simplify()
" TODO:
"       (*) Decide what #depth('../../bar') shall return
"       (*) Fix #simplify('../../bar')
" }}}1
"=============================================================================


"=============================================================================
" Avoid global reinclusion {{{1
let s:cpo_save=&cpo
set cpo&vim

"=============================================================================
" ## Functions {{{1
" # Debug {{{2
let s:verbose = 0
function! lh#path#verbose(...)
  if a:0 > 0 | let s:verbose = a:1 | endif
  return s:verbose
endfunction

function! s:Verbose(expr)
  if s:verbose
    echomsg a:expr
  endif
endfunction

function! lh#path#debug(expr)
  return eval(a:expr)
endfunction

"=============================================================================
" # Public {{{2
" Function: lh#path#simplify({pathname}) {{{3
" Like |simplify()|, but also strip the leading './'
" It seems unable to simplify '..\' when compiled without +shellslash
function! lh#path#simplify(pathname)
  let pathname = simplify(a:pathname)
  let pathname = substitute(pathname, '^\%(\.[/\\]\)\+', '', '')
  let pathname = substitute(pathname, '\([/\\]\)\%(\.[/\\]\)\+', '\1', 'g')
  let pwd = getcwd().'/'
  let pathname = substitute(pathname, '^'.lh#path#to_regex(pwd), '', 'g')
  return pathname
endfunction
function! lh#path#Simplify(pathname)
  return lh#path#simplify(a:pathname)
endfunction

" Function: lh#path#common({pathnames}) {{{3
" Find the common leading path between all pathnames
function! lh#path#common(pathnames)
  " assert(len(pathnames)) > 1
  let common = a:pathnames[0]
  let i = 1
  while i < len(a:pathnames)
    let fcrt = a:pathnames[i]
    " pathnames should not contain @
    let common = matchstr(common.'@@'.fcrt, '^\zs\(.*[/\\]\)\ze.\{-}@@\1.*$')
    if strlen(common) == 0
      " No need to further checks
      break
    endif
    let i += 1
  endwhile
  return common
endfunction

" Function: lh#path#strip_common({pathnames}) {{{3
" Find the common leading path between all pathnames, and strip it
function! lh#path#strip_common(pathnames)
  " assert(len(pathnames)) > 1
  let common = lh#path#common(a:pathnames)
  let l = strlen(common)
  if l == 0
    return a:pathnames
  else
  let pathnames = a:pathnames
  call map(pathnames, 'strpart(v:val, '.l.')' )
  return pathnames
  endif
endfunction
function! lh#path#StripCommon(pathnames)
  return lh#path#strip_common(a:pathnames)
endfunction

" Function: lh#path#is_absolute_path({path}) {{{3
function! lh#path#is_absolute_path(path)
  return a:path =~ '^/'
	\ . '\|^[a-zA-Z]:[/\\]'
	\ . '\|^[/\\]\{2}'
  "    Unix absolute path 
  " or Windows absolute path
  " or UNC path
endfunction
function! lh#path#IsAbsolutePath(path)
  return lh#path#is_absolute_path(a:path)
endfunction

" Function: lh#path#is_url({path}) {{{3
function! lh#path#is_url(path)
  " todo: support UNC paths and other urls
  return a:path =~ '^\%(https\=\|s\=ftp\|dav\|fetch\|file\|rcp\|rsynch\|scp\)://'
endfunction
function! lh#path#IsURL(path)
  return lh#path#is_url(a:path)
endfunction

" Function: lh#path#select_one({pathnames},{prompt}) {{{3
function! lh#path#select_one(pathnames, prompt)
  if len(a:pathnames) > 1
    let simpl_pathnames = deepcopy(a:pathnames) 
    let simpl_pathnames = lh#path#strip_common(simpl_pathnames)
    let simpl_pathnames = [ '&Cancel' ] + simpl_pathnames
    " Consider guioptions+=c is case of difficulties with the gui
    let selection = confirm(a:prompt, join(simpl_pathnames,"\n"), 1, 'Question')
    let file = (selection == 1) ? '' : a:pathnames[selection-2]
    return file
  elseif len(a:pathnames) == 0
    return ''
  else
    return a:pathnames[0]
  endif
endfunction
function! lh#path#SelectOne(pathnames, prompt)
  return lh#path#select_one(a:pathnames, a:prompt)
endfunction

" Function: lh#path#to_relative({pathname}) {{{3
function! lh#path#to_relative(pathname)
  let newpath = fnamemodify(a:pathname, ':p:.')
  let newpath = simplify(newpath)
  return newpath
endfunction
function! lh#path#ToRelative(pathname)
  return lh#path#to_relative(a:pathname)
endfunction

" Function: lh#path#to_dirname({dirname}) {{{3
" todo: use &shellslash
function! lh#path#to_dirname(dirname)
  let dirname = a:dirname . (a:dirname[-1:] =~ '[/\\]' ? '' : '/')
  return dirname
endfunction

" Function: lh#path#depth({dirname}) {{{3
" todo: make a choice about "negative" paths like "../../foo"
function! lh#path#depth(dirname)
  if empty(a:dirname) | return 0 | endif
  let dirname = lh#path#to_dirname(a:dirname)
  let dirname = lh#path#simplify(dirname)
  if lh#path#is_absolute_path(dirname)
    let dirname = matchstr(dirname, '.\{-}[/\\]\zs.*')
  endif
  let depth = len(substitute(dirname, '[^/\\]\+[/\\]', '§', 'g'))
  return depth
endfunction

" Function: lh#path#relative_to({from}, {to}) {{{3
" @param two directories
" @return a directories delta that ends with a '/' (may depends on
" &shellslash)
function! lh#path#relative_to(from, to)
  " let from = fnamemodify(a:from, ':p')
  " let to   = fnamemodify(a:to  , ':p')
  let from = lh#path#to_dirname(a:from)
  let to   = lh#path#to_dirname(a:to  )
  let [from, to] = lh#path#strip_common([from, to])
  let nb_up =  lh#path#depth(from)
  return repeat('../', nb_up).to

  " cannot rely on :cd (as it alters things, and doesn't work with
  " non-existant paths)
  let pwd = getcwd()
  exe 'cd '.a:to
  let res = lh#path#to_relative(a:from)
  exe 'cd '.pwd
  return res
endfunction

" Function: lh#path#glob_as_list({pathslist}, {expr}) {{{3
function! s:GlobAsList(pathslist, expr)
  let sResult = globpath(a:pathslist, a:expr)
  let lResult = split(sResult, '\n')
  " workaround a non feature of wildignore: it does not ignore directories
  for ignored_pattern in split(&wildignore,',')
    if stridx(ignored_pattern,'/') != -1
      call filter(lResult, 'v:val !~ '.string(ignored_pattern))
    endif
  endfor
  return lResult
endfunction

function! lh#path#glob_as_list(pathslist, expr)
  if type(a:expr) == type('string')
    return s:GlobAsList(a:pathslist, a:expr)
  elseif type(a:expr) == type([])
    let res = []
    for expr in a:expr
      call extend(res, s:GlobAsList(a:pathslist, expr))
    endfor
    return res
  else
    throw "Unexpected type for a:expression"
  endif
endfunction
function! lh#path#GlobAsList(pathslist, expr)
  return lh#path#glob_as_list(a:pathslist, a:expr)
endfunction

" Function: lh#path#strip_start({pathname}, {pathslist}) {{{3
" Strip occurrence of paths from {pathslist} in {pathname}
" @param[in] {pathname} name to simplify
" @param[in] {pathslist} list of pathname (can be a |string| of pathnames
" separated by ",", of a |List|).
function! lh#path#strip_start(pathname, pathslist)
  if type(a:pathslist) == type('string')
    " let strip_re = escape(a:pathslist, '\\.')
    " let strip_re = '^' . substitute(strip_re, ',', '\\|^', 'g')
    let pathslist = split(a:pathslist, ',')
  elseif type(a:pathslist) == type([])
    let pathslist = deepcopy(a:pathslist)
  else
    throw "Unexpected type for a:pathname"
  endif

  " apply a realpath like operation
  let nb_paths = len(pathslist) " set before the loop
  let i = 0
  while i != nb_paths
    if pathslist[i] =~ '^\.\%(/\|$\)'
      let path2 = getcwd().pathslist[i][1:]
      call add(pathslist, path2)
    endif
    let i += 1
  endwhile
  " replace path separators by a regex that can match them
  call map(pathslist, 'substitute(v:val, "[\\\\/]", "[\\\\/]", "g")')
  " echomsg string(pathslist)
  " escape .
  call map(pathslist, '"^".escape(v:val, ".")')
  " build the strip regex
  let strip_re = join(pathslist, '\|')
  " echomsg strip_re
  let res = substitute(a:pathname, '\%('.strip_re.'\)[/\\]\=', '', '')
  return res
endfunction
function! lh#path#StripStart(pathname, pathslist)
  return lh#path#strip_start(a:pathname, a:pathslist)
endfunction

" Function: lh#path#to_regex({pathname}) {{{3
function! lh#path#to_regex(path)
  let regex = substitute(a:path, '[/\\]', '[/\\\\]', 'g')
  return regex
endfunction

" Function: lh#path#find({pathname}, {regex}) {{{3
function! lh#path#find(paths, regex)
  let paths = (type(a:paths) == type([]))
	\ ? (a:paths) 
	\ : split(a:paths,',')
  for path in paths
    if match(path ,a:regex) != -1
      return path
    endif
  endfor
  return ''
endfunction
"=============================================================================
let &cpo=s:cpo_save
"=============================================================================
" vim600: set fdm=marker: