mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
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)
543c9b1921
closes #12212
This commit is contained in:
parent
48c2198297
commit
d2766b06c8
@ -6688,6 +6688,33 @@ range({expr} [, {max} [, {stride}]]) *range()*
|
||||
range(2, -2, -1) " [2, 1, 0, -1, -2]
|
||||
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}]])
|
||||
@ -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.
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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={},
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user