diff --git a/README.md b/README.md index 725f743f..3f99bd5d 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,7 @@ Usage: dust --skip-total (No total row will be displayed) Usage: dust -z 40000/30MB/20kib (Exclude output files/directories below size 40000 bytes / 30MB / 20KiB) Usage: dust -j (Prints JSON representation of directories, try: dust -j | jq) Usage: dust --files0-from=FILE (Reads null-terminated file paths from FILE); If FILE is - then read from stdin +Usage: dust --collapse=node-modules will keep the node-modules folder collapsed in display instead of recursively opening it ``` ## Config file diff --git a/completions/_dust b/completions/_dust index 2fb2dede..3209c66c 100644 --- a/completions/_dust +++ b/completions/_dust @@ -45,6 +45,7 @@ _dust() { '-y+[just like -mtime, but based on file change time]: : ' \ '--ctime=[just like -mtime, but based on file change time]: : ' \ '--files0-from=[run dust on NUL-terminated file names specified in file; if argument is -, then read names from standard input]: :_files' \ +'*--collapse=[Keep these directories collapsed]: :_files' \ '-m+[Directory '\''size'\'' is max filetime of child files instead of disk size. while a/c/m for last accessed/changed/modified time]: :(a c m)' \ '--filetime=[Directory '\''size'\'' is max filetime of child files instead of disk size. while a/c/m for last accessed/changed/modified time]: :(a c m)' \ '-p[Subdirectories will not have their path shortened]' \ diff --git a/completions/_dust.ps1 b/completions/_dust.ps1 index edca87a3..295c01a3 100644 --- a/completions/_dust.ps1 +++ b/completions/_dust.ps1 @@ -51,6 +51,7 @@ Register-ArgumentCompleter -Native -CommandName 'dust' -ScriptBlock { [CompletionResult]::new('-y', 'y', [CompletionResultType]::ParameterName, 'just like -mtime, but based on file change time') [CompletionResult]::new('--ctime', 'ctime', [CompletionResultType]::ParameterName, 'just like -mtime, but based on file change time') [CompletionResult]::new('--files0-from', 'files0-from', [CompletionResultType]::ParameterName, 'run dust on NUL-terminated file names specified in file; if argument is -, then read names from standard input') + [CompletionResult]::new('--collapse', 'collapse', [CompletionResultType]::ParameterName, 'Keep these directories collapsed') [CompletionResult]::new('-m', 'm', [CompletionResultType]::ParameterName, 'Directory ''size'' is max filetime of child files instead of disk size. while a/c/m for last accessed/changed/modified time') [CompletionResult]::new('--filetime', 'filetime', [CompletionResultType]::ParameterName, 'Directory ''size'' is max filetime of child files instead of disk size. while a/c/m for last accessed/changed/modified time') [CompletionResult]::new('-p', 'p', [CompletionResultType]::ParameterName, 'Subdirectories will not have their path shortened') diff --git a/completions/dust.bash b/completions/dust.bash index 9b0b365f..2bb63210 100644 --- a/completions/dust.bash +++ b/completions/dust.bash @@ -19,7 +19,7 @@ _dust() { case "${cmd}" in dust) - opts="-d -T -n -p -X -I -L -x -s -r -c -C -b -B -z -R -f -i -v -e -t -w -P -D -F -o -S -j -M -A -y -m -h -V --depth --threads --config --number-of-lines --full-paths --ignore-directory --ignore-all-in-file --dereference-links --limit-filesystem --apparent-size --reverse --no-colors --force-colors --no-percent-bars --bars-on-right --min-size --screen-reader --skip-total --filecount --ignore_hidden --invert-filter --filter --file_types --terminal_width --no-progress --print-errors --only-dir --only-file --output-format --stack-size --output-json --mtime --atime --ctime --files0-from --filetime --help --version [PATH]..." + opts="-d -T -n -p -X -I -L -x -s -r -c -C -b -B -z -R -f -i -v -e -t -w -P -D -F -o -S -j -M -A -y -m -h -V --depth --threads --config --number-of-lines --full-paths --ignore-directory --ignore-all-in-file --dereference-links --limit-filesystem --apparent-size --reverse --no-colors --force-colors --no-percent-bars --bars-on-right --min-size --screen-reader --skip-total --filecount --ignore_hidden --invert-filter --filter --file_types --terminal_width --no-progress --print-errors --only-dir --only-file --output-format --stack-size --output-json --mtime --atime --ctime --files0-from --collapse --filetime --help --version [PATH]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -178,6 +178,10 @@ _dust() { COMPREPLY=($(compgen -f "${cur}")) return 0 ;; + --collapse) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; --filetime) COMPREPLY=($(compgen -W "a c m" -- "${cur}")) return 0 diff --git a/completions/dust.elv b/completions/dust.elv index 5ba01abd..d39054b7 100644 --- a/completions/dust.elv +++ b/completions/dust.elv @@ -48,6 +48,7 @@ set edit:completion:arg-completer[dust] = {|@words| cand -y 'just like -mtime, but based on file change time' cand --ctime 'just like -mtime, but based on file change time' cand --files0-from 'run dust on NUL-terminated file names specified in file; if argument is -, then read names from standard input' + cand --collapse 'Keep these directories collapsed' cand -m 'Directory ''size'' is max filetime of child files instead of disk size. while a/c/m for last accessed/changed/modified time' cand --filetime 'Directory ''size'' is max filetime of child files instead of disk size. while a/c/m for last accessed/changed/modified time' cand -p 'Subdirectories will not have their path shortened' diff --git a/completions/dust.fish b/completions/dust.fish index 4e733c68..e7fe2548 100644 --- a/completions/dust.fish +++ b/completions/dust.fish @@ -14,6 +14,7 @@ complete -c dust -s M -l mtime -d '+/-n matches files modified more/less than n complete -c dust -s A -l atime -d 'just like -mtime, but based on file access time' -r complete -c dust -s y -l ctime -d 'just like -mtime, but based on file change time' -r complete -c dust -l files0-from -d 'run dust on NUL-terminated file names specified in file; if argument is -, then read names from standard input' -r -F +complete -c dust -l collapse -d 'Keep these directories collapsed' -r -F complete -c dust -s m -l filetime -d 'Directory \'size\' is max filetime of child files instead of disk size. while a/c/m for last accessed/changed/modified time' -r -f -a "{a\t'',c\t'',m\t''}" complete -c dust -s p -l full-paths -d 'Subdirectories will not have their path shortened' complete -c dust -s L -l dereference-links -d 'dereference sym links - Treat sym links as directories and go into them' diff --git a/man-page/dust.1 b/man-page/dust.1 index f67c8c83..1d3ebd9d 100644 --- a/man-page/dust.1 +++ b/man-page/dust.1 @@ -4,7 +4,7 @@ .SH NAME Dust \- Like du but more intuitive .SH SYNOPSIS -\fBdust\fR [\fB\-d\fR|\fB\-\-depth\fR] [\fB\-T\fR|\fB\-\-threads\fR] [\fB\-\-config\fR] [\fB\-n\fR|\fB\-\-number\-of\-lines\fR] [\fB\-p\fR|\fB\-\-full\-paths\fR] [\fB\-X\fR|\fB\-\-ignore\-directory\fR] [\fB\-I\fR|\fB\-\-ignore\-all\-in\-file\fR] [\fB\-L\fR|\fB\-\-dereference\-links\fR] [\fB\-x\fR|\fB\-\-limit\-filesystem\fR] [\fB\-s\fR|\fB\-\-apparent\-size\fR] [\fB\-r\fR|\fB\-\-reverse\fR] [\fB\-c\fR|\fB\-\-no\-colors\fR] [\fB\-C\fR|\fB\-\-force\-colors\fR] [\fB\-b\fR|\fB\-\-no\-percent\-bars\fR] [\fB\-B\fR|\fB\-\-bars\-on\-right\fR] [\fB\-z\fR|\fB\-\-min\-size\fR] [\fB\-R\fR|\fB\-\-screen\-reader\fR] [\fB\-\-skip\-total\fR] [\fB\-f\fR|\fB\-\-filecount\fR] [\fB\-i\fR|\fB\-\-ignore_hidden\fR] [\fB\-v\fR|\fB\-\-invert\-filter\fR] [\fB\-e\fR|\fB\-\-filter\fR] [\fB\-t\fR|\fB\-\-file_types\fR] [\fB\-w\fR|\fB\-\-terminal_width\fR] [\fB\-P\fR|\fB\-\-no\-progress\fR] [\fB\-\-print\-errors\fR] [\fB\-D\fR|\fB\-\-only\-dir\fR] [\fB\-F\fR|\fB\-\-only\-file\fR] [\fB\-o\fR|\fB\-\-output\-format\fR] [\fB\-S\fR|\fB\-\-stack\-size\fR] [\fB\-j\fR|\fB\-\-output\-json\fR] [\fB\-M\fR|\fB\-\-mtime\fR] [\fB\-A\fR|\fB\-\-atime\fR] [\fB\-y\fR|\fB\-\-ctime\fR] [\fB\-\-files0\-from\fR] [\fB\-m\fR|\fB\-\-filetime\fR] [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] [\fIPATH\fR] +\fBdust\fR [\fB\-d\fR|\fB\-\-depth\fR] [\fB\-T\fR|\fB\-\-threads\fR] [\fB\-\-config\fR] [\fB\-n\fR|\fB\-\-number\-of\-lines\fR] [\fB\-p\fR|\fB\-\-full\-paths\fR] [\fB\-X\fR|\fB\-\-ignore\-directory\fR] [\fB\-I\fR|\fB\-\-ignore\-all\-in\-file\fR] [\fB\-L\fR|\fB\-\-dereference\-links\fR] [\fB\-x\fR|\fB\-\-limit\-filesystem\fR] [\fB\-s\fR|\fB\-\-apparent\-size\fR] [\fB\-r\fR|\fB\-\-reverse\fR] [\fB\-c\fR|\fB\-\-no\-colors\fR] [\fB\-C\fR|\fB\-\-force\-colors\fR] [\fB\-b\fR|\fB\-\-no\-percent\-bars\fR] [\fB\-B\fR|\fB\-\-bars\-on\-right\fR] [\fB\-z\fR|\fB\-\-min\-size\fR] [\fB\-R\fR|\fB\-\-screen\-reader\fR] [\fB\-\-skip\-total\fR] [\fB\-f\fR|\fB\-\-filecount\fR] [\fB\-i\fR|\fB\-\-ignore_hidden\fR] [\fB\-v\fR|\fB\-\-invert\-filter\fR] [\fB\-e\fR|\fB\-\-filter\fR] [\fB\-t\fR|\fB\-\-file_types\fR] [\fB\-w\fR|\fB\-\-terminal_width\fR] [\fB\-P\fR|\fB\-\-no\-progress\fR] [\fB\-\-print\-errors\fR] [\fB\-D\fR|\fB\-\-only\-dir\fR] [\fB\-F\fR|\fB\-\-only\-file\fR] [\fB\-o\fR|\fB\-\-output\-format\fR] [\fB\-S\fR|\fB\-\-stack\-size\fR] [\fB\-j\fR|\fB\-\-output\-json\fR] [\fB\-M\fR|\fB\-\-mtime\fR] [\fB\-A\fR|\fB\-\-atime\fR] [\fB\-y\fR|\fB\-\-ctime\fR] [\fB\-\-files0\-from\fR] [\fB\-\-collapse\fR] [\fB\-m\fR|\fB\-\-filetime\fR] [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] [\fIPATH\fR] .SH DESCRIPTION Like du but more intuitive .SH OPTIONS @@ -118,6 +118,9 @@ just like \-mtime, but based on file change time \fB\-\-files0\-from\fR run dust on NUL\-terminated file names specified in file; if argument is \-, then read names from standard input .TP +\fB\-\-collapse\fR +Keep these directories collapsed +.TP \fB\-m\fR, \fB\-\-filetime\fR Directory \*(Aqsize\*(Aq is max filetime of child files instead of disk size. while a/c/m for last accessed/changed/modified time .br diff --git a/src/cli.rs b/src/cli.rs index d8d8cb4d..905a27ce 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -303,6 +303,14 @@ pub fn build_cli() -> Command { .num_args(1) .help("run dust on NUL-terminated file names specified in file; if argument is -, then read names from standard input"), ) + .arg( + Arg::new("collapse") + .long("collapse") + .value_hint(clap::ValueHint::AnyPath) + .value_parser(value_parser!(String)) + .action(clap::ArgAction::Append) + .help("Keep these directories collapsed"), + ) .arg( Arg::new("filetime") .short('m') diff --git a/src/filter.rs b/src/filter.rs index c81aad08..c7629bb5 100644 --- a/src/filter.rs +++ b/src/filter.rs @@ -3,6 +3,7 @@ use crate::node::FileTime; use crate::node::Node; use std::collections::BinaryHeap; use std::collections::HashMap; +use std::collections::HashSet; use std::path::Path; use std::path::PathBuf; @@ -19,6 +20,7 @@ pub fn get_biggest( top_level_nodes: Vec, display_data: AggregateData, by_filetime: &Option, + keep_collapsed: HashSet, ) -> Option { if top_level_nodes.is_empty() { // perhaps change this, bring back Error object? @@ -52,13 +54,19 @@ pub fn get_biggest( heap = add_children(&display_data, &root, heap); } - Some(fill_remaining_lines(heap, &root, display_data)) + Some(fill_remaining_lines( + heap, + &root, + display_data, + keep_collapsed, + )) } pub fn fill_remaining_lines<'a>( mut heap: BinaryHeap<&'a Node>, root: &'a Node, display_data: AggregateData, + keep_collapsed: HashSet, ) -> DisplayNode { let mut allowed_nodes = HashMap::new(); @@ -69,7 +77,9 @@ pub fn fill_remaining_lines<'a>( if !display_data.only_file || line.children.is_empty() { allowed_nodes.insert(line.name.as_path(), line); } - heap = add_children(&display_data, line, heap); + if !keep_collapsed.contains(&line.name) { + heap = add_children(&display_data, line, heap); + } } None => break, } diff --git a/src/main.rs b/src/main.rs index 53166674..66b9ac4d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -244,6 +244,19 @@ fn main() { indicator.spawn(output_format.clone()) } + let keep_collapsed: HashSet = match options.get_many::("collapse") { + Some(collapse) => { + let mut combined_dirs = HashSet::new(); + for collapse_dir in collapse { + for target_dir in target_dirs.iter() { + combined_dirs.insert(PathBuf::from(target_dir).join(collapse_dir)); + } + } + combined_dirs + } + None => HashSet::new(), + }; + let filter_modified_time = config.get_modified_time_operator(&options); let filter_accessed_time = config.get_accessed_time_operator(&options); let filter_changed_time = config.get_changed_time_operator(&options); @@ -281,7 +294,7 @@ fn main() { depth, using_a_filter: !filter_regexs.is_empty() || !invert_filter_regexs.is_empty(), }; - get_biggest(top_level_nodes, agg_data, &by_filetime) + get_biggest(top_level_nodes, agg_data, &by_filetime, keep_collapsed) } }; diff --git a/tests/test_flags.rs b/tests/test_flags.rs index ed3a8235..eed8c70c 100644 --- a/tests/test_flags.rs +++ b/tests/test_flags.rs @@ -254,3 +254,10 @@ pub fn test_force_color() { assert!(output.contains("\x1B[31m")); assert!(output.contains("\x1B[0m")); } + +#[test] +pub fn test_collapse() { + let output = build_command(vec!["--collapse", "many", "tests/test_dir/"]); + assert!(output.contains("many")); + assert!(!output.contains("hello_file")); +}