diff options
author | Hennadii Chernyshchyk <genaloner@gmail.com> | 2020-05-05 18:15:45 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-05-05 08:15:45 -0700 |
commit | d2766b06c8fc50d06765c5c607744cc6b5f5ef0a (patch) | |
tree | 1b3b1a46c44a9c33163d0ef111d70e3bb3fd5784 | |
parent | 48c219829786c14b6e511229a59e2fda32ffe352 (diff) | |
download | rneovim-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.txt | 38 | ||||
-rw-r--r-- | src/nvim/eval.c | 4 | ||||
-rw-r--r-- | src/nvim/eval.lua | 1 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 93 | ||||
-rw-r--r-- | src/nvim/testdir/test_functions.vim | 30 |
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 |