vim-patch:9.1.1507: symlinks are resolved on :cd commands (#34758)

Problem:  File paths change from symlink to target path after :cd command
          when editing files through symbolic links
Solution: Add "~" flag to 'cpoptions' to control symlink resolution.
          When not included (default), symlinks are resolved maintaining
          backward compatibility. When included, symlinks are preserved
          providing the improved behavior. (glepnir)

related: neovim/neovim#15695
closes: vim/vim#17628

4ade668fb6
This commit is contained in:
glepnir
2025-07-04 13:44:39 +08:00
committed by GitHub
parent c48dea20f5
commit bb75610d99
7 changed files with 90 additions and 5 deletions

View File

@ -1962,6 +1962,13 @@ A jump table for the options with a short description can be found at |Q_op|.
character, the cursor won't move. When not included, character, the cursor won't move. When not included,
the cursor would skip over it and jump to the the cursor would skip over it and jump to the
following occurrence. following occurrence.
*cpo-~*
~ When included, don't resolve symbolic links when
changing directory with |:cd|, |:lcd|, or |:tcd|.
This preserves the symbolic link path in buffer names
and when displaying the current directory. When
excluded (default), symbolic links are resolved to
their target paths.
*cpo-_* *cpo-_*
_ When using |cw| on a word, do not include the _ When using |cw| on a word, do not include the
whitespace following the word in the motion. whitespace following the word in the motion.

View File

@ -1515,6 +1515,13 @@ vim.bo.ci = vim.bo.copyindent
--- character, the cursor won't move. When not included, --- character, the cursor won't move. When not included,
--- the cursor would skip over it and jump to the --- the cursor would skip over it and jump to the
--- following occurrence. --- following occurrence.
--- *cpo-~*
--- ~ When included, don't resolve symbolic links when
--- changing directory with `:cd`, `:lcd`, or `:tcd`.
--- This preserves the symbolic link path in buffer names
--- and when displaying the current directory. When
--- excluded (default), symbolic links are resolved to
--- their target paths.
--- *cpo-_* --- *cpo-_*
--- _ When using `cw` on a word, do not include the --- _ When using `cw` on a word, do not include the
--- whitespace following the word in the motion. --- whitespace following the word in the motion.

View File

@ -6016,7 +6016,7 @@ static void post_chdir(CdScope scope, bool trigger_dirchanged)
} }
last_chdir_reason = NULL; last_chdir_reason = NULL;
shorten_fnames(true); shorten_fnames(vim_strchr(p_cpo, CPO_NOSYMLINKS) == NULL);
if (trigger_dirchanged) { if (trigger_dirchanged) {
do_autocmd_dirchanged(cwd, scope, kCdCauseManual, false); do_autocmd_dirchanged(cwd, scope, kCdCauseManual, false);

View File

@ -121,10 +121,11 @@
#define CPO_REGAPPEND '>' // insert NL when appending to a register #define CPO_REGAPPEND '>' // insert NL when appending to a register
#define CPO_SCOLON ';' // using "," and ";" will skip over char if #define CPO_SCOLON ';' // using "," and ";" will skip over char if
// cursor would not move // cursor would not move
#define CPO_NOSYMLINKS '~' // don't resolve symlinks when changing directory
#define CPO_CHANGEW '_' // "cw" special-case #define CPO_CHANGEW '_' // "cw" special-case
// default values for Vim and Vi // default values for Vim and Vi
#define CPO_VIM "aABceFs_" #define CPO_VIM "aABceFs_"
#define CPO_VI "aAbBcCdDeEfFiIJKlLmMnoOpPqrRsStuvWxXyZ$!%+>;_" #define CPO_VI "aAbBcCdDeEfFiIJKlLmMnoOpPqrRsStuvWxXyZ$!%+>;~_"
// characters for p_ww option: // characters for p_ww option:
#define WW_ALL "bshl<>[]~" #define WW_ALL "bshl<>[]~"

View File

@ -2002,6 +2002,13 @@ local options = {
character, the cursor won't move. When not included, character, the cursor won't move. When not included,
the cursor would skip over it and jump to the the cursor would skip over it and jump to the
following occurrence. following occurrence.
*cpo-~*
~ When included, don't resolve symbolic links when
changing directory with |:cd|, |:lcd|, or |:tcd|.
This preserves the symbolic link path in buffer names
and when displaying the current directory. When
excluded (default), symbolic links are resolved to
their target paths.
*cpo-_* *cpo-_*
_ When using |cw| on a word, do not include the _ When using |cw| on a word, do not include the
whitespace following the word in the motion. whitespace following the word in the motion.

View File

@ -253,4 +253,67 @@ func Test_getcwd_actual_dir()
call chdir(startdir) call chdir(startdir)
endfunc endfunc
func Test_cd_preserve_symlinks()
" Test new behavior: preserve symlinks when cpo-=~
set cpoptions+=~
let savedir = getcwd()
call mkdir('Xsource', 'R')
call writefile(['abc'], 'Xsource/foo.txt', 'D')
if has("win32")
silent !mklink /D Xdest Xsource
else
silent !ln -s Xsource Xdest
endif
if v:shell_error
call delete('Xsource', 'rf')
throw 'Skipped: cannot create symlinks'
endif
edit Xdest/foo.txt
let path_before = expand('%')
call assert_match('Xdest[/\\]foo\.txt$', path_before)
cd .
let path_after = expand('%')
call assert_equal(path_before, path_after)
call assert_match('Xdest[/\\]foo\.txt$', path_after)
bwipe!
set cpoptions&
call delete('Xdest', 'rf')
call delete('Xsource', 'rf')
call chdir(savedir)
endfunc
func Test_cd_symlinks()
CheckNotMSWindows
let savedir = getcwd()
call mkdir('Xsource', 'R')
call writefile(['abc'], 'Xsource/foo.txt', 'D')
silent !ln -s Xsource Xdest
if v:shell_error
call delete('Xsource', 'rf')
throw 'Skipped: cannot create symlinks'
endif
edit Xdest/foo.txt
let path_before = expand('%')
call assert_match('Xdest[/\\]foo\.txt$', path_before)
cd .
let path_after = expand('%')
call assert_match('Xsource[/\\]foo\.txt$', path_after)
call assert_notequal(path_before, path_after)
bwipe!
set cpoptions&
call delete('Xdest', 'rf')
call delete('Xsource', 'rf')
call chdir(savedir)
endfunc
" vim: shiftwidth=2 sts=2 expandtab " vim: shiftwidth=2 sts=2 expandtab

View File

@ -2265,7 +2265,7 @@ func Test_VIM_POSIX()
qall qall
[CODE] [CODE]
if RunVim([], after, '') if RunVim([], after, '')
call assert_equal(['aAbBcCdDeEfFgHiIjJkKlLmMnoOpPqrRsStuvwWxXyZ$!%*-+<>#{|&/\.;', call assert_equal(['aAbBcCdDeEfFgHiIjJkKlLmMnoOpPqrRsStuvwWxXyZ$!%*-+<>#{|&/\.;~',
\ 'AS'], readfile('X_VIM_POSIX')) \ 'AS'], readfile('X_VIM_POSIX'))
endif endif
@ -2527,8 +2527,8 @@ func Test_string_option_revert_on_failure()
\ ['completeopt', 'preview', 'a123'], \ ['completeopt', 'preview', 'a123'],
"\ ['completepopup', 'width:20', 'border'], "\ ['completepopup', 'width:20', 'border'],
\ ['concealcursor', 'v', 'xyz'], \ ['concealcursor', 'v', 'xyz'],
"\ ['cpoptions', 'HJ', '~'], "\ ['cpoptions', 'HJ', 'Q'],
\ ['cpoptions', 'J', '~'], \ ['cpoptions', 'J', 'Q'],
"\ ['cryptmethod', 'zip', 'a123'], "\ ['cryptmethod', 'zip', 'a123'],
\ ['cursorlineopt', 'screenline', 'a123'], \ ['cursorlineopt', 'screenline', 'a123'],
\ ['debug', 'throw', 'a123'], \ ['debug', 'throw', 'a123'],