Skip to content

Commit

Permalink
the big short
Browse files Browse the repository at this point in the history
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).
  • Loading branch information
justinmk committed Jul 13, 2022
1 parent b5d8d23 commit 2fc4551
Showing 1 changed file with 51 additions and 31 deletions.
82 changes: 51 additions & 31 deletions autoload/dirvish.vim
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,21 @@ func! s:suf() abort
return type(m) == type(0) && m <= 1 ? 1 : 0
endf

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

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

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

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

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

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

let paths = getline(a:line1, a:line2)
for path in paths
let path = s:sl(path)
let isdir = path[-1:] == s:sep
if !isdirectory(path) && !filereadable(path)
call s:msg_error("invalid (access denied?): ".path)
call s:msg_error(printf('invalid (access denied?): %s', path))
continue
endif
" Open files (not dirs) using relative paths.
let shortname = fnamemodify(path, isdir ? ':p:~' : ':~:.')

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

if isdirectory(path)
exe (p || a:splitcmd ==# 'edit' ? '' : a:splitcmd.'|') 'Dirvish' fnameescape(path)
if isdir
exe (p || a:splitcmd ==# 'edit' ? '' : a:splitcmd.'|') 'Dirvish' fnameescape(shortname)
else
exe (p ? 'edit' : a:splitcmd) fnameescape(path)
exe (p ? 'edit' : a:splitcmd) fnameescape(shortname)
endif

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

if !isdirectory(s:sl(bufname('%')))
echoerr 'dirvish: fatal: buffer name is not a directory:' bufname('%')
if !isdirectory(a:dir)
echoerr 'dirvish: not a directory:' a:dir
return
endif

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

let s:recursive = ''
func! s:open_dir(d, reload) abort
if s:recursive ==# a:d._dir
return
endif
let s:recursive = a:d._dir
call s:log(printf('open_dir ENTER: %d %s', bufnr('%'), a:d._dir))
let d = a:d
let dirname_without_sep = substitute(d._dir, '[\\/]\+$', '', 'g')

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

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

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

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

func! s:should_reload() abort
Expand All @@ -504,22 +526,20 @@ func! dirvish#open(...) range abort
return
endif

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

let d._dir = filereadable(to_path) ? fnamemodify(to_path, ':p:h') : to_path
let d._dir = s:normalize_dir(d._dir, is_uri)
let d._dir = s:fix_dir(filereadable(to_path) ? fnamemodify(to_path, ':p:h') : to_path, is_uri)
" Fallback to CWD for URIs. #127
let d._dir = empty(d._dir) && is_uri ? s:normalize_dir(getcwd(), is_uri) : d._dir
if empty(d._dir) " s:normalize_dir() already showed error.
let d._dir = empty(d._dir) && is_uri ? s:fix_dir(getcwd(), is_uri) : d._dir
if empty(d._dir) " s:fix_dir() already showed error.
return
endif

let reloading = exists('b:dirvish') && d._dir ==# b:dirvish._dir
let reloading = exists('b:dirvish') && d._dir ==# b:dirvish._dir && s:recursive !=# d._dir

if reloading
let d.lastpath = '' " Do not place cursor when reloading.
Expand Down

0 comments on commit 2fc4551

Please sign in to comment.