My dotfiles. Period.

path.vim 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. "=============================================================================
  2. " $Id: path.vim 606 2012-05-31 17:09:46Z luc.hermitte@gmail.com $
  3. " File: autoload/lh/path.vim {{{1
  4. " Author: Luc Hermitte <EMAIL:hermitte {at} free {dot} fr>
  5. " <URL:http://code.google.com/p/lh-vim/>
  6. " License: GPLv3 with exceptions
  7. " <URL:http://code.google.com/p/lh-vim/wiki/License>
  8. " Version: 3.1.1
  9. " Created: 23rd Jan 2007
  10. " Last Update: $Date
  11. "------------------------------------------------------------------------
  12. " Description:
  13. " Functions related to the handling of pathnames
  14. "
  15. "------------------------------------------------------------------------
  16. " Installation:
  17. " Drop this file into {rtp}/autoload/lh
  18. " Requires Vim7+
  19. " History:
  20. " v 1.0.0 First Version
  21. " (*) Functions moved from searchInRuntimeTime
  22. " v 2.0.1
  23. " (*) lh#path#Simplify() becomes like |simplify()| except for trailing
  24. " v 2.0.2
  25. " (*) lh#path#SelectOne()
  26. " (*) lh#path#ToRelative()
  27. " v 2.0.3
  28. " (*) lh#path#GlobAsList()
  29. " v 2.0.4
  30. " (*) lh#path#StripStart()
  31. " v 2.0.5
  32. " (*) lh#path#StripStart() interprets '.' as getcwd()
  33. " v 2.2.0
  34. " (*) new functions: lh#path#common(), lh#path#to_dirname(),
  35. " lh#path#depth(), lh#path#relative_to(), lh#path#to_regex(),
  36. " lh#path#find()
  37. " (*) lh#path#simplify() fixed
  38. " (*) lh#path#to_relative() use simplify()
  39. " v 2.2.2
  40. " (*) lh#path#strip_common() fixed
  41. " (*) lh#path#simplify() new optional parameter: make_relative_to_pwd
  42. " v 2.2.5
  43. " (*) fix lh#path#to_dirname('') -> return ''
  44. " v 2.2.6
  45. " (*) fix lh#path#glob_as_list() does not return the same path several
  46. " times
  47. " v 2.2.7
  48. " (*) fix lh#path#strip_start() to strip as much as possible.
  49. " (*) lh#path#glob_as_list() changed to handle **
  50. " v 3.0.0
  51. " (*) GPLv3
  52. " v 3.1.0
  53. " (*) lh#path#glob_as_list accepts a new option: mustSort which value
  54. " true by default.
  55. " v 3.1.1
  56. " (*) lh#path#strip_start() shall support very big lists of dirnames now.
  57. " TODO:
  58. " (*) Decide what #depth('../../bar') shall return
  59. " (*) Fix #simplify('../../bar')
  60. " }}}1
  61. "=============================================================================
  62. "=============================================================================
  63. " Avoid global reinclusion {{{1
  64. let s:cpo_save=&cpo
  65. set cpo&vim
  66. "=============================================================================
  67. " ## Functions {{{1
  68. " # Version {{{2
  69. let s:k_version = 310
  70. function! lh#path#version()
  71. return s:k_version
  72. endfunction
  73. " # Debug {{{2
  74. let s:verbose = 0
  75. function! lh#path#verbose(...)
  76. if a:0 > 0 | let s:verbose = a:1 | endif
  77. return s:verbose
  78. endfunction
  79. function! s:Verbose(expr)
  80. if s:verbose
  81. echomsg a:expr
  82. endif
  83. endfunction
  84. function! lh#path#debug(expr)
  85. return eval(a:expr)
  86. endfunction
  87. "=============================================================================
  88. " # Public {{{2
  89. " Function: lh#path#simplify({pathname}, [make_relative_to_pwd=true]) {{{3
  90. " Like |simplify()|, but also strip the leading './'
  91. " It seems unable to simplify '..\' when compiled without +shellslash
  92. function! lh#path#simplify(pathname, ...)
  93. let make_relative_to_pwd = a:0 == 0 || a:1 == 1
  94. let pathname = simplify(a:pathname)
  95. let pathname = substitute(pathname, '^\%(\.[/\\]\)\+', '', '')
  96. let pathname = substitute(pathname, '\([/\\]\)\%(\.[/\\]\)\+', '\1', 'g')
  97. if make_relative_to_pwd
  98. let pwd = getcwd().'/'
  99. let pathname = substitute(pathname, '^'.lh#path#to_regex(pwd), '', 'g')
  100. endif
  101. return pathname
  102. endfunction
  103. function! lh#path#Simplify(pathname)
  104. return lh#path#simplify(a:pathname)
  105. endfunction
  106. " Function: lh#path#common({pathnames}) {{{3
  107. " Find the common leading path between all pathnames
  108. function! lh#path#common(pathnames)
  109. " assert(len(pathnames)) > 1
  110. let common = a:pathnames[0]
  111. let i = 1
  112. while i < len(a:pathnames)
  113. let fcrt = a:pathnames[i]
  114. " pathnames should not contain @
  115. " let common = matchstr(common.'@@'.fcrt, '^\zs\(.*[/\\]\)\ze.\{-}@@\1.*$')
  116. let common = matchstr(common.'@@'.fcrt, '^\zs\(.*\>\)\ze.\{-}@@\1\>.*$')
  117. if strlen(common) == 0
  118. " No need to further checks
  119. break
  120. endif
  121. let i += 1
  122. endwhile
  123. return common
  124. endfunction
  125. " Function: lh#path#strip_common({pathnames}) {{{3
  126. " Find the common leading path between all pathnames, and strip it
  127. function! lh#path#strip_common(pathnames)
  128. " assert(len(pathnames)) > 1
  129. let common = lh#path#common(a:pathnames)
  130. let common = lh#path#to_dirname(common)
  131. let l = strlen(common)
  132. if l == 0
  133. return a:pathnames
  134. else
  135. let pathnames = a:pathnames
  136. call map(pathnames, 'strpart(v:val, '.l.')' )
  137. return pathnames
  138. endif
  139. endfunction
  140. function! lh#path#StripCommon(pathnames)
  141. return lh#path#strip_common(a:pathnames)
  142. endfunction
  143. " Function: lh#path#is_absolute_path({path}) {{{3
  144. function! lh#path#is_absolute_path(path)
  145. return a:path =~ '^/'
  146. \ . '\|^[a-zA-Z]:[/\\]'
  147. \ . '\|^[/\\]\{2}'
  148. " Unix absolute path
  149. " or Windows absolute path
  150. " or UNC path
  151. endfunction
  152. function! lh#path#IsAbsolutePath(path)
  153. return lh#path#is_absolute_path(a:path)
  154. endfunction
  155. " Function: lh#path#is_url({path}) {{{3
  156. function! lh#path#is_url(path)
  157. " todo: support UNC paths and other urls
  158. return a:path =~ '^\%(https\=\|s\=ftp\|dav\|fetch\|file\|rcp\|rsynch\|scp\)://'
  159. endfunction
  160. function! lh#path#IsURL(path)
  161. return lh#path#is_url(a:path)
  162. endfunction
  163. " Function: lh#path#select_one({pathnames},{prompt}) {{{3
  164. function! lh#path#select_one(pathnames, prompt)
  165. if len(a:pathnames) > 1
  166. let simpl_pathnames = deepcopy(a:pathnames)
  167. let simpl_pathnames = lh#path#strip_common(simpl_pathnames)
  168. let simpl_pathnames = [ '&Cancel' ] + simpl_pathnames
  169. " Consider guioptions+=c is case of difficulties with the gui
  170. let selection = confirm(a:prompt, join(simpl_pathnames,"\n"), 1, 'Question')
  171. let file = (selection == 1) ? '' : a:pathnames[selection-2]
  172. return file
  173. elseif len(a:pathnames) == 0
  174. return ''
  175. else
  176. return a:pathnames[0]
  177. endif
  178. endfunction
  179. function! lh#path#SelectOne(pathnames, prompt)
  180. return lh#path#select_one(a:pathnames, a:prompt)
  181. endfunction
  182. " Function: lh#path#to_relative({pathname}) {{{3
  183. function! lh#path#to_relative(pathname)
  184. let newpath = fnamemodify(a:pathname, ':p:.')
  185. let newpath = simplify(newpath)
  186. return newpath
  187. endfunction
  188. function! lh#path#ToRelative(pathname)
  189. return lh#path#to_relative(a:pathname)
  190. endfunction
  191. " Function: lh#path#to_dirname({dirname}) {{{3
  192. " todo: use &shellslash
  193. function! lh#path#to_dirname(dirname)
  194. let dirname = a:dirname . (empty(a:dirname) || a:dirname[-1:] =~ '[/\\]' ? '' : '/')
  195. return dirname
  196. endfunction
  197. " Function: lh#path#depth({dirname}) {{{3
  198. " todo: make a choice about "negative" paths like "../../foo"
  199. function! lh#path#depth(dirname)
  200. if empty(a:dirname) | return 0 | endif
  201. let dirname = lh#path#to_dirname(a:dirname)
  202. let dirname = lh#path#simplify(dirname)
  203. if lh#path#is_absolute_path(dirname)
  204. let dirname = matchstr(dirname, '.\{-}[/\\]\zs.*')
  205. endif
  206. let depth = len(substitute(dirname, '[^/\\]\+[/\\]', '#', 'g'))
  207. return depth
  208. endfunction
  209. " Function: lh#path#relative_to({from}, {to}) {{{3
  210. " @param two directories
  211. " @return a directories delta that ends with a '/' (may depends on
  212. " &shellslash)
  213. function! lh#path#relative_to(from, to)
  214. " let from = fnamemodify(a:from, ':p')
  215. " let to = fnamemodify(a:to , ':p')
  216. let from = lh#path#to_dirname(a:from)
  217. let to = lh#path#to_dirname(a:to )
  218. let [from, to] = lh#path#strip_common([from, to])
  219. let nb_up = lh#path#depth(from)
  220. return repeat('../', nb_up).to
  221. " cannot rely on :cd (as it alters things, and doesn't work with
  222. " non-existant paths)
  223. let pwd = getcwd()
  224. exe 'cd '.a:to
  225. let res = lh#path#to_relative(a:from)
  226. exe 'cd '.pwd
  227. return res
  228. endfunction
  229. " Function: lh#path#glob_as_list({pathslist}, {expr} [, mustSort=1]) {{{3
  230. function! s:GlobAsList(pathslist, expr, mustSort)
  231. let pathslist = type(a:pathslist) == type([]) ? join(a:pathslist, ',') : a:pathslist
  232. let sResult = globpath(pathslist, a:expr)
  233. let lResult = split(sResult, '\n')
  234. " workaround a non feature of wildignore: it does not ignore directories
  235. for ignored_pattern in split(&wildignore,',')
  236. if stridx(ignored_pattern,'/') != -1
  237. call filter(lResult, 'v:val !~ '.string(ignored_pattern))
  238. endif
  239. endfor
  240. return a:mustSort ? lh#list#unique_sort(lResult) : lResult
  241. endfunction
  242. function! lh#path#glob_as_list(pathslist, expr, ...)
  243. let mustSort = (a:0 > 0) ? (a:1) : 0
  244. if type(a:expr) == type('string')
  245. return s:GlobAsList(a:pathslist, a:expr, mustSort)
  246. elseif type(a:expr) == type([])
  247. let res = []
  248. for expr in a:expr
  249. call extend(res, s:GlobAsList(a:pathslist, expr, mustSort))
  250. endfor
  251. return res
  252. else
  253. throw "Unexpected type for a:expression"
  254. endif
  255. endfunction
  256. function! lh#path#GlobAsList(pathslist, expr)
  257. return lh#path#glob_as_list(a:pathslist, a:expr)
  258. endfunction
  259. " Function: lh#path#strip_start({pathname}, {pathslist}) {{{3
  260. " Strip occurrence of paths from {pathslist} in {pathname}
  261. " @param[in] {pathname} name to simplify
  262. " @param[in] {pathslist} list of pathname (can be a |string| of pathnames
  263. " separated by ",", of a |List|).
  264. function! lh#path#strip_start(pathname, pathslist)
  265. if type(a:pathslist) == type('string')
  266. " let strip_re = escape(a:pathslist, '\\.')
  267. " let strip_re = '^' . substitute(strip_re, ',', '\\|^', 'g')
  268. let pathslist = split(a:pathslist, ',')
  269. elseif type(a:pathslist) == type([])
  270. let pathslist = deepcopy(a:pathslist)
  271. else
  272. throw "Unexpected type for a:pathname"
  273. endif
  274. " apply a realpath like operation
  275. let nb_paths = len(pathslist) " set before the loop
  276. let i = 0
  277. while i != nb_paths
  278. if pathslist[i] =~ '^\.\%(/\|$\)'
  279. let path2 = getcwd().pathslist[i][1:]
  280. call add(pathslist, path2)
  281. endif
  282. let i += 1
  283. endwhile
  284. " replace path separators by a regex that can match them
  285. call map(pathslist, 'substitute(v:val, "[\\\\/]", "[\\\\/]", "g")')
  286. " echomsg string(pathslist)
  287. " escape .
  288. call map(pathslist, '"^".escape(v:val, ".")')
  289. " handle "**" as anything
  290. call map(pathslist, 'substitute(v:val, "\\*\\*", "\\\\%([^\\\\/]*[\\\\/]\\\\)*", "g")')
  291. " reverse the list to use the real best match, which is "after"
  292. call reverse(pathslist)
  293. if 0
  294. " build the strip regex
  295. let strip_re = join(pathslist, '\|')
  296. " echomsg strip_re
  297. let best_match = substitute(a:pathname, '\%('.strip_re.'\)[/\\]\=', '', '')
  298. else
  299. let best_match = ''
  300. for path in pathslist
  301. let a_match = substitute(a:pathname, '\%('.path.'\)[/\\]\=', '', '')
  302. if len(a_match) < len(best_match) || empty(best_match)
  303. let best_match = a_match
  304. endif
  305. endfor
  306. endif
  307. return best_match
  308. endfunction
  309. function! lh#path#StripStart(pathname, pathslist)
  310. return lh#path#strip_start(a:pathname, a:pathslist)
  311. endfunction
  312. " Function: lh#path#to_regex({pathname}) {{{3
  313. function! lh#path#to_regex(path)
  314. let regex = substitute(a:path, '[/\\]', '[/\\\\]', 'g')
  315. return regex
  316. endfunction
  317. " Function: lh#path#find({pathname}, {regex}) {{{3
  318. function! lh#path#find(paths, regex)
  319. let paths = (type(a:paths) == type([]))
  320. \ ? (a:paths)
  321. \ : split(a:paths,',')
  322. for path in paths
  323. if match(path ,a:regex) != -1
  324. return path
  325. endif
  326. endfor
  327. return ''
  328. endfunction
  329. " Function: lh#path#vimfiles() {{{3
  330. function! lh#path#vimfiles()
  331. let expected_win = $HOME . '/vimfiles'
  332. let expected_nix = $HOME . '/.vim'
  333. let what = lh#path#to_regex($HOME.'/').'\(vimfiles\|.vim\)'
  334. " Comment what
  335. let z = lh#path#find(&rtp,what)
  336. return z
  337. endfunction
  338. " }}}1
  339. "=============================================================================
  340. let &cpo=s:cpo_save
  341. "=============================================================================
  342. " vim600: set fdm=marker: