xaizek / vifm (License: GPLv2+) (since 2018-12-07)
Vifm is a file manager with curses interface, which provides Vi[m]-like environment for managing objects within file systems, extended with some useful ideas from mutt.
Commit 461666ff23d31f01f390154a191c13940ad3965b

Add "maxtreedepth" value to 'previewoptions'
It allows limiting depths of the displayed tree.

Thanks to Afz.

Closes #795 on GitHub.
Author: xaizek
Author date (UTC): 2022-06-19 09:57
Committer name: xaizek
Committer date (UTC): 2022-06-19 10:31
Parent(s): 925e873bd2ea41f3a07d9de63aed5359025fa133
Signing key: 99DC5E4DB05F6BE2
Tree: cb09f06341c23d1cafdcdbec5045a5e704400d1f
File Lines added Lines deleted
ChangeLog 3 0
data/man/vifm.1 7 1
data/vim/doc/app/vifm-app.txt 5 1
src/cfg/config.c 1 0
src/cfg/config.h 2 0
src/opt_handlers.c 30 2
src/ui/quickview.c 12 0
src/vcache.c 5 1
tests/misc/options.c 26 4
tests/misc/view_dir.c 28 0
File ChangeLog changed (mode: 100644) (index 52d412f4c..3bd66dc4e)
46 46 Added optional border for horizontal split layout controlled by newly Added optional border for horizontal split layout controlled by newly
47 47 added "hborder" in 'fillchars'. Patch by qsmodo. added "hborder" in 'fillchars'. Patch by qsmodo.
48 48
49 Added "maxtreedepth" value to 'previewoptions' option, which allows
50 limiting depths of the displayed tree. Thanks to Afz.
51
49 52 Extended optimizations for globs to cover `something*` and `some*thing` Extended optimizations for globs to cover `something*` and `some*thing`
50 53 cases. cases.
51 54
File data/man/vifm.1 changed (mode: 100644) (index 2ca7db327..cc8ab7fd4)
1 .TH VIFM 1 "June 12, 2022" "vifm 0.12"
1 .TH VIFM 1 "June 19, 2022" "vifm 0.12"
2 2 .\" --------------------------------------------------------------------------- .\" ---------------------------------------------------------------------------
3 3 .SH NAME .SH NAME
4 4 .\" --------------------------------------------------------------------------- .\" ---------------------------------------------------------------------------
 
... ... view mode).
4433 4433 item default meaning item default meaning
4434 4434 graphicsdelay:num 0 delay before drawing graphics (microseconds) graphicsdelay:num 0 delay before drawing graphics (microseconds)
4435 4435 hardgraphicsclear unset redraw screen to get rid of graphics hardgraphicsclear unset redraw screen to get rid of graphics
4436 maxtreedepth:num 0 max number of levels in preview tree
4436 4437 toptreestats unset show file counts before the tree toptreestats unset show file counts before the tree
4437 4438
4438 4439 graphicsdelay is needed if terminal requires some timeout before it can graphicsdelay is needed if terminal requires some timeout before it can
 
... ... draw graphics (otherwise it gets lost).
4441 4442 hardgraphicsclear seems to be necessary to get rid of sixel graphics in some hardgraphicsclear seems to be necessary to get rid of sixel graphics in some
4442 4443 terminals, where it otherwise lingers. This can cause flicker on the screen terminals, where it otherwise lingers. This can cause flicker on the screen
4443 4444 due to erasure followed by redrawing. due to erasure followed by redrawing.
4445
4446 0 for maxtreedepth means "unlimited", 1 will only show selected directory, 2
4447 adds its children, and so forth.
4448
4449 Default value is used when item is missing from the option.
4444 4450 .TP .TP
4445 4451 .BI "'previewprg'" .BI "'previewprg'"
4446 4452 type: string type: string
File data/vim/doc/app/vifm-app.txt changed (mode: 100644) (index cc5460534..8485ae6f0)
1 *vifm-app.txt* For Vifm version 0.12 Last change: 2022 June 12
1 *vifm-app.txt* For Vifm version 0.12 Last change: 2022 June 19
2 2
3 3 Email for bugs and suggestions: <xaizek@posteo.net> Email for bugs and suggestions: <xaizek@posteo.net>
4 4
 
... ... view mode).
3721 3721 item default meaning ~ item default meaning ~
3722 3722 graphicsdelay:num 0 delay before drawing graphics (microseconds) graphicsdelay:num 0 delay before drawing graphics (microseconds)
3723 3723 hardgraphicsclear unset redraw screen to get rid of graphics hardgraphicsclear unset redraw screen to get rid of graphics
3724 maxtreedepth:num 0 max number of levels in preview tree
3724 3725 toptreestats unset show file counts before the tree toptreestats unset show file counts before the tree
3725 3726
3726 3727 graphicsdelay is needed if terminal requires some timeout before it can graphicsdelay is needed if terminal requires some timeout before it can
 
... ... hardgraphicsclear seems to be necessary to get rid of sixel graphics in some
3730 3731 terminals, where it otherwise lingers. This can cause flicker on the screen terminals, where it otherwise lingers. This can cause flicker on the screen
3731 3732 due to erasure followed by redrawing. due to erasure followed by redrawing.
3732 3733
3734 0 for maxtreedepth means "unlimited", 1 will only show selected directory, 2
3735 adds its children, and so forth.
3736
3733 3737 Default value is used when item is missing from the option. Default value is used when item is missing from the option.
3734 3738
3735 3739 *vifm-'previewprg'* *vifm-'previewprg'*
File src/cfg/config.c changed (mode: 100644) (index 1b58e42bb..2b925ab19)
... ... cfg_init(void)
199 199 cfg.graphics_delay = 50000; cfg.graphics_delay = 50000;
200 200 cfg.hard_graphics_clear = 0; cfg.hard_graphics_clear = 0;
201 201 cfg.top_tree_stats = 0; cfg.top_tree_stats = 0;
202 cfg.max_tree_depth = 0;
202 203
203 204 cfg.timeout_len = 1000; cfg.timeout_len = 1000;
204 205 cfg.min_timeout_len = 150; cfg.min_timeout_len = 150;
File src/cfg/config.h changed (mode: 100644) (index b1ee5e0e0..a99ed17b0)
... ... typedef struct config_t
312 312 int hard_graphics_clear; int hard_graphics_clear;
313 313 /* Show file counts on top of the tree. */ /* Show file counts on top of the tree. */
314 314 int top_tree_stats; int top_tree_stats;
315 /* Max depth of preview tree. Zero means "no limit". */
316 int max_tree_depth;
315 317
316 318 int timeout_len; /* Maximum period on waiting for the input. */ int timeout_len; /* Maximum period on waiting for the input. */
317 319 int min_timeout_len; /* Minimum period on waiting for the input. */ int min_timeout_len; /* Minimum period on waiting for the input. */
File src/opt_handlers.c changed (mode: 100644) (index 59316f2a5..88cfe1218)
... ... static const char *lsoptions_enum[][2] = {
337 337 static const char *previewoptions_vals[][2] = { static const char *previewoptions_vals[][2] = {
338 338 { "graphicsdelay:", "delay before drawing graphics" }, { "graphicsdelay:", "delay before drawing graphics" },
339 339 { "hardgraphicsclear", "redraw screen to get rid of graphics" }, { "hardgraphicsclear", "redraw screen to get rid of graphics" },
340 { "maxtreedepth:", "how many tree levels too display" },
340 341 { "toptreestats", "show file counts on top of the tree" }, { "toptreestats", "show file counts on top of the tree" },
341 342 }; };
342 343
 
... ... init_previewoptions(optval_t *val)
1212 1213 { {
1213 1214 (void)sstrappend(buf, &len, sizeof(buf), "toptreestats,"); (void)sstrappend(buf, &len, sizeof(buf), "toptreestats,");
1214 1215 } }
1216 if(cfg.max_tree_depth > 0)
1217 {
1218 snprintf(buf + len, sizeof(buf) - len, "maxtreedepth:%d,",
1219 cfg.max_tree_depth);
1220 }
1215 1221 if(cfg.graphics_delay != 0) if(cfg.graphics_delay != 0)
1216 1222 { {
1217 1223 snprintf(buf + len, sizeof(buf) - len, "graphicsdelay:%d,", snprintf(buf + len, sizeof(buf) - len, "graphicsdelay:%d,",
 
... ... previewoptions_handler(OPT_OP op, optval_t val)
2324 2330 int graphics_delay = 0; int graphics_delay = 0;
2325 2331 int hard_graphics_clear = 0; int hard_graphics_clear = 0;
2326 2332 int top_tree_stats = 0; int top_tree_stats = 0;
2333 int max_tree_depth = 0;
2327 2334
2328 2335 while((part = split_and_get(part, ',', &state)) != NULL) while((part = split_and_get(part, ',', &state)) != NULL)
2329 2336 { {
 
... ... previewoptions_handler(OPT_OP op, optval_t val)
2343 2350 break; break;
2344 2351 } }
2345 2352 } }
2353 else if(starts_with_lit(part, "maxtreedepth:"))
2354 {
2355 const char *const num = after_first(part, ':');
2356 if(!read_int(num, &max_tree_depth))
2357 {
2358 vle_tb_append_linef(vle_err,
2359 "Failed to parse \"maxtreedepth\" value: %s", num);
2360 break;
2361 }
2362 if(max_tree_depth < 0)
2363 {
2364 vle_tb_append_linef(vle_err,
2365 "\"maxtreedepth\" can't be negative, got: %s", num);
2366 break;
2367 }
2368 }
2346 2369 else if(strcmp(part, "hardgraphicsclear") == 0) else if(strcmp(part, "hardgraphicsclear") == 0)
2347 2370 { {
2348 2371 hard_graphics_clear = 1; hard_graphics_clear = 1;
 
... ... previewoptions_handler(OPT_OP op, optval_t val)
2363 2386
2364 2387 if(part == NULL) if(part == NULL)
2365 2388 { {
2389 int need_update = (top_tree_stats != cfg.top_tree_stats)
2390 || (max_tree_depth != cfg.max_tree_depth);
2391
2366 2392 cfg.graphics_delay = graphics_delay; cfg.graphics_delay = graphics_delay;
2367 2393 cfg.hard_graphics_clear = hard_graphics_clear; cfg.hard_graphics_clear = hard_graphics_clear;
2368 if(top_tree_stats != cfg.top_tree_stats)
2394 cfg.top_tree_stats = top_tree_stats;
2395 cfg.max_tree_depth = max_tree_depth;
2396
2397 if(need_update)
2369 2398 { {
2370 2399 text_option_changed(); text_option_changed();
2371 2400 } }
2372 cfg.top_tree_stats = top_tree_stats;
2373 2401 } }
2374 2402
2375 2403 /* In case of error, restore previous value, otherwise reload it anyway to /* In case of error, restore previous value, otherwise reload it anyway to
File src/ui/quickview.c changed (mode: 100644) (index 76ff2b98a..c5f3bda8e)
... ... typedef struct
86 86 int nfiles; /* Number of seen files. */ int nfiles; /* Number of seen files. */
87 87 int max; /* Maximum line number. */ int max; /* Maximum line number. */
88 88 int full_stats; /* Collect statistics for the whole tree. */ int full_stats; /* Collect statistics for the whole tree. */
89 int depth; /* Current depth of the traversal. */
89 90 char prefix[4096]; /* Prefix character for each tree level. */ char prefix[4096]; /* Prefix character for each tree level. */
90 91 } }
91 92 tree_print_state_t; tree_print_state_t;
 
... ... print_dir_tree(tree_print_state_t *s, const char path[], int last)
533 534 return 1; return 1;
534 535 } }
535 536
537 /* No need to check cfg.max_tree_depth for 0, after enter_dir s->depth is
538 * greater than 0. */
539 if(s->depth == cfg.max_tree_depth)
540 {
541 free_string_array(lst, len);
542 leave_dir(s);
543 return 0;
544 }
545
536 546 int i; int i;
537 547 int reached_limit = 0; int reached_limit = 0;
538 548 for(i = 0; i < len && !reached_limit && !ui_cancellation_requested(); ++i) for(i = 0; i < len && !reached_limit && !ui_cancellation_requested(); ++i)
 
... ... enter_dir(tree_print_state_t *s, const char path[], int last)
662 672 indent_prefix(s); indent_prefix(s);
663 673 set_prefix_char(s, '|'); set_prefix_char(s, '|');
664 674
675 ++s->depth;
665 676 return (++s->n >= s->max); return (++s->n >= s->max);
666 677 } }
667 678
 
... ... static void
696 707 leave_dir(tree_print_state_t *s) leave_dir(tree_print_state_t *s)
697 708 { {
698 709 unindent_prefix(s); unindent_prefix(s);
710 --s->depth;
699 711 } }
700 712
701 713 /* Adds one indentation level for the following elements of tree. */ /* Adds one indentation level for the following elements of tree. */
File src/vcache.c changed (mode: 100644) (index cb100f337..4565a350a)
... ... typedef struct vcache_entry_t
63 63 size_t size; /* Size taken up by this entry (lower bound). */ size_t size; /* Size taken up by this entry (lower bound). */
64 64 int max_lines; /* Number of lines requested. */ int max_lines; /* Number of lines requested. */
65 65
66 /* Value of maxtreedepth for this entry. */
67 int max_tree_depth;
66 68 /* Whether cache contains complete output of the viewer. */ /* Whether cache contains complete output of the viewer. */
67 69 unsigned int complete : 1; unsigned int complete : 1;
68 70 /* Whether last line is truncated. */ /* Whether last line is truncated. */
 
... ... is_cache_valid(const vcache_entry_t *centry, const char path[],
424 426 } }
425 427 } }
426 428
427 if(centry->top_tree_stats != cfg.top_tree_stats && is_dir(path))
429 if((centry->top_tree_stats != cfg.top_tree_stats ||
430 centry->max_tree_depth != cfg.max_tree_depth) && is_dir(path))
428 431 { {
429 432 return 0; return 0;
430 433 } }
 
... ... view_builtin(vcache_entry_t *centry, const char **error)
671 674 if(dir) if(dir)
672 675 { {
673 676 centry->top_tree_stats = cfg.top_tree_stats; centry->top_tree_stats = cfg.top_tree_stats;
677 centry->max_tree_depth = cfg.max_tree_depth;
674 678 fp = qv_view_dir(centry->path, centry->max_lines); fp = qv_view_dir(centry->path, centry->max_lines);
675 679 } }
676 680 else else
File tests/misc/options.c changed (mode: 100644) (index a9e37e933..47e1a5254)
... ... TEST(previewoptions)
684 684 CIT_COMMAND)); CIT_COMMAND));
685 685 assert_int_equal(12345, cfg.graphics_delay); assert_int_equal(12345, cfg.graphics_delay);
686 686 assert_false(cfg.hard_graphics_clear); assert_false(cfg.hard_graphics_clear);
687 assert_int_equal(0, cfg.max_tree_depth);
687 688 assert_false(cfg.top_tree_stats); assert_false(cfg.top_tree_stats);
688 689
689 690 assert_failure(exec_commands("set previewoptions=graphicsdelay:inf", &lwin, assert_failure(exec_commands("set previewoptions=graphicsdelay:inf", &lwin,
690 691 CIT_COMMAND)); CIT_COMMAND));
692 assert_string_equal("Failed to parse \"graphicsdelay\" value: inf",
693 vle_tb_get_data(vle_err));
694 assert_failure(exec_commands("set previewoptions=maxtreedepth:inf", &lwin,
695 CIT_COMMAND));
696 assert_string_equal("Failed to parse \"maxtreedepth\" value: inf",
697 vle_tb_get_data(vle_err));
691 698 assert_int_equal(12345, cfg.graphics_delay); assert_int_equal(12345, cfg.graphics_delay);
692 699 assert_false(cfg.hard_graphics_clear); assert_false(cfg.hard_graphics_clear);
700 assert_int_equal(0, cfg.max_tree_depth);
693 701 assert_false(cfg.top_tree_stats); assert_false(cfg.top_tree_stats);
694 assert_string_equal("Failed to parse \"graphicsdelay\" value: inf",
695 vle_tb_get_data(vle_err));
696 702
697 703 assert_failure(exec_commands("set previewoptions=graphicsdelay:-12345", &lwin, assert_failure(exec_commands("set previewoptions=graphicsdelay:-12345", &lwin,
698 704 CIT_COMMAND)); CIT_COMMAND));
705 assert_string_equal("\"graphicsdelay\" can't be negative, got: -12345",
706 vle_tb_get_data(vle_err));
707 assert_failure(exec_commands("set previewoptions=maxtreedepth:-1", &lwin,
708 CIT_COMMAND));
709 assert_string_equal("\"maxtreedepth\" can't be negative, got: -1",
710 vle_tb_get_data(vle_err));
699 711 assert_int_equal(12345, cfg.graphics_delay); assert_int_equal(12345, cfg.graphics_delay);
700 712 assert_false(cfg.hard_graphics_clear); assert_false(cfg.hard_graphics_clear);
713 assert_int_equal(0, cfg.max_tree_depth);
701 714 assert_false(cfg.top_tree_stats); assert_false(cfg.top_tree_stats);
702 assert_string_equal("\"graphicsdelay\" can't be negative, got: -12345",
703 vle_tb_get_data(vle_err));
704 715
705 716 assert_failure(exec_commands("set previewoptions=graphicsdelay:145,wtf", assert_failure(exec_commands("set previewoptions=graphicsdelay:145,wtf",
706 717 &lwin, CIT_COMMAND)); &lwin, CIT_COMMAND));
707 718 assert_int_equal(12345, cfg.graphics_delay); assert_int_equal(12345, cfg.graphics_delay);
708 719 assert_false(cfg.hard_graphics_clear); assert_false(cfg.hard_graphics_clear);
720 assert_int_equal(0, cfg.max_tree_depth);
709 721 assert_false(cfg.top_tree_stats); assert_false(cfg.top_tree_stats);
710 722 assert_string_equal("Unknown key for 'previewoptions' option: wtf", assert_string_equal("Unknown key for 'previewoptions' option: wtf",
711 723 vle_tb_get_data(vle_err)); vle_tb_get_data(vle_err));
 
... ... TEST(previewoptions)
714 726 CIT_COMMAND)); CIT_COMMAND));
715 727 assert_int_equal(0, cfg.graphics_delay); assert_int_equal(0, cfg.graphics_delay);
716 728 assert_true(cfg.hard_graphics_clear); assert_true(cfg.hard_graphics_clear);
729 assert_int_equal(0, cfg.max_tree_depth);
717 730 assert_false(cfg.top_tree_stats); assert_false(cfg.top_tree_stats);
718 731
719 732 assert_success(exec_commands("set previewoptions=toptreestats", &lwin, assert_success(exec_commands("set previewoptions=toptreestats", &lwin,
720 733 CIT_COMMAND)); CIT_COMMAND));
721 734 assert_true(cfg.top_tree_stats); assert_true(cfg.top_tree_stats);
722 735 assert_int_equal(0, cfg.graphics_delay); assert_int_equal(0, cfg.graphics_delay);
736 assert_int_equal(0, cfg.max_tree_depth);
737 assert_false(cfg.hard_graphics_clear);
738
739 assert_success(exec_commands("set previewoptions=maxtreedepth:10", &lwin,
740 CIT_COMMAND));
741 assert_false(cfg.top_tree_stats);
742 assert_int_equal(0, cfg.graphics_delay);
743 assert_int_equal(10, cfg.max_tree_depth);
723 744 assert_false(cfg.hard_graphics_clear); assert_false(cfg.hard_graphics_clear);
724 745
725 746 assert_success(exec_commands("set previewoptions=", &lwin, CIT_COMMAND)); assert_success(exec_commands("set previewoptions=", &lwin, CIT_COMMAND));
726 747 assert_int_equal(0, cfg.graphics_delay); assert_int_equal(0, cfg.graphics_delay);
727 748 assert_false(cfg.hard_graphics_clear); assert_false(cfg.hard_graphics_clear);
749 assert_int_equal(0, cfg.max_tree_depth);
728 750 assert_false(cfg.top_tree_stats); assert_false(cfg.top_tree_stats);
729 751 } }
730 752
File tests/misc/view_dir.c changed (mode: 100644) (index 65d135dbe..c85221690)
... ... TEST(top_tree_stats_in_small_window)
294 294 cfg.top_tree_stats = 0; cfg.top_tree_stats = 0;
295 295 } }
296 296
297 TEST(depth_limit)
298 {
299 cfg.max_tree_depth = 2;
300
301 assert_success(os_mkdir("dir", 0777));
302 assert_success(os_mkdir("dir/nested1", 0777));
303 assert_success(os_mkdir("dir/nested1/nested2", 0777));
304
305 int nlines;
306 FILE *fp = qv_view_dir("dir", INT_MAX);
307 char **lines = read_file_lines(fp, &nlines);
308
309 assert_int_equal(4, nlines);
310 assert_string_equal("dir/", lines[0]);
311 assert_string_equal("`-- nested1/", lines[1]);
312 assert_string_equal("", lines[2]);
313 assert_string_equal("1 directory, 0 files", lines[3]);
314
315 free_string_array(lines, nlines);
316 fclose(fp);
317
318 assert_success(rmdir("dir/nested1/nested2"));
319 assert_success(rmdir("dir/nested1"));
320 assert_success(rmdir("dir"));
321
322 cfg.max_tree_depth = 0;
323 }
324
297 325 /* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */ /* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */
298 326 /* vim: set cinoptions+=t0 filetype=c : */ /* vim: set cinoptions+=t0 filetype=c : */
Hints

Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://code.reversed.top/user/xaizek/vifm

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@code.reversed.top/user/xaizek/vifm

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a pull request:
... clone the repository ...
... make some changes and some commits ...
git push origin master