@@ -32,12 +32,21 @@ func! s:suf() abort
3232 return type (m ) == type (0 ) && m <= 1 ? 1 : 0
3333endf
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 )
3846endf
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
5662endf
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 )
6167endf
6268
6369if v: version > 704 || v: version == 704 && has (' patch279' )
7177endif
7278
7379func ! 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 (pat), ' /' , 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
425435endf
426436
437+ let s: recursive = ' '
427438func ! 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))
481503endf
482504
483505func ! 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