diff options
author | andy.boot <bootandy@gmail.com> | 2023-01-08 11:51:20 +0000 |
---|---|---|
committer | andy.boot <bootandy@gmail.com> | 2023-01-29 09:52:10 +0000 |
commit | 184d1ec5e8f739049e6c710afe58eeb8e10a14bb (patch) | |
tree | f3a6a15278dc1ebb09f0cbf87e864a1708a55831 | |
parent | 1e87a0661bb2e94734e7228ce1d63c353641499b (diff) |
Feature: Add flag for screen readers
Screen reader mode will,
Reverse direction
Not shows symbols (directory hierarchy or percentage bars)
Adds a column 'depth' to show depth of directory
-rw-r--r-- | completions/_dust | 2 | ||||
-rw-r--r-- | completions/_dust.ps1 | 2 | ||||
-rw-r--r-- | completions/dust.bash | 2 | ||||
-rw-r--r-- | completions/dust.elv | 2 | ||||
-rw-r--r-- | completions/dust.fish | 1 | ||||
-rw-r--r-- | man-page/dust.1 | 5 | ||||
-rw-r--r-- | src/cli.rs | 6 | ||||
-rw-r--r-- | src/config.rs | 4 | ||||
-rw-r--r-- | src/display.rs | 86 | ||||
-rw-r--r-- | src/main.rs | 1 | ||||
-rw-r--r-- | tests/test_flags.rs | 17 |
11 files changed, 105 insertions, 23 deletions
diff --git a/completions/_dust b/completions/_dust index a73c891..9e8be82 100644 --- a/completions/_dust +++ b/completions/_dust @@ -47,6 +47,8 @@ _dust() { '--no-colors[No colors will be printed (Useful for commands like: watch)]' \ '-b[No percent bars or percentages will be displayed]' \ '--no-percent-bars[No percent bars or percentages will be displayed]' \ +'-R[For screen readers. Removes bars. Adds new column: depth level (May want to use -p too for full path)]' \ +'--screen-reader[For screen readers. Removes bars. Adds new column: depth level (May want to use -p too for full path)]' \ '--skip-total[No total row will be displayed]' \ '-f[Directory '\''size'\'' is number of child files/dirs not disk size]' \ '--filecount[Directory '\''size'\'' is number of child files/dirs not disk size]' \ diff --git a/completions/_dust.ps1 b/completions/_dust.ps1 index 69ccc41..f87a5b9 100644 --- a/completions/_dust.ps1 +++ b/completions/_dust.ps1 @@ -53,6 +53,8 @@ Register-ArgumentCompleter -Native -CommandName 'dust' -ScriptBlock { [CompletionResult]::new('--no-colors', 'no-colors', [CompletionResultType]::ParameterName, 'No colors will be printed (Useful for commands like: watch)') [CompletionResult]::new('-b', 'b', [CompletionResultType]::ParameterName, 'No percent bars or percentages will be displayed') [CompletionResult]::new('--no-percent-bars', 'no-percent-bars', [CompletionResultType]::ParameterName, 'No percent bars or percentages will be displayed') + [CompletionResult]::new('-R', 'R', [CompletionResultType]::ParameterName, 'For screen readers. Removes bars. Adds new column: depth level (May want to use -p too for full path)') + [CompletionResult]::new('--screen-reader', 'screen-reader', [CompletionResultType]::ParameterName, 'For screen readers. Removes bars. Adds new column: depth level (May want to use -p too for full path)') [CompletionResult]::new('--skip-total', 'skip-total', [CompletionResultType]::ParameterName, 'No total row will be displayed') [CompletionResult]::new('-f', 'f', [CompletionResultType]::ParameterName, 'Directory ''size'' is number of child files/dirs not disk size') [CompletionResult]::new('--filecount', 'filecount', [CompletionResultType]::ParameterName, 'Directory ''size'' is number of child files/dirs not disk size') diff --git a/completions/dust.bash b/completions/dust.bash index 64b39a8..83d7b65 100644 --- a/completions/dust.bash +++ b/completions/dust.bash @@ -19,7 +19,7 @@ _dust() { case "${cmd}" in dust) - opts="-h -V -d -n -p -X -L -x -s -r -c -b -z -f -i -v -e -t -w -H -P -D -F --help --version --depth --number-of-lines --full-paths --ignore-directory --dereference-links --limit-filesystem --apparent-size --reverse --no-colors --no-percent-bars --min-size --skip-total --filecount --ignore_hidden --invert-filter --filter --file_types --terminal_width --si --no-progress --only-dir --only-file <inputs>..." + opts="-h -V -d -n -p -X -L -x -s -r -c -b -z -R -f -i -v -e -t -w -H -P -D -F --help --version --depth --number-of-lines --full-paths --ignore-directory --dereference-links --limit-filesystem --apparent-size --reverse --no-colors --no-percent-bars --min-size --screen-reader --skip-total --filecount --ignore_hidden --invert-filter --filter --file_types --terminal_width --si --no-progress --only-dir --only-file <inputs>..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 diff --git a/completions/dust.elv b/completions/dust.elv index acb8ef9..a11c4a9 100644 --- a/completions/dust.elv +++ b/completions/dust.elv @@ -50,6 +50,8 @@ set edit:completion:arg-completer[dust] = {|@words| cand --no-colors 'No colors will be printed (Useful for commands like: watch)' cand -b 'No percent bars or percentages will be displayed' cand --no-percent-bars 'No percent bars or percentages will be displayed' + cand -R 'For screen readers. Removes bars. Adds new column: depth level (May want to use -p too for full path)' + cand --screen-reader 'For screen readers. Removes bars. Adds new column: depth level (May want to use -p too for full path)' cand --skip-total 'No total row will be displayed' cand -f 'Directory ''size'' is number of child files/dirs not disk size' cand --filecount 'Directory ''size'' is number of child files/dirs not disk size' diff --git a/completions/dust.fish b/completions/dust.fish index c5bcfc6..526530f 100644 --- a/completions/dust.fish +++ b/completions/dust.fish @@ -14,6 +14,7 @@ complete -c dust -s s -l apparent-size -d 'Use file length instead of blocks' complete -c dust -s r -l reverse -d 'Print tree upside down (biggest highest)' complete -c dust -s c -l no-colors -d 'No colors will be printed (Useful for commands like: watch)' complete -c dust -s b -l no-percent-bars -d 'No percent bars or percentages will be displayed' +complete -c dust -s R -l screen-reader -d 'For screen readers. Removes bars. Adds new column: depth level (May want to use -p too for full path)' complete -c dust -l skip-total -d 'No total row will be displayed' complete -c dust -s f -l filecount -d 'Directory \'size\' is number of child files/dirs not disk size' complete -c dust -s i -l ignore_hidden -d 'Do not display hidden files' diff --git a/man-page/dust.1 b/man-page/dust.1 index 17f93e0..4ca7608 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\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] [\fB\-d\fR|\fB\-\-depth\fR] [\fB\-n\fR|\fB\-\-number\-of\-lines\fR] [\fB\-p\fR|\fB\-\-full\-paths\fR] [\fB\-X\fR|\fB\-\-ignore\-directory\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\-b\fR|\fB\-\-no\-percent\-bars\fR] [\fB\-z\fR|\fB\-\-min\-size\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\-H\fR|\fB\-\-si\fR] [\fB\-P\fR|\fB\-\-no\-progress\fR] [\fB\-D\fR|\fB\-\-only\-dir\fR] [\fB\-F\fR|\fB\-\-only\-file\fR] [\fIinputs\fR] +\fBDust\fR [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] [\fB\-d\fR|\fB\-\-depth\fR] [\fB\-n\fR|\fB\-\-number\-of\-lines\fR] [\fB\-p\fR|\fB\-\-full\-paths\fR] [\fB\-X\fR|\fB\-\-ignore\-directory\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\-b\fR|\fB\-\-no\-percent\-bars\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\-H\fR|\fB\-\-si\fR] [\fB\-P\fR|\fB\-\-no\-progress\fR] [\fB\-D\fR|\fB\-\-only\-dir\fR] [\fB\-F\fR|\fB\-\-only\-file\fR] [\fIinputs\fR] .SH DESCRIPTION Like du but more intuitive .SH OPTIONS @@ -48,6 +48,9 @@ No percent bars or percentages will be displayed \fB\-z\fR, \fB\-\-min\-size\fR Minimum size file to include in output .TP +\fB\-R\fR, \fB\-\-screen\-reader\fR +For screen readers. Removes bars. Adds new column: depth level (May want to use \-p too for full path) +.TP \fB\-\-skip\-total\fR No total row will be displayed .TP @@ -82,6 +82,12 @@ pub fn build_cli() -> Command<'static> { .help("Minimum size file to include in output"), ) .arg( + Arg::new("screen_reader") + .short('R') + .long("screen-reader") + .help("For screen readers. Removes bars. Adds new column: depth level (May want to use -p too for full path)"), + ) + .arg( Arg::new("skip_total") .long("skip-total") .help("No total row will be displayed"), diff --git a/src/config.rs b/src/config.rs index 91294ab..a85e159 100644 --- a/src/config.rs +++ b/src/config.rs @@ -16,6 +16,7 @@ pub struct Config { pub no_colors: Option<bool>, pub no_bars: Option<bool>, pub skip_total: Option<bool>, + pub screen_reader: Option<bool>, pub ignore_hidden: Option<bool>, pub iso: Option<bool>, pub min_size: Option<String>, @@ -55,6 +56,9 @@ impl Config { pub fn get_skip_total(&self, options: &ArgMatches) -> bool { Some(true) == self.skip_total || options.is_present("skip_total") } + pub fn get_screen_reader(&self, options: &ArgMatches) -> bool { + Some(true) == self.screen_reader || options.is_present("screen_reader") + } pub fn get_min_size(&self, options: &ArgMatches, iso: bool) -> Option<usize> { let size_from_param = options.value_of("min_size"); self._get_min_size(size_from_param, iso) diff --git a/src/display.rs b/src/display.rs index 944f23d..51ea3bf 100644 --- a/src/display.rs +++ b/src/display.rs @@ -22,6 +22,7 @@ pub struct DisplayData { pub is_reversed: bool, pub colors_on: bool, pub by_filecount: bool, + pub is_screen_reader: bool, pub num_chars_needed_on_left_most: usize, pub base_size: u64, pub longest_string_length: usize, @@ -83,6 +84,9 @@ impl DrawData<'_> { // TODO: can we test this? fn generate_bar(&self, node: &DisplayNode, level: usize) -> String { + if self.display_data.is_screen_reader { + return level.to_string(); + } let chars_in_bar = self.percent_bar.chars().count(); let num_bars = chars_in_bar as f32 * self.display_data.percent_size(node); let mut num_not_my_bar = (chars_in_bar as i32) - num_bars as i32; @@ -104,6 +108,7 @@ impl DrawData<'_> { } } +// TODO: Push these into one object ? #[allow(clippy::too_many_arguments)] pub fn draw_it( use_full_path: bool, @@ -115,6 +120,7 @@ pub fn draw_it( root_node: &DisplayNode, iso: bool, skip_total: bool, + is_screen_reader: bool, ) { let biggest = match skip_total { false => root_node, @@ -138,8 +144,13 @@ pub fn draw_it( let allowed_width = terminal_width - num_chars_needed_on_left_most - 2; let num_indent_chars = 3; - let longest_string_length = - find_longest_dir_name(root_node, num_indent_chars, allowed_width, !use_full_path); + let longest_string_length = find_longest_dir_name( + root_node, + num_indent_chars, + allowed_width, + !use_full_path, + is_screen_reader, + ); let max_bar_length = if no_percent_bars || longest_string_length + 7 >= allowed_width { 0 @@ -154,6 +165,7 @@ pub fn draw_it( is_reversed, colors_on: !no_colors, by_filecount, + is_screen_reader, num_chars_needed_on_left_most, base_size: biggest.size, longest_string_length, @@ -193,17 +205,23 @@ fn find_longest_dir_name( indent: usize, terminal: usize, long_paths: bool, + is_screen_reader: bool, ) -> usize { let printable_name = get_printable_name(&node.name, long_paths); - let longest = min( - UnicodeWidthStr::width(&*printable_name) + 1 + indent, - terminal, - ); + + let longest = if is_screen_reader { + UnicodeWidthStr::width(&*printable_name) + 1 + } else { + min( + UnicodeWidthStr::width(&*printable_name) + 1 + indent, + terminal, + ) + }; // each none root tree drawing is 2 more chars, hence we increment indent by 2 node.children .iter() - .map(|c| find_longest_dir_name(c, indent + 2, terminal, long_paths)) + .map(|c| find_longest_dir_name(c, indent + 2, terminal, long_paths, is_screen_reader)) .fold(longest, max) } @@ -314,14 +332,20 @@ fn maybe_trim_filename(name_in: String, indent: &str, display_data: &DisplayData pub fn format_string( node: &DisplayNode, indent: &str, - percent_bar: &str, + bars: &str, is_biggest: bool, display_data: &DisplayData, ) -> String { - let (percents, name_and_padding) = get_name_percent(node, indent, percent_bar, display_data); + let (percent, name_and_padding) = get_name_percent(node, indent, bars, display_data); let pretty_size = get_pretty_size(node, is_biggest, display_data); let pretty_name = get_pretty_name(node, name_and_padding, display_data); - format!("{pretty_size} {indent} {pretty_name}{percents}") + // we can clean this and the method below somehow, not sure yet + if display_data.is_screen_reader { + // if screen_reader then bars is 'depth' + format!("{pretty_name} {bars} {pretty_size}{percent}") + } else { + format!("{pretty_size} {indent} {pretty_name}{percent}") + } } fn get_name_percent( @@ -330,7 +354,14 @@ fn get_name_percent( bar_chart: &str, display_data: &DisplayData, ) -> (String, String) { - if !bar_chart.is_empty() { + if display_data.is_screen_reader { + let percent = display_data.percent_size(node) * 100.0; + let percent_size_str = format!("{percent:.0}%"); + let percents = format!(" {percent_size_str:>4}",); + let name = pad_or_trim_filename(node, "", display_data); + (percents, name) + // Bar chart being empty may come from either config or the screen not being wide enough + } else if !bar_chart.is_empty() { let percent = display_data.percent_size(node) * 100.0; let percent_size_str = format!("{percent:.0}%"); let percents = format!("│{bar_chart} │ {percent_size_str:>4}"); @@ -407,8 +438,9 @@ mod tests { is_reversed: false, colors_on: false, by_filecount: false, + is_screen_reader: false, num_chars_needed_on_left_most: 5, - base_size: 1, + base_size: 2_u64.pow(12), // 4.0K longest_string_length, ls_colors: LsColors::from_env().unwrap_or_default(), iso: false, @@ -425,14 +457,9 @@ mod tests { let indent = "┌─┴"; let percent_bar = ""; let is_biggest = false; + let data = get_fake_display_data(20); - let s = format_string( - &n, - indent, - percent_bar, - is_biggest, - &get_fake_display_data(20), - ); + let s = format_string(&n, indent, percent_bar, is_biggest, &data); assert_eq!(s, " 4.0K ┌─┴ short"); } @@ -448,8 +475,8 @@ mod tests { let percent_bar = ""; let is_biggest = false; - let dd = get_fake_display_data(64); - let s = format_string(&n, indent, percent_bar, is_biggest, &dd); + let data = get_fake_display_data(64); + let s = format_string(&n, indent, percent_bar, is_biggest, &data); assert_eq!( s, " 4.0K ┌─┴ very_long_name_longer_than_the_eighty_character_limit_very_.." @@ -457,6 +484,23 @@ mod tests { } #[test] + fn test_format_str_screen_reader() { + let n = DisplayNode { + name: PathBuf::from("/short"), + size: 2_u64.pow(12), // This is 4.0K + children: vec![], + }; + let indent = ""; + let percent_bar = "3"; + let is_biggest = false; + let mut data = get_fake_display_data(20); + data.is_screen_reader = true; + + let s = format_string(&n, indent, percent_bar, is_biggest, &data); + assert_eq!(s, "short 3 4.0K 100%"); + } + + #[test] fn test_human_readable_number() { assert_eq!(human_readable_number(1, false), "1B"); assert_eq!(human_readable_number(956, false), "956B"); diff --git a/src/main.rs b/src/main.rs index 5a0dcf5..e8df466 100644 --- a/src/main.rs +++ b/src/main.rs @@ -225,6 +225,7 @@ fn main() { &root_node, iso, config.get_skip_total(&options), + config.get_screen_reader(&options), ) } } diff --git a/tests/test_flags.rs b/tests/test_flags.rs index d291e87..cdf658a 100644 --- a/tests/test_flags.rs +++ b/tests/test_flags.rs @@ -138,6 +138,23 @@ pub fn test_output_skip_total() { } #[test] +pub fn test_output_screen_reader() { + let output = build_command(vec!["--screen-reader", "-c", "tests/test_dir/"]); + println!("{}", output); + assert!(output.contains("test_dir 0")); + assert!(output.contains("many 1")); + assert!(output.contains("hello_file 2")); + assert!(output.contains("a_file 2")); + + // Verify no 'symbols' reported by screen reader + assert!(!output.contains("│")); + + for block in ['█', '▓', '▒', '░'] { + assert!(!output.contains(block)); + } +} + +#[test] pub fn test_show_files_by_regex_match_lots() { // Check we can see '.rs' files in the tests directory let output = build_command(vec!["-c", "-e", "\\.rs$", "tests"]); |