aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/editing.txt4
-rw-r--r--src/nvim/os/shell.c27
-rw-r--r--test/old/testdir/check.vim16
-rw-r--r--test/old/testdir/test_functions.vim22
-rw-r--r--test/old/testdir/test_messages.vim3
5 files changed, 65 insertions, 7 deletions
diff --git a/runtime/doc/editing.txt b/runtime/doc/editing.txt
index 2f44256138..abfe466b07 100644
--- a/runtime/doc/editing.txt
+++ b/runtime/doc/editing.txt
@@ -356,7 +356,9 @@ as a wildcard when "[" is in the 'isfname' option. A simple way to avoid this
is to use "path\[[]abc]", this matches the file "path\[abc]".
*starstar-wildcard*
-Expanding "**" is possible on Unix, Win32, macOS and a few other systems.
+Expanding "**" is possible on Unix, Win32, macOS and a few other systems (but
+it may depend on your 'shell' setting. It's known to work correctly for zsh; for
+bash this requires at least bash version >= 4.X).
This allows searching a directory tree. This goes up to 100 directories deep.
Note there are some commands where this works slightly differently, see
|file-searching|.
diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c
index 1cd560c5ef..582135349f 100644
--- a/src/nvim/os/shell.c
+++ b/src/nvim/os/shell.c
@@ -134,6 +134,8 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in
#define STYLE_VIMGLOB 2 // use "vimglob", for Posix sh
#define STYLE_PRINT 3 // use "print -N", for zsh
#define STYLE_BT 4 // `cmd` expansion, execute the pattern directly
+#define STYLE_GLOBSTAR 5 // use extended shell glob for bash (this uses extended
+ // globbing functionality with globstar, needs bash > 4)
int shell_style = STYLE_ECHO;
int check_spaces;
static bool did_find_nul = false;
@@ -141,6 +143,9 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in
// vimglob() function to define for Posix shell
static char *sh_vimglob_func =
"vimglob() { while [ $# -ge 1 ]; do echo \"$1\"; shift; done }; vimglob >";
+ // vimglob() function with globstar setting enabled, only for bash >= 4.X
+ static char *sh_globstar_opt =
+ "[[ ${BASH_VERSINFO[0]} -ge 4 ]] && shopt -s globstar; ";
bool is_fish_shell =
#if defined(UNIX)
@@ -190,6 +195,8 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in
// If we use *zsh, "print -N" will work better than "glob".
// STYLE_VIMGLOB: NL separated
// If we use *sh*, we define "vimglob()".
+ // STYLE_GLOBSTAR: NL separated
+ // If we use *bash*, we define "vimglob() and enable globstar option".
// STYLE_ECHO: space separated.
// A shell we don't know, stay safe and use "echo".
if (num_pat == 1 && *pat[0] == '`'
@@ -203,9 +210,12 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in
shell_style = STYLE_PRINT;
}
}
- if (shell_style == STYLE_ECHO
- && strstr(path_tail(p_sh), "sh") != NULL) {
- shell_style = STYLE_VIMGLOB;
+ if (shell_style == STYLE_ECHO) {
+ if (strstr(path_tail(p_sh), "bash") != NULL) {
+ shell_style = STYLE_GLOBSTAR;
+ } else if (strstr(path_tail(p_sh), "sh") != NULL) {
+ shell_style = STYLE_VIMGLOB;
+ }
}
// Compute the length of the command. We need 2 extra bytes: for the
@@ -214,6 +224,8 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in
len = strlen(tempname) + 29;
if (shell_style == STYLE_VIMGLOB) {
len += strlen(sh_vimglob_func);
+ } else if (shell_style == STYLE_GLOBSTAR) {
+ len += strlen(sh_vimglob_func) + strlen(sh_globstar_opt);
}
for (i = 0; i < num_pat; i++) {
@@ -281,6 +293,9 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in
STRCAT(command, "print -N >");
} else if (shell_style == STYLE_VIMGLOB) {
STRCAT(command, sh_vimglob_func);
+ } else if (shell_style == STYLE_GLOBSTAR) {
+ STRCAT(command, sh_globstar_opt);
+ STRCAT(command, sh_vimglob_func);
} else {
STRCAT(command, "echo >");
}
@@ -430,7 +445,9 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in
p = skipwhite(p); // skip to next entry
}
// file names are separated with NL
- } else if (shell_style == STYLE_BT || shell_style == STYLE_VIMGLOB) {
+ } else if (shell_style == STYLE_BT
+ || shell_style == STYLE_VIMGLOB
+ || shell_style == STYLE_GLOBSTAR) {
buffer[len] = NUL; // make sure the buffer ends in NUL
p = buffer;
for (i = 0; *p != NUL; i++) { // count number of entries
@@ -496,7 +513,7 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in
(*file)[i] = p;
// Space or NL separates
if (shell_style == STYLE_ECHO || shell_style == STYLE_BT
- || shell_style == STYLE_VIMGLOB) {
+ || shell_style == STYLE_VIMGLOB || shell_style == STYLE_GLOBSTAR) {
while (!(shell_style == STYLE_ECHO && *p == ' ')
&& *p != '\n' && *p != NUL) {
p++;
diff --git a/test/old/testdir/check.vim b/test/old/testdir/check.vim
index af1a80250c..647e25b214 100644
--- a/test/old/testdir/check.vim
+++ b/test/old/testdir/check.vim
@@ -108,6 +108,22 @@ func CheckNotBSD()
endif
endfunc
+" Command to check for not running on a MacOS
+command CheckNotMac call CheckNotMac()
+func CheckNotMac()
+ if has('mac')
+ throw 'Skipped: does not work on MacOS'
+ endif
+endfunc
+
+" Command to check for not running on a MacOS M1 system.
+command CheckNotMacM1 call CheckNotMacM1()
+func CheckNotMacM1()
+ if has('mac') && system('uname -a') =~ '\<arm64\>'
+ throw 'Skipped: does not work on MacOS M1'
+ endif
+endfunc
+
" Command to check that making screendumps is supported.
" Caller must source screendump.vim
command CheckScreendump call CheckScreendump()
diff --git a/test/old/testdir/test_functions.vim b/test/old/testdir/test_functions.vim
index 3e1e5a4816..7b20f47f7a 100644
--- a/test/old/testdir/test_functions.vim
+++ b/test/old/testdir/test_functions.vim
@@ -3281,4 +3281,26 @@ func Test_fullcommand()
call assert_equal('', fullcommand(10))
endfunc
+" Test for glob() with shell special patterns
+func Test_glob_extended_bash()
+ CheckExecutable bash
+ CheckNotMSWindows
+ CheckNotMac " The default version of bash is old on macOS.
+
+ let _shell = &shell
+ set shell=bash
+
+ call mkdir('Xtestglob/foo/bar/src', 'p')
+ call writefile([], 'Xtestglob/foo/bar/src/foo.sh')
+ call writefile([], 'Xtestglob/foo/bar/src/foo.h')
+ call writefile([], 'Xtestglob/foo/bar/src/foo.cpp')
+
+ " Sort output of glob() otherwise we end up with different
+ " ordering depending on whether file system is case-sensitive.
+ let expected = ['Xtestglob/foo/bar/src/foo.cpp', 'Xtestglob/foo/bar/src/foo.h']
+ call assert_equal(expected, sort(glob('Xtestglob/**/foo.{h,cpp}', 0, 1)))
+ call delete('Xtestglob', 'rf')
+ let &shell=_shell
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/test/old/testdir/test_messages.vim b/test/old/testdir/test_messages.vim
index adafb6bd23..9a999d71a8 100644
--- a/test/old/testdir/test_messages.vim
+++ b/test/old/testdir/test_messages.vim
@@ -379,7 +379,8 @@ endfunc
" Test verbose message before echo command
func Test_echo_verbose_system()
CheckRunVimInTerminal
- CheckUnix
+ CheckUnix " needs the "seq" command
+ CheckNotMac " doesn't use /tmp
let buf = RunVimInTerminal('', {'rows': 10})
call term_sendkeys(buf, ":4 verbose echo system('seq 20')\<CR>")