aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHennadii Chernyshchyk <genaloner@gmail.com>2020-05-05 18:15:45 +0300
committerGitHub <noreply@github.com>2020-05-05 08:15:45 -0700
commitd2766b06c8fc50d06765c5c607744cc6b5f5ef0a (patch)
tree1b3b1a46c44a9c33163d0ef111d70e3bb3fd5784
parent48c219829786c14b6e511229a59e2fda32ffe352 (diff)
downloadrneovim-d2766b06c8fc50d06765c5c607744cc6b5f5ef0a.tar.gz
rneovim-d2766b06c8fc50d06765c5c607744cc6b5f5ef0a.tar.bz2
rneovim-d2766b06c8fc50d06765c5c607744cc6b5f5ef0a.zip
vim-patch:8.1.1120: cannot easily get directory entry matches #12222
Problem: Cannot easily get directory entry matches. Solution: Add the readdir() function. (Yasuhiro Matsumoto, closes vim/vim#2439) https://github.com/vim/vim/commit/543c9b1921d7605498b54afdef518e312f1b4515 closes #12212
-rw-r--r--runtime/doc/eval.txt38
-rw-r--r--src/nvim/eval.c4
-rw-r--r--src/nvim/eval.lua1
-rw-r--r--src/nvim/eval/funcs.c93
-rw-r--r--src/nvim/testdir/test_functions.vim30
5 files changed, 153 insertions, 13 deletions
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index 4ff6269004..6f13b34876 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -6689,6 +6689,33 @@ range({expr} [, {max} [, {stride}]]) *range()*
range(0) " []
range(2, 0) " error!
<
+ *readdir()*
+readdir({directory} [, {expr}])
+ Return a list with file and directory names in {directory}.
+
+ When {expr} is omitted all entries are included.
+ When {expr} is given, it is evaluated to check what to do:
+ If {expr} results in -1 then no further entries will
+ be handled.
+ If {expr} results in 0 then this entry will not be
+ added to the list.
+ If {expr} results in 1 then this entry will be added
+ to the list.
+ Each time {expr} is evaluated |v:val| is set to the entry name.
+ When {expr} is a function the name is passed as the argument.
+ For example, to get a list of files ending in ".txt": >
+ readdir(dirname, {n -> n =~ '.txt$'})
+< To skip hidden and backup files: >
+ readdir(dirname, {n -> n !~ '^\.\|\~$'})
+
+< If you want to get a directory tree: >
+ function! s:tree(dir)
+ return {a:dir : map(readdir(a:dir),
+ \ {_, x -> isdirectory(x) ?
+ \ {x : s:tree(a:dir . '/' . x)} : x})}
+ endfunction
+ echo s:tree(".")
+<
*readfile()*
readfile({fname} [, {binary} [, {max}]])
Read file {fname} and return a |List|, each line of the file
@@ -6720,17 +6747,6 @@ readfile({fname} [, {binary} [, {max}]])
the result is an empty list.
Also see |writefile()|.
- *readdir()*
-readdir({directory} [, {expr}])
- Return a list with file and directory names in {directory}.
- You can also use |glob()| if you don't need to do complicated
- things, such as limiting the number of matches.
-
- When {expr} is omitted all entries are included.
- When {expr} is given, it is evaluated to check what to do:
- If {expr} results in -1 then no further entries will
- be handled.
-
reg_executing() *reg_executing()*
Returns the single letter name of the register being executed.
Returns an empty string when no register is being executed.
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index a6162af472..4a0876a952 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -915,7 +915,7 @@ varnumber_T eval_to_number(char_u *expr)
* Save the current typeval in "save_tv".
* When not used yet add the variable to the v: hashtable.
*/
-static void prepare_vimvar(int idx, typval_T *save_tv)
+void prepare_vimvar(int idx, typval_T *save_tv)
{
*save_tv = vimvars[idx].vv_tv;
if (vimvars[idx].vv_type == VAR_UNKNOWN)
@@ -926,7 +926,7 @@ static void prepare_vimvar(int idx, typval_T *save_tv)
* Restore v: variable "idx" to typeval "save_tv".
* When no longer defined, remove the variable from the v: hashtable.
*/
-static void restore_vimvar(int idx, typval_T *save_tv)
+void restore_vimvar(int idx, typval_T *save_tv)
{
hashitem_T *hi;
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index d3e769a7ef..65c4cfe553 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -255,6 +255,7 @@ return {
pyeval={args=1},
pyxeval={args=1},
range={args={1, 3}},
+ readdir={args={1, 2}},
readfile={args={1, 3}},
reg_executing={},
reg_recording={},
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 26e87df6d4..217490ad10 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -6316,6 +6316,99 @@ static void f_range(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
+// Evaluate "expr" for readdir().
+static varnumber_T readdir_checkitem(typval_T *expr, const char *name)
+{
+ typval_T save_val;
+ typval_T rettv;
+ typval_T argv[2];
+ varnumber_T retval = 0;
+ bool error = false;
+
+ prepare_vimvar(VV_VAL, &save_val);
+ set_vim_var_string(VV_VAL, name, -1);
+ argv[0].v_type = VAR_STRING;
+ argv[0].vval.v_string = (char_u *)name;
+
+ if (eval_expr_typval(expr, argv, 1, &rettv) == FAIL) {
+ goto theend;
+ }
+
+ retval = tv_get_number_chk(&rettv, &error);
+ if (error) {
+ retval = -1;
+ }
+
+ tv_clear(&rettv);
+
+theend:
+ set_vim_var_string(VV_VAL, NULL, 0);
+ restore_vimvar(VV_VAL, &save_val);
+ return retval;
+}
+
+// "readdir()" function
+static void f_readdir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ typval_T *expr;
+ const char *path;
+ garray_T ga;
+ Directory dir;
+
+ tv_list_alloc_ret(rettv, kListLenUnknown);
+ path = tv_get_string(&argvars[0]);
+ expr = &argvars[1];
+ ga_init(&ga, (int)sizeof(char *), 20);
+
+ if (!os_scandir(&dir, path)) {
+ smsg(_(e_notopen), path);
+ } else {
+ for (;;) {
+ bool ignore;
+
+ path = os_scandir_next(&dir);
+ if (path == NULL) {
+ break;
+ }
+
+ ignore = (path[0] == '.'
+ && (path[1] == NUL || (path[1] == '.' && path[2] == NUL)));
+ if (!ignore && expr->v_type != VAR_UNKNOWN) {
+ varnumber_T r = readdir_checkitem(expr, path);
+
+ if (r < 0) {
+ break;
+ }
+ if (r == 0) {
+ ignore = true;
+ }
+ }
+
+ if (!ignore) {
+ ga_grow(&ga, 1);
+ ((char **)ga.ga_data)[ga.ga_len++] = xstrdup(path);
+ }
+ }
+
+ os_closedir(&dir);
+ }
+
+ rettv->vval.v_list = tv_list_alloc(kListLenShouldKnow);
+ if (rettv->vval.v_list != NULL) {
+ tv_list_ref(rettv->vval.v_list);
+ sort_strings((char_u **)ga.ga_data, ga.ga_len);
+ for (int i = 0; i < ga.ga_len; i++) {
+ path = ((const char **)ga.ga_data)[i];
+ tv_list_append_string(rettv->vval.v_list, path, -1);
+ }
+ }
+ for (int i = 0; i < ga.ga_len; i++) {
+ xfree(((uint8_t **)ga.ga_data)[i]);
+ }
+
+ ga_clear(&ga);
+}
+
/*
* "readfile()" function
*/
diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim
index bd5cb6ad19..51689db9c4 100644
--- a/src/nvim/testdir/test_functions.vim
+++ b/src/nvim/testdir/test_functions.vim
@@ -1307,3 +1307,33 @@ func Test_bufadd_bufload()
bwipe otherName
call assert_equal(0, bufexists('someName'))
endfunc
+
+func Test_readdir()
+ call mkdir('Xdir')
+ call writefile([], 'Xdir/foo.txt')
+ call writefile([], 'Xdir/bar.txt')
+ call mkdir('Xdir/dir')
+
+ " All results
+ let files = readdir('Xdir')
+ call assert_equal(['bar.txt', 'dir', 'foo.txt'], sort(files))
+
+ " Only results containing "f"
+ let files = readdir('Xdir', { x -> stridx(x, 'f') !=- 1 })
+ call assert_equal(['foo.txt'], sort(files))
+
+ " Only .txt files
+ let files = readdir('Xdir', { x -> x =~ '.txt$' })
+ call assert_equal(['bar.txt', 'foo.txt'], sort(files))
+
+ " Only .txt files with string
+ let files = readdir('Xdir', 'v:val =~ ".txt$"')
+ call assert_equal(['bar.txt', 'foo.txt'], sort(files))
+
+ " Limit to 1 result.
+ let l = []
+ let files = readdir('Xdir', {x -> len(add(l, x)) == 2 ? -1 : 1})
+ call assert_equal(1, len(files))
+
+ call delete('Xdir', 'rf')
+endfunc