diff --git a/Cargo.lock b/Cargo.lock index 3c6fd25b..7cddac68 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -215,6 +215,7 @@ dependencies = [ "clap_complete", "clap_mangen", "concat-string", + "const_format", "crossterm", "ctrlc", "dirs", @@ -360,6 +361,26 @@ dependencies = [ "cache-padded", ] +[[package]] +name = "const_format" +version = "0.2.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7309d9b4d3d2c0641e018d449232f2e28f1b22933c137f157d3dbc14228b8c0e" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f47bf7270cf70d370f8f98c1abb6d2d4cf60a6845d30e05bfb90c6568650" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + [[package]] name = "core-foundation" version = "0.7.0" diff --git a/Cargo.toml b/Cargo.toml index 7e7f932d..36bbc299 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -64,6 +64,7 @@ anyhow = "1.0.57" backtrace = "0.3.65" cfg-if = "1.0.0" clap = { version = "3.1.12", features = ["default", "cargo", "wrap_help"] } +const_format = "0.2.30" concat-string = "1.0.1" crossterm = "0.25.0" ctrlc = { version = "3.1.9", features = ["termination"] } diff --git a/src/app/widgets/process_table.rs b/src/app/widgets/process_table.rs index b4fabc7a..6cd1bda9 100644 --- a/src/app/widgets/process_table.rs +++ b/src/app/widgets/process_table.rs @@ -1,5 +1,6 @@ use std::{borrow::Cow, collections::BTreeMap}; +use const_format::formatcp; use fxhash::{FxHashMap, FxHashSet}; use itertools::Itertools; @@ -385,7 +386,10 @@ impl ProcWidget { .collect_vec(); stack.sort_unstable_by_key(|p| p.pid); - self.try_sort_skip_pid_asc(&mut stack); + + let column = self.table.columns.get(self.table.sort_index()).unwrap(); + sort_skip_pid_asc(column.inner(), &mut stack, self.table.order()); + stack.reverse(); let mut length_stack = vec![stack.len()]; @@ -449,14 +453,13 @@ impl ProcWidget { data.push(process.prefix(Some(prefix)).disabled(disabled)); if let Some(children_pids) = filtered_tree.get(&pid) { - // TODO: Can probably use static strings for prefixes rather than allocating. if prefixes.is_empty() { - prefixes.push(String::default()); + prefixes.push(""); } else { prefixes.push(if is_last { - " ".to_string() + " " } else { - format!("{} ", BRANCH_VERTICAL) + formatcp!("{} ", BRANCH_VERTICAL) }); } @@ -468,7 +471,9 @@ impl ProcWidget { }) }) .collect_vec(); - self.try_rev_sort(&mut children); + + column.sort_by(&mut children, self.table.order().rev()); + length_stack.push(children.len()); stack.extend(children); } @@ -548,33 +553,14 @@ impl ProcWidget { }; self.id_pid_map = id_pid_map; - self.try_sort_skip_pid_asc(&mut filtered_data); + + if let Some(column) = self.table.columns.get(self.table.sort_index()) { + sort_skip_pid_asc(column.inner(), &mut filtered_data, self.table.order()); + } + filtered_data } - #[inline(always)] - fn try_rev_sort(&self, filtered_data: &mut [ProcWidgetData]) { - if let Some(column) = self.table.columns.get(self.table.sort_index()) { - column.sort_by( - filtered_data, - match self.table.order() { - SortOrder::Ascending => SortOrder::Descending, - SortOrder::Descending => SortOrder::Ascending, - }, - ); - } - } - - #[inline(always)] - fn try_sort_skip_pid_asc(&self, filtered_data: &mut [ProcWidgetData]) { - if let Some(column) = self.table.columns.get(self.table.sort_index()) { - let column = column.inner(); - let descending = matches!(self.table.order(), SortOrder::Descending); - - sort_skip_pid_asc(column, filtered_data, descending); - } - } - #[inline(always)] fn get_mut_proc_col(&mut self, index: usize) -> Option<&mut ProcColumn> { self.table.columns.get_mut(index).map(|col| col.inner_mut()) @@ -862,7 +848,9 @@ impl ProcWidget { } } -fn sort_skip_pid_asc(column: &ProcColumn, data: &mut [ProcWidgetData], descending: bool) { +#[inline] +fn sort_skip_pid_asc(column: &ProcColumn, data: &mut [ProcWidgetData], order: SortOrder) { + let descending = matches!(order, SortOrder::Descending); match column { ProcColumn::Pid if !descending => {} _ => { @@ -876,11 +864,6 @@ mod test { use super::*; use crate::app::widgets::MemUsage; - #[test] - fn sorting_trees() { - // FIXME: Add a test for this... - } - #[test] fn test_proc_sort() { let a = ProcWidgetData { @@ -903,6 +886,7 @@ mod test { let b = ProcWidgetData { pid: 2, + ppid: Some(1), id: "B".into(), cpu_usage_percent: 1.1, mem_usage: MemUsage::Percent(2.2), @@ -911,6 +895,7 @@ mod test { let c = ProcWidgetData { pid: 3, + ppid: Some(1), id: "C".into(), cpu_usage_percent: 2.2, mem_usage: MemUsage::Percent(0.0), @@ -919,17 +904,17 @@ mod test { let d = ProcWidgetData { pid: 4, + ppid: Some(2), id: "D".into(), cpu_usage_percent: 0.0, mem_usage: MemUsage::Percent(0.0), ..(a.clone()) }; - let mut data = vec![d.clone(), b.clone(), c.clone(), a.clone()]; // Assume we had sorted over by pid. data.sort_by_key(|p| p.pid); - sort_skip_pid_asc(&ProcColumn::CpuPercent, &mut data, true); + sort_skip_pid_asc(&ProcColumn::CpuPercent, &mut data, SortOrder::Descending); assert_eq!( vec![&c, &b, &a, &d] .iter() @@ -940,7 +925,7 @@ mod test { // Note that the PID ordering for ties is still ascending. data.sort_by_key(|p| p.pid); - sort_skip_pid_asc(&ProcColumn::CpuPercent, &mut data, false); + sort_skip_pid_asc(&ProcColumn::CpuPercent, &mut data, SortOrder::Ascending); assert_eq!( vec![&a, &d, &b, &c] .iter() @@ -950,7 +935,7 @@ mod test { ); data.sort_by_key(|p| p.pid); - sort_skip_pid_asc(&ProcColumn::MemoryPercent, &mut data, true); + sort_skip_pid_asc(&ProcColumn::MemoryPercent, &mut data, SortOrder::Descending); assert_eq!( vec![&b, &a, &c, &d] .iter() @@ -961,7 +946,7 @@ mod test { // Note that the PID ordering for ties is still ascending. data.sort_by_key(|p| p.pid); - sort_skip_pid_asc(&ProcColumn::MemoryPercent, &mut data, false); + sort_skip_pid_asc(&ProcColumn::MemoryPercent, &mut data, SortOrder::Ascending); assert_eq!( vec![&c, &d, &a, &b] .iter() diff --git a/src/canvas/widgets/process_table.rs b/src/canvas/widgets/process_table.rs index a2e66da1..0c1ac693 100644 --- a/src/canvas/widgets/process_table.rs +++ b/src/canvas/widgets/process_table.rs @@ -213,21 +213,17 @@ impl Painter { // TODO: [MOUSE] Mouse support for these in search // TODO: [MOVEMENT] Movement support for these in search + let (case, whole, regex) = if self.is_mac_os { + ("Case(F1)", "Whole(F2)", "Regex(F3)") + } else { + ("Case(Alt+C)", "Whole(Alt+W)", "Regex(Alt+R)") + }; let option_text = Spans::from(vec![ - Span::styled( - format!("Case({})", if self.is_mac_os { "F1" } else { "Alt+C" }), - case_style, - ), + Span::styled(case, case_style), Span::raw(" "), - Span::styled( - format!("Whole({})", if self.is_mac_os { "F2" } else { "Alt+W" }), - whole_word_style, - ), + Span::styled(whole, whole_word_style), Span::raw(" "), - Span::styled( - format!("Regex({})", if self.is_mac_os { "F3" } else { "Alt+R" }), - regex_style, - ), + Span::styled(regex, regex_style), ]); search_text.push(Spans::from(Span::styled( diff --git a/src/components/data_table/sortable.rs b/src/components/data_table/sortable.rs index f5343b85..06f3a86b 100644 --- a/src/components/data_table/sortable.rs +++ b/src/components/data_table/sortable.rs @@ -17,6 +17,16 @@ pub enum SortOrder { Descending, } +impl SortOrder { + /// Returns the reverse [`SortOrder`]. + pub fn rev(&self) -> SortOrder { + match self { + SortOrder::Ascending => SortOrder::Descending, + SortOrder::Descending => SortOrder::Ascending, + } + } +} + impl Default for SortOrder { fn default() -> Self { Self::Ascending