mirror of
https://github.com/ClementTsang/bottom.git
synced 2025-07-25 06:35:07 +02:00
change: show process in tree if any ancestor or descendent matches (#1026)
* refactor: optimize kept list in tree to just store filtered values in a set * change: show all direct children of a tree process children if the parent matches * change: show process in tree if any ancestor or descendent matches
This commit is contained in:
parent
6d15f01009
commit
f1f07945f6
@ -149,6 +149,8 @@ pub fn handle_key_event_or_break(
|
|||||||
KeyCode::Char('c') | KeyCode::Char('C') => app.toggle_ignore_case(),
|
KeyCode::Char('c') | KeyCode::Char('C') => app.toggle_ignore_case(),
|
||||||
KeyCode::Char('w') | KeyCode::Char('W') => app.toggle_search_whole_word(),
|
KeyCode::Char('w') | KeyCode::Char('W') => app.toggle_search_whole_word(),
|
||||||
KeyCode::Char('r') | KeyCode::Char('R') => app.toggle_search_regex(),
|
KeyCode::Char('r') | KeyCode::Char('R') => app.toggle_search_regex(),
|
||||||
|
// KeyCode::Char('b') | KeyCode::Char('B') => todo!(),
|
||||||
|
// KeyCode::Char('f') | KeyCode::Char('F') => todo!(),
|
||||||
KeyCode::Char('h') => app.on_left_key(),
|
KeyCode::Char('h') => app.on_left_key(),
|
||||||
KeyCode::Char('l') => app.on_right_key(),
|
KeyCode::Char('l') => app.on_right_key(),
|
||||||
_ => {}
|
_ => {}
|
||||||
|
@ -289,25 +289,50 @@ impl ProcWidgetState {
|
|||||||
..
|
..
|
||||||
} = &data_collection.process_data;
|
} = &data_collection.process_data;
|
||||||
|
|
||||||
|
// Only keep a set of the kept PIDs.
|
||||||
let kept_pids = data_collection
|
let kept_pids = data_collection
|
||||||
.process_data
|
.process_data
|
||||||
.process_harvest
|
.process_harvest
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(pid, process)| {
|
.filter_map(|(pid, process)| {
|
||||||
(
|
if search_query
|
||||||
*pid,
|
.as_ref()
|
||||||
search_query
|
.map(|q| q.check(process, is_using_command))
|
||||||
.as_ref()
|
.unwrap_or(true)
|
||||||
.map(|q| q.check(process, is_using_command))
|
{
|
||||||
.unwrap_or(true),
|
Some(*pid)
|
||||||
)
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.collect::<FxHashMap<_, _>>();
|
.collect::<FxHashSet<_>>();
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_ancestor_shown(
|
||||||
|
current_process: &ProcessHarvest, kept_pids: &FxHashSet<Pid>,
|
||||||
|
process_harvest: &BTreeMap<Pid, ProcessHarvest>,
|
||||||
|
) -> bool {
|
||||||
|
if let Some(ppid) = current_process.parent_pid {
|
||||||
|
if kept_pids.contains(&ppid) {
|
||||||
|
true
|
||||||
|
} else if let Some(parent) = process_harvest.get(&ppid) {
|
||||||
|
is_ancestor_shown(parent, kept_pids, process_harvest)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A process is shown under the filtered tree if at least one of these conditions hold:
|
||||||
|
// - The process itself matches.
|
||||||
|
// - The process contains some descendant that matches.
|
||||||
|
// - The process's parent (and only parent, not any ancestor) matches.
|
||||||
let filtered_tree = {
|
let filtered_tree = {
|
||||||
let mut filtered_tree = FxHashMap::default();
|
let mut filtered_tree = FxHashMap::default();
|
||||||
|
|
||||||
// We do a simple BFS traversal to build our filtered parent-to-tree mappings.
|
// We do a simple DFS traversal to build our filtered parent-to-tree mappings.
|
||||||
let mut visited_pids = FxHashMap::default();
|
let mut visited_pids = FxHashMap::default();
|
||||||
let mut stack = orphan_pids
|
let mut stack = orphan_pids
|
||||||
.iter()
|
.iter()
|
||||||
@ -315,7 +340,7 @@ impl ProcWidgetState {
|
|||||||
.collect_vec();
|
.collect_vec();
|
||||||
|
|
||||||
while let Some(process) = stack.last() {
|
while let Some(process) = stack.last() {
|
||||||
let is_process_matching = *kept_pids.get(&process.pid).unwrap_or(&false);
|
let is_process_matching = kept_pids.contains(&process.pid);
|
||||||
|
|
||||||
if let Some(children_pids) = process_parent_mapping.get(&process.pid) {
|
if let Some(children_pids) = process_parent_mapping.get(&process.pid) {
|
||||||
if children_pids
|
if children_pids
|
||||||
@ -326,7 +351,14 @@ impl ProcWidgetState {
|
|||||||
.iter()
|
.iter()
|
||||||
.filter(|pid| visited_pids.get(*pid).copied().unwrap_or(false))
|
.filter(|pid| visited_pids.get(*pid).copied().unwrap_or(false))
|
||||||
.collect_vec();
|
.collect_vec();
|
||||||
let is_shown = is_process_matching || !shown_children.is_empty();
|
|
||||||
|
// Show the entry if it is:
|
||||||
|
// - Matches the filter.
|
||||||
|
// - Has at least one child (doesn't have to be direct) that matches the filter.
|
||||||
|
// - Is the child of a shown process.
|
||||||
|
let is_shown = is_process_matching
|
||||||
|
|| !shown_children.is_empty()
|
||||||
|
|| is_ancestor_shown(process, &kept_pids, process_harvest);
|
||||||
visited_pids.insert(process.pid, is_shown);
|
visited_pids.insert(process.pid, is_shown);
|
||||||
|
|
||||||
if is_shown {
|
if is_shown {
|
||||||
@ -352,11 +384,14 @@ impl ProcWidgetState {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if is_process_matching {
|
let is_shown = is_process_matching
|
||||||
|
|| is_ancestor_shown(process, &kept_pids, process_harvest);
|
||||||
|
|
||||||
|
if is_shown {
|
||||||
filtered_tree.insert(process.pid, vec![]);
|
filtered_tree.insert(process.pid, vec![]);
|
||||||
}
|
}
|
||||||
|
|
||||||
visited_pids.insert(process.pid, is_process_matching);
|
visited_pids.insert(process.pid, is_shown);
|
||||||
stack.pop();
|
stack.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -384,14 +419,13 @@ impl ProcWidgetState {
|
|||||||
let column = self.table.columns.get(self.table.sort_index()).unwrap();
|
let column = self.table.columns.get(self.table.sort_index()).unwrap();
|
||||||
sort_skip_pid_asc(column.inner(), &mut stack, self.table.order());
|
sort_skip_pid_asc(column.inner(), &mut stack, self.table.order());
|
||||||
|
|
||||||
stack.reverse();
|
|
||||||
|
|
||||||
let mut length_stack = vec![stack.len()];
|
let mut length_stack = vec![stack.len()];
|
||||||
|
stack.reverse();
|
||||||
|
|
||||||
while let (Some(process), Some(siblings_left)) = (stack.pop(), length_stack.last_mut()) {
|
while let (Some(process), Some(siblings_left)) = (stack.pop(), length_stack.last_mut()) {
|
||||||
*siblings_left -= 1;
|
*siblings_left -= 1;
|
||||||
|
|
||||||
let disabled = !*kept_pids.get(&process.pid).unwrap_or(&false);
|
let disabled = !kept_pids.contains(&process.pid);
|
||||||
let is_last = *siblings_left == 0;
|
let is_last = *siblings_left == 0;
|
||||||
|
|
||||||
if collapsed_pids.contains(&process.pid) {
|
if collapsed_pids.contains(&process.pid) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user