Skip to content

Commit 2fc4551

Browse files
committed
the big short
Problem: No reason for files (as opposed to directories) to be opened with full (absolute) paths. And this is annoying because the absolute form "sticks" in the buffer name. Solution: - When opening a file from Dirvish's listing, use the relative form. - Directories are still named with the full path (for %:h, etc).
1 parent b5d8d23 commit 2fc4551

File tree

1 file changed

+51
-31
lines changed

1 file changed

+51
-31
lines changed

autoload/dirvish.vim

Lines changed: 51 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,21 @@ func! s:suf() abort
3232
return type(m) == type(0) && m <= 1 ? 1 : 0
3333
endf
3434

35-
" Normalize slashes for safe use of fnameescape(), isdirectory(). Vim bug #541.
36-
func! s:sl(path) abort
37-
return has('win32') ? tr(a:path, '\', '/') : a:path
35+
" Normalizes slashes:
36+
" - Replace "\" with "/", for safe use of fnameescape(), isdirectory(). Vim bug #541.
37+
" - Collapse slashes (except UNC-style \\foo\bar).
38+
" - Always end dir with "/".
39+
" - Special case: empty string (CWD) => "./".
40+
func! s:sl(f) abort
41+
let f = has('win32') ? tr(a:f, '\', '/') : a:f
42+
" Collapse slashes (except UNC-style \\foo\bar).
43+
let f = f[0] . substitute(f[1:], '/\+', '/', 'g')
44+
" End with separator.
45+
return empty(f) ? './' : (isdirectory(f) && (f[-1:] !=# '/') ? f.'/' : f)
3846
endf
3947

40-
func! s:normalize_dir(dir, silent) abort
48+
" Workaround for platform quirks, and shows an error if dir is invalid.
49+
func! s:fix_dir(dir, silent) abort
4150
let dir = s:sl(a:dir)
4251
if !isdirectory(dir)
4352
" Fallback for cygwin/MSYS paths lacking a drive letter.
@@ -49,15 +58,12 @@ func! s:normalize_dir(dir, silent) abort
4958
return ''
5059
endif
5160
endif
52-
" Collapse slashes (except UNC-style \\foo\bar).
53-
let dir = dir[0] . substitute(dir[1:], '/\+', '/', 'g')
54-
" Always end with separator.
55-
return (dir[-1:] ==# '/') ? dir : dir.'/'
61+
return dir
5662
endf
5763

58-
func! s:parent_dir(dir) abort
59-
let mod = isdirectory(s:sl(a:dir)) ? ':p:h:h' : ':p:h'
60-
return s:normalize_dir(fnamemodify(a:dir, mod), 0)
64+
func! s:parent_dir(f) abort
65+
let f_noslash = substitute(a:f, escape(s:sep == '\'?'[/\]':'/','\').'\+$', '', 'g')
66+
return s:fix_dir(fnamemodify(f_noslash, ':h'), 0)
6167
endf
6268

6369
if v:version > 704 || v:version == 704 && has('patch279')
@@ -71,14 +77,15 @@ endf
7177
endif
7278

7379
func! s:list_dir(dir) abort
80+
let s:rel = get(g:, 'dirvish_relative_paths', 0)
7481
" Escape for globpath().
7582
let dir_esc = escape(substitute(a:dir,'\[','[[]','g'), ',;*?{}^$\')
7683
let paths = s:globlist(dir_esc, '*')
7784
"Append dot-prefixed files. globpath() cannot do both in 1 pass.
7885
let paths = paths + s:globlist(dir_esc, '.[^.]*')
7986

80-
if s:rel && !s:eq(a:dir, s:parent_dir(getcwd()))
81-
return map(paths, "fnamemodify(v:val, ':p:.')") " Avoid blank CWD.
87+
if s:rel && !s:eq(a:dir, s:parent_dir(s:sl(getcwd()))) " Avoid blank CWD.
88+
return map(paths, "fnamemodify(v:val, ':p:.')")
8289
else
8390
return map(paths, "fnamemodify(v:val, ':p')")
8491
endif
@@ -265,20 +272,22 @@ func! s:open_selected(splitcmd, bg, line1, line2) abort
265272

266273
let paths = getline(a:line1, a:line2)
267274
for path in paths
268-
let path = s:sl(path)
275+
let isdir = path[-1:] == s:sep
269276
if !isdirectory(path) && !filereadable(path)
270-
call s:msg_error("invalid (access denied?): ".path)
277+
call s:msg_error(printf('invalid (access denied?): %s', path))
271278
continue
272279
endif
280+
" Open files (not dirs) using relative paths.
281+
let shortname = fnamemodify(path, isdir ? ':p:~' : ':~:.')
273282

274283
if p " Go to previous window.
275284
exe (winnr('$') > 1 ? 'wincmd p|if winnr()=='.winnr().'|wincmd w|endif' : 'vsplit')
276285
endif
277286

278-
if isdirectory(path)
279-
exe (p || a:splitcmd ==# 'edit' ? '' : a:splitcmd.'|') 'Dirvish' fnameescape(path)
287+
if isdir
288+
exe (p || a:splitcmd ==# 'edit' ? '' : a:splitcmd.'|') 'Dirvish' fnameescape(shortname)
280289
else
281-
exe (p ? 'edit' : a:splitcmd) fnameescape(path)
290+
exe (p ? 'edit' : a:splitcmd) fnameescape(shortname)
282291
endif
283292

284293
" Return to previous window after _each_ split, else we get lost.
@@ -367,8 +376,8 @@ func! s:buf_render(dir, lastpath) abort
367376
let bnr = bufnr('%')
368377
let isnew = empty(getline(1))
369378

370-
if !isdirectory(s:sl(bufname('%')))
371-
echoerr 'dirvish: fatal: buffer name is not a directory:' bufname('%')
379+
if !isdirectory(a:dir)
380+
echoerr 'dirvish: not a directory:' a:dir
372381
return
373382
endif
374383

@@ -395,6 +404,7 @@ func! s:buf_render(dir, lastpath) abort
395404
if !empty(a:lastpath)
396405
let pat = s:rel ? fnamemodify(a:lastpath, ':p:.') : a:lastpath
397406
let pat = empty(pat) ? a:lastpath : pat " no longer in CWD
407+
let pat = tr(s:sl(path), '/', s:sep) " platform slashes
398408
call search('\V\^'.escape(pat, '\').'\$', 'cw')
399409
endif
400410
" Place cursor on the tail (last path segment).
@@ -424,7 +434,13 @@ func! s:apply_icons() abort
424434
endfor
425435
endf
426436

437+
let s:recursive = ''
427438
func! s:open_dir(d, reload) abort
439+
if s:recursive ==# a:d._dir
440+
return
441+
endif
442+
let s:recursive = a:d._dir
443+
call s:log(printf('open_dir ENTER: %d %s', bufnr('%'), a:d._dir))
428444
let d = a:d
429445
let dirname_without_sep = substitute(d._dir, '[\\/]\+$', '', 'g')
430446

@@ -441,17 +457,21 @@ func! s:open_dir(d, reload) abort
441457
endif
442458
endfor
443459

460+
" Note: :noautocmd not used here, to allow BufEnter/BufNew. 61282f2453af
461+
" Thus s:recursive guards against recursion (for performance).
444462
if -1 == bnr
445463
execute 'silent' s:noswapfile 'keepalt edit' fnameescape(d._dir)
446464
else
447465
execute 'silent' s:noswapfile 'buffer' bnr
448466
endif
449467

450-
" Use :file to force a normalized path.
468+
" Force a normalized directory path.
469+
" - Starts with "~/" or "/", ie absolute (important for ":h").
470+
" - Ends with "/".
451471
" - Avoids ".././..", ".", "./", etc. (breaks %:p, not updated on :cd).
452472
" - Avoids [Scratch] in some cases (":e ~/" on Windows).
453-
if !s:rel && s:sl(bufname('%')) !=# d._dir
454-
execute 'silent '.s:noswapfile.' file ' . fnameescape(d._dir)
473+
if bufname('%')[-1:] != '/' || bufname('%')[0:1] !=# d._dir[0:1]
474+
execute 'silent' s:noswapfile 'file' fnameescape(d._dir)
455475
endif
456476

457477
if !isdirectory(bufname('%')) " sanity check
@@ -478,6 +498,8 @@ func! s:open_dir(d, reload) abort
478498
let b:dirvish._c = b:changedtick
479499
call s:apply_icons()
480500
endif
501+
let s:recursive = ''
502+
call s:log(printf('open_dir EXIT : %d %s', bufnr('%'), a:d._dir))
481503
endf
482504

483505
func! s:should_reload() abort
@@ -504,22 +526,20 @@ func! dirvish#open(...) range abort
504526
return
505527
endif
506528

507-
let s:rel = get(g:, 'dirvish_relative_paths', 0)
508529
let d = {}
509530
let is_uri = -1 != match(a:1, '^\w\+:[\/][\/]')
510-
let from_path = fnamemodify(bufname('%'), ':p')
511-
let to_path = fnamemodify(s:sl(a:1), ':p')
531+
let from_path = s:sl(fnamemodify(bufname('%'), ':p'))
532+
let to_path = s:sl(fnamemodify(a:1, ':p'))
512533
" ^resolves to CWD if a:1 is empty
513534

514-
let d._dir = filereadable(to_path) ? fnamemodify(to_path, ':p:h') : to_path
515-
let d._dir = s:normalize_dir(d._dir, is_uri)
535+
let d._dir = s:fix_dir(filereadable(to_path) ? fnamemodify(to_path, ':p:h') : to_path, is_uri)
516536
" Fallback to CWD for URIs. #127
517-
let d._dir = empty(d._dir) && is_uri ? s:normalize_dir(getcwd(), is_uri) : d._dir
518-
if empty(d._dir) " s:normalize_dir() already showed error.
537+
let d._dir = empty(d._dir) && is_uri ? s:fix_dir(getcwd(), is_uri) : d._dir
538+
if empty(d._dir) " s:fix_dir() already showed error.
519539
return
520540
endif
521541

522-
let reloading = exists('b:dirvish') && d._dir ==# b:dirvish._dir
542+
let reloading = exists('b:dirvish') && d._dir ==# b:dirvish._dir && s:recursive !=# d._dir
523543

524544
if reloading
525545
let d.lastpath = '' " Do not place cursor when reloading.

0 commit comments

Comments
 (0)