diff options
-rw-r--r-- | runtime/doc/eval.txt | 4 | ||||
-rw-r--r-- | src/nvim/option.c | 6 | ||||
-rw-r--r-- | src/nvim/strings.c | 12 | ||||
-rw-r--r-- | src/nvim/testdir/test_functions.vim | 15 |
4 files changed, 37 insertions, 0 deletions
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 87240831a2..9430d4ab58 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -8290,6 +8290,10 @@ shellescape({string} [, {special}]) *shellescape()* - The <NL> character is escaped (twice if {special} is a ||non-zero-arg|). + If 'shell' contains "fish" in the tail, the "\" character will + be escaped because in fish it is used as an escape character + inside single quotes. + Example of use with a |:!| command: > :exe '!dir ' . shellescape(expand('<cfile>'), 1) < This results in a directory listing for the file under the diff --git a/src/nvim/option.c b/src/nvim/option.c index d11bbc8ecc..fbf19ab9ff 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -7618,6 +7618,12 @@ int csh_like_shell(void) return strstr((char *)path_tail(p_sh), "csh") != NULL; } +/// Return true when 'shell' has "fish" in the tail. +bool fish_like_shell(void) +{ + return strstr((char *)path_tail(p_sh), "fish") != NULL; +} + /// Return the number of requested sign columns, based on current /// buffer signs and on user configuration. int win_signcol_count(win_T *wp) diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 0363afe02d..33310701d5 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -190,6 +190,7 @@ char_u *vim_strsave_shellescape(const char_u *string, char_u *escaped_string; size_t l; int csh_like; + bool fish_like; /* Only csh and similar shells expand '!' within single quotes. For sh and * the like we must not put a backslash before it, it will be taken @@ -197,6 +198,10 @@ char_u *vim_strsave_shellescape(const char_u *string, * Csh also needs to have "\n" escaped twice when do_special is set. */ csh_like = csh_like_shell(); + // Fish shell uses '\' as an escape character within single quotes, so '\' + // itself must be escaped to get a literal '\'. + fish_like = fish_like_shell(); + /* First count the number of extra bytes required. */ size_t length = STRLEN(string) + 3; // two quotes and a trailing NUL for (const char_u *p = string; *p != NUL; MB_PTR_ADV(p)) { @@ -220,6 +225,9 @@ char_u *vim_strsave_shellescape(const char_u *string, ++length; /* insert backslash */ p += l - 1; } + if (*p == '\\' && fish_like) { + length++; // insert backslash + } } /* Allocate memory for the result and fill it. */ @@ -267,6 +275,10 @@ char_u *vim_strsave_shellescape(const char_u *string, *d++ = *p++; continue; } + if (*p == '\\' && fish_like) { + *d++ = '\\'; + *d++ = *p++; + } MB_COPY_CHAR(p, d); } diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 6cb3e24201..c964f7aea4 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -1160,6 +1160,21 @@ func Test_shellescape() call assert_equal("'te\\\nxt'", shellescape("te\nxt")) call assert_equal("'te\\\\\nxt'", shellescape("te\nxt", 1)) + set shell=fish + call assert_equal("'text'", shellescape('text')) + call assert_equal("'te\"xt'", shellescape('te"xt')) + call assert_equal("'te'\\''xt'", shellescape("te'xt")) + + call assert_equal("'te%xt'", shellescape("te%xt")) + call assert_equal("'te\\%xt'", shellescape("te%xt", 1)) + call assert_equal("'te#xt'", shellescape("te#xt")) + call assert_equal("'te\\#xt'", shellescape("te#xt", 1)) + call assert_equal("'te!xt'", shellescape("te!xt")) + call assert_equal("'te\\!xt'", shellescape("te!xt", 1)) + + call assert_equal("'te\\\\xt'", shellescape("te\\xt")) + call assert_equal("'te\\\\xt'", shellescape("te\\xt", 1)) + let &shell = save_shell endfunc |