Compare commits

...

6 Commits

Author SHA1 Message Date
andy.boot
d7fa260bba hack 2025-07-07 23:11:17 +01:00
andy.boot
5057337340 hack 2025-07-07 22:19:16 +01:00
andy.boot
9957cd3d9c hack 2025-07-06 10:17:26 +01:00
andy.boot
69ba3adcfc hack 2025-07-06 09:54:30 +01:00
andy.boot
fff6695798 hack 2025-07-06 09:54:29 +01:00
andy.boot
a78c80ac1c hack 2025-07-06 09:53:43 +01:00
4 changed files with 234 additions and 25 deletions

35
Cargo.lock generated
View File

@@ -344,6 +344,7 @@ dependencies = [
"sysinfo",
"tempfile",
"terminal_size",
"termion",
"thousands",
"unicode-width",
"winapi-util",
@@ -486,6 +487,7 @@ checksum = "1580801010e535496706ba011c15f8532df6b42297d2e471fec38ceadd8c0638"
dependencies = [
"bitflags 2.9.1",
"libc",
"redox_syscall",
]
[[package]]
@@ -562,6 +564,12 @@ dependencies = [
"autocfg",
]
[[package]]
name = "numtoa"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6aa2c4e539b869820a2b82e1aef6ff40aa85e65decdd5185e83fb4b1249cd00f"
[[package]]
name = "once_cell"
version = "1.21.3"
@@ -657,6 +665,21 @@ dependencies = [
"crossbeam-utils",
]
[[package]]
name = "redox_syscall"
version = "0.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6"
dependencies = [
"bitflags 2.9.1",
]
[[package]]
name = "redox_termios"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20145670ba436b55d91fc92d25e71160fbfbdd57831631c8d7d36377a476f1cb"
[[package]]
name = "redox_users"
version = "0.4.6"
@@ -841,6 +864,18 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "termion"
version = "4.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3669a69de26799d6321a5aa713f55f7e2cd37bd47be044b50f2acafc42c122bb"
dependencies = [
"libc",
"libredox",
"numtoa",
"redox_termios",
]
[[package]]
name = "termtree"
version = "0.5.1"

View File

@@ -43,6 +43,7 @@ directories = "4"
sysinfo = "0.27"
ctrlc = "3.4"
chrono = "0.4"
termion="4"
[target.'cfg(not(target_has_atomic = "64"))'.dependencies]
portable-atomic = "1.4"

View File

@@ -4,6 +4,7 @@ use crate::node::FileTime;
use ansi_term::Colour::Red;
use lscolors::{LsColors, Style};
use termion::raw::RawTerminal;
use unicode_width::UnicodeWidthStr;
use stfu8::encode_u8;
@@ -12,6 +13,8 @@ use chrono::{DateTime, Local, TimeZone, Utc};
use std::cmp::max;
use std::cmp::min;
use std::fs;
use std::io::Stdout;
use std::io::Write;
use std::iter::repeat_n;
use std::path::Path;
use thousands::Separable;
@@ -29,6 +32,7 @@ pub struct InitialDisplayData {
pub is_screen_reader: bool,
pub output_format: String,
pub bars_on_right: bool,
pub selected_index: i32,
}
pub struct DisplayData {
@@ -81,6 +85,7 @@ struct DrawData<'a> {
display_data: &'a DisplayData,
}
impl DrawData<'_> {
fn get_new_indent(&self, has_children: bool, was_i_last: bool) -> String {
let chars = self.display_data.get_tree_chars(was_i_last, has_children);
@@ -129,6 +134,7 @@ pub fn draw_it(
no_percent_bars: bool,
terminal_width: usize,
skip_total: bool,
stdout: &mut RawTerminal<Stdout>,
) {
let num_chars_needed_on_left_most = if idd.by_filecount {
let max_size = root_node.size;
@@ -170,8 +176,14 @@ pub fn draw_it(
display_data: &display_data,
};
let mut test = if display_data.initial.is_reversed {
recursive_child_count(root_node)
} else {
0
};
if !skip_total {
display_node(root_node, &draw_data, true, true);
display_node(root_node, stdout, &draw_data, true, true, test);
} else {
for (count, c) in root_node
.get_children_from_node(draw_data.display_data.initial.is_reversed)
@@ -179,7 +191,13 @@ pub fn draw_it(
{
let is_biggest = display_data.is_biggest(count, root_node.num_siblings());
let was_i_last = display_data.is_last(count, root_node.num_siblings());
display_node(c, &draw_data, is_biggest, was_i_last);
display_node(c, stdout, &draw_data, is_biggest, was_i_last, test);
// not yet tested:
if display_data.initial.is_reversed {
test += recursive_child_count(c);
} else {
test += recursive_child_count(c);
}
}
}
}
@@ -218,16 +236,46 @@ fn find_longest_dir_name(
.fold(longest, max)
}
fn display_node(node: &DisplayNode, draw_data: &DrawData, is_biggest: bool, is_last: bool) {
pub fn recursive_child_count(node: &DisplayNode) -> i32 {
let mut total = 1;
for n in node.children.iter() {
total += recursive_child_count(&n);
}
return total;
}
fn display_node(
node: &DisplayNode,
stdout: &mut RawTerminal<Stdout>,
draw_data: &DrawData,
is_biggest: bool,
is_last: bool,
test: i32,
) {
// hacky way of working out how deep we are in the tree
let indent = draw_data.get_new_indent(!node.children.is_empty(), is_last);
let level = ((indent.chars().count() - 1) / 2) - 1;
let bar_text = draw_data.generate_bar(node, level);
let to_print = format_string(node, &indent, &bar_text, is_biggest, draw_data.display_data);
let cnt = if draw_data.display_data.initial.is_reversed {
recursive_child_count(node)
} else {
0
};
let to_print = format_string(
node,
&indent,
&bar_text,
is_biggest,
&draw_data.display_data,
test - cnt,
);
let mut tt = test;
if !draw_data.display_data.initial.is_reversed {
println!("{to_print}")
tt += 1;
write!(stdout, "{to_print}").unwrap()
}
let dd = DrawData {
@@ -244,11 +292,17 @@ fn display_node(node: &DisplayNode, draw_data: &DrawData, is_biggest: bool, is_l
{
let is_biggest = dd.display_data.is_biggest(count, num_siblings);
let was_i_last = dd.display_data.is_last(count, num_siblings);
display_node(c, &dd, is_biggest, was_i_last);
display_node(c, stdout, &dd, is_biggest, was_i_last, tt);
if draw_data.display_data.initial.is_reversed {
tt -= recursive_child_count(c)
} else {
tt += recursive_child_count(c)
}
}
if draw_data.display_data.initial.is_reversed {
println!("{to_print}")
write!(stdout, "{to_print}").unwrap()
}
}
@@ -325,18 +379,22 @@ pub fn format_string(
bars: &str,
is_biggest: bool,
display_data: &DisplayData,
test: i32,
) -> String {
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_size = get_pretty_size(node, is_biggest, display_data, test);
let pretty_name = get_pretty_name(node, name_and_padding, display_data);
let marked = get_name_if_marked(test==display_data.initial.selected_index, pretty_name);
let indent = get_indent_if_marked(test==display_data.initial.selected_index, indent);
// we can clean this and the method below somehow, not sure yet
if display_data.initial.is_screen_reader {
// if screen_reader then bars is 'depth'
format!("{pretty_name} {bars} {pretty_size}{percent}")
format!("{marked} {bars} {pretty_size}{percent}")
} else if display_data.initial.by_filetime.is_some() {
format!("{pretty_size} {indent}{pretty_name}")
format!("{pretty_size} {indent}{marked}")
} else {
format!("{pretty_size} {indent} {pretty_name}{percent}")
format!("{pretty_size} {indent} {marked}{percent}")
}
}
@@ -366,13 +424,19 @@ fn get_name_percent(
}
}
fn get_pretty_size(node: &DisplayNode, is_biggest: bool, display_data: &DisplayData) -> String {
fn get_pretty_size(
node: &DisplayNode,
is_biggest: bool,
display_data: &DisplayData,
n: i32,
) -> String {
let output = if display_data.initial.by_filecount {
node.size.separate_with_commas()
} else if display_data.initial.by_filetime.is_some() {
get_pretty_file_modified_time(node.size as i64)
} else {
human_readable_number(node.size, &display_data.initial.output_format)
// human_readable_number(n, &display_data.initial.output_format)
format!("{n}")
};
let spaces_to_add = display_data.num_chars_needed_on_left_most - output.chars().count();
let output = " ".repeat(spaces_to_add) + output.as_str();
@@ -392,6 +456,33 @@ fn get_pretty_file_modified_time(timestamp: i64) -> String {
local_datetime.format("%Y-%m-%dT%H:%M:%S").to_string()
}
fn get_indent_if_marked(test: bool, indent: &str) -> String {
if test {
let mut new_name = String::new();
for _ in indent.chars() {
new_name.push(BLOCKS[0])
}
new_name
} else {
indent.into()
}
}
fn get_name_if_marked(test: bool, name: String) -> String {
if test {
let mut new_name = String::new();
for c in name.chars() {
if c == ' ' {
new_name.push(BLOCKS[0])
} else {
new_name.push(c)
}
}
new_name
} else {
name.into()
}
}
fn get_pretty_name(
node: &DisplayNode,
name_and_padding: String,

View File

@@ -12,6 +12,7 @@ mod utils;
use crate::cli::Cli;
use crate::config::Config;
use crate::display::recursive_child_count;
use crate::display_node::DisplayNode;
use crate::progress::RuntimeErrors;
use clap::Parser;
@@ -20,17 +21,27 @@ use display::InitialDisplayData;
use filter::AggregateData;
use progress::PIndicator;
use regex::Error;
use std::cmp::min;
use std::collections::HashSet;
use std::env;
use std::fs::read_to_string;
use std::io;
use std::io::Stdout;
use std::io::stdin;
use std::io::stdout;
use std::panic;
use std::process;
use std::sync::Arc;
use std::sync::Mutex;
use sysinfo::{System, SystemExt};
use termion::raw::RawTerminal;
use utils::canonicalize_absolute_path;
use std::io::Write;
use termion::event::{Event, Key};
use termion::input::TermRead;
use termion::raw::IntoRawMode;
use self::display::draw_it;
use config::get_config;
use dir_walker::walk_it;
@@ -309,24 +320,93 @@ fn main() {
let print_errors = config.get_print_errors(&options);
print_any_errors(print_errors, walk_data.errors);
let stdin = stdin();
let mut out = stdout().into_raw_mode().unwrap();
write!(
out,
"{}{}Dust interactive (q to quit)",
termion::clear::All,
termion::cursor::Goto(1, 1)
)
.unwrap();
write!(out, "{}", termion::cursor::Goto(1, 2)).unwrap();
print_output(
config,
options,
tree,
walk_data.by_filecount,
&config,
&options,
&tree,
is_colors,
terminal_width,
)
});
&mut out,
0,
);
out.flush().unwrap();
let mut state = 0;
for c in stdin.events() {
write!(
out,
"{}{}Dust interactive (q to quit) {state}",
termion::clear::All,
termion::cursor::Goto(1, 1)
)
.unwrap();
write!(out, "{}", termion::cursor::Goto(1, 2)).unwrap();
let evt = c.unwrap();
match evt {
Event::Key(Key::Char('q')) => break,
Event::Key(Key::Up | Key::Char('k')) => {
write!(out, "up\n").unwrap();
if !config.get_reverse(&options){
state += 1;
} else {
state -= 1;
}
}
Event::Key(Key::Down | Key::Char('j')) => {
write!(out, "down\n").unwrap();
if !config.get_reverse(&options){
state -= 1;
} else {
state += 1;
}
}
Event::Key(Key::Left | Key::Char('h')) => {
write!(out, "left\n").unwrap();
}
Event::Key(Key::Right | Key::Char('l')) => {
write!(out, "right\n").unwrap();
}
Event::Key(Key::Char(x)) => {
write!(out, "{x} key\n").unwrap();
}
_ => {}
}
state = max(0, state);
state = min(recursive_child_count(&tree)-1, state);
write!(out, "{}", termion::cursor::Goto(1, 3)).unwrap();
print_output(
&config,
&options,
&tree,
is_colors,
terminal_width,
&mut out,
state,
);
out.flush().unwrap();
}
})
}
fn print_output(
config: Config,
options: Cli,
tree: DisplayNode,
by_filecount: bool,
config: &Config,
options: &Cli,
tree: &DisplayNode,
is_colors: bool,
terminal_width: usize,
stdout: &mut RawTerminal<Stdout>,
selected_index: i32,
) {
let output_format = config.get_output_format(&options);
@@ -340,19 +420,21 @@ fn print_output(
short_paths: !config.get_full_paths(&options),
is_reversed: !config.get_reverse(&options),
colors_on: is_colors,
by_filecount,
by_filecount: options.filecount,
by_filetime: config.get_filetime(&options),
is_screen_reader: config.get_screen_reader(&options),
output_format,
bars_on_right: config.get_bars_on_right(&options),
selected_index: selected_index,
};
draw_it(
idd,
&tree,
tree,
config.get_no_bars(&options),
terminal_width,
config.get_skip_total(&options),
stdout,
)
}
}