@@ -32,12 +32,21 @@ func! s:suf() abort
32
32
return type (m ) == type (0 ) && m <= 1 ? 1 : 0
33
33
endf
34
34
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 )
38
46
endf
39
47
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
41
50
let dir = s: sl (a: dir )
42
51
if ! isdirectory (dir )
43
52
" Fallback for cygwin/MSYS paths lacking a drive letter.
@@ -49,15 +58,12 @@ func! s:normalize_dir(dir, silent) abort
49
58
return ' '
50
59
endif
51
60
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
56
62
endf
57
63
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 )
61
67
endf
62
68
63
69
if v: version > 704 || v: version == 704 && has (' patch279' )
71
77
endif
72
78
73
79
func ! s: list_dir (dir ) abort
80
+ let s: rel = get (g: , ' dirvish_relative_paths' , 0 )
74
81
" Escape for globpath().
75
82
let dir_esc = escape (substitute (a: dir ,' \[' ,' [[]' ,' g' ), ' ,;*?{}^$\' )
76
83
let paths = s: globlist (dir_esc, ' *' )
77
84
" Append dot-prefixed files. globpath() cannot do both in 1 pass.
78
85
let paths = paths + s: globlist (dir_esc, ' .[^.]*' )
79
86
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:.')" )
82
89
else
83
90
return map (paths, " fnamemodify(v:val, ':p')" )
84
91
endif
@@ -265,20 +272,22 @@ func! s:open_selected(splitcmd, bg, line1, line2) abort
265
272
266
273
let paths = getline (a: line1 , a: line2 )
267
274
for path in paths
268
- let path = s: sl ( path )
275
+ let isdir = path [ -1 :] == s: sep
269
276
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 ) )
271
278
continue
272
279
endif
280
+ " Open files (not dirs) using relative paths.
281
+ let shortname = fnamemodify (path , isdir ? ' :p:~' : ' :~:.' )
273
282
274
283
if p " Go to previous window.
275
284
exe (winnr (' $' ) > 1 ? ' wincmd p|if winnr()==' .winnr ().' |wincmd w|endif' : ' vsplit' )
276
285
endif
277
286
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 )
280
289
else
281
- exe (p ? ' edit' : a: splitcmd ) fnameescape (path )
290
+ exe (p ? ' edit' : a: splitcmd ) fnameescape (shortname )
282
291
endif
283
292
284
293
" Return to previous window after _each_ split, else we get lost.
@@ -367,8 +376,8 @@ func! s:buf_render(dir, lastpath) abort
367
376
let bnr = bufnr (' %' )
368
377
let isnew = empty (getline (1 ))
369
378
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
372
381
return
373
382
endif
374
383
@@ -395,6 +404,7 @@ func! s:buf_render(dir, lastpath) abort
395
404
if ! empty (a: lastpath )
396
405
let pat = s: rel ? fnamemodify (a: lastpath , ' :p:.' ) : a: lastpath
397
406
let pat = empty (pat) ? a: lastpath : pat " no longer in CWD
407
+ let pat = tr (s: sl (path ), ' /' , s: sep ) " platform slashes
398
408
call search (' \V\^' .escape (pat, ' \' ).' \$' , ' cw' )
399
409
endif
400
410
" Place cursor on the tail (last path segment).
@@ -424,7 +434,13 @@ func! s:apply_icons() abort
424
434
endfor
425
435
endf
426
436
437
+ let s: recursive = ' '
427
438
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))
428
444
let d = a: d
429
445
let dirname_without_sep = substitute (d ._dir, ' [\\/]\+$' , ' ' , ' g' )
430
446
@@ -441,17 +457,21 @@ func! s:open_dir(d, reload) abort
441
457
endif
442
458
endfor
443
459
460
+ " Note: :noautocmd not used here, to allow BufEnter/BufNew. 61282f2453af
461
+ " Thus s:recursive guards against recursion (for performance).
444
462
if -1 == bnr
445
463
execute ' silent' s: noswapfile ' keepalt edit' fnameescape (d ._dir)
446
464
else
447
465
execute ' silent' s: noswapfile ' buffer' bnr
448
466
endif
449
467
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 "/".
451
471
" - Avoids ".././..", ".", "./", etc. (breaks %:p, not updated on :cd).
452
472
" - 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)
455
475
endif
456
476
457
477
if ! isdirectory (bufname (' %' )) " sanity check
@@ -478,6 +498,8 @@ func! s:open_dir(d, reload) abort
478
498
let b: dirvish ._c = b: changedtick
479
499
call s: apply_icons ()
480
500
endif
501
+ let s: recursive = ' '
502
+ call s: log (printf (' open_dir EXIT : %d %s' , bufnr (' %' ), a: d ._dir))
481
503
endf
482
504
483
505
func ! s: should_reload () abort
@@ -504,22 +526,20 @@ func! dirvish#open(...) range abort
504
526
return
505
527
endif
506
528
507
- let s: rel = get (g: , ' dirvish_relative_paths' , 0 )
508
529
let d = {}
509
530
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' ) )
512
533
" ^resolves to CWD if a:1 is empty
513
534
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)
516
536
" 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.
519
539
return
520
540
endif
521
541
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
523
543
524
544
if reloading
525
545
let d .lastpath = ' ' " Do not place cursor when reloading.
0 commit comments