Skip to content

Commit

Permalink
fix: use id for queries
Browse files Browse the repository at this point in the history
  • Loading branch information
kxxt committed Jan 11, 2025
1 parent ba3beca commit 8ca4a4c
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 49 deletions.
20 changes: 19 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ tui-popup = "0.6.0"
thiserror = "1.0.59"
tui-scrollview = "0.5.0"
bitflags = "2.5.0"
indexmap = "2.2.6"
tui-prompts = "0.5.0"
unicode-segmentation = "1.11.0"
unicode-width = "0.1.12"
Expand All @@ -81,6 +80,7 @@ libseccomp = { version = "0.3.0", optional = true }
weak-table = { version = "0.3.2", default-features = false, features = ["ahash"] }
rand = "0.8.5"
hashbrown = "0.15.2"
indexset = "0.10.0"
# tui-prompts = { version = "0.3.11", path = "../../contrib/tui-prompts" }
# tui-popup = { version = "0.3.0", path = "../../contrib/tui-popup" }

Expand Down
1 change: 0 additions & 1 deletion src/tui/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,6 @@ impl App {
debug!("Received tracee spawn event: {pid}");
self.root_pid = Some(*pid);
}
debug_assert_eq!(e.id, self.event_list.len() as u64);
self.event_list.push(e.id, e.details);
if self.event_list.is_following() {
action_tx.send(Action::ScrollToBottom)?;
Expand Down
72 changes: 50 additions & 22 deletions src/tui/event_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
use std::{cell::RefCell, collections::VecDeque, rc::Rc, sync::Arc};

use hashbrown::HashMap;
use indexmap::IndexMap;
use nix::sys::signal;
use ratatui::{
layout::Alignment::Right,
Expand Down Expand Up @@ -251,15 +250,14 @@ impl Widget for &mut EventList {
let items = self
.events
.iter()
.enumerate()
.skip(self.window.0)
.take(self.window.1 - self.window.0)
.map(|(i, event)| {
.map(|event| {
max_len = max_len.max(event.borrow().event_line.line.width());
let highlighted = self
.query_result
.as_ref()
.is_some_and(|query_result| query_result.indices.contains_key(&i));
.is_some_and(|query_result| query_result.indices.contains(&event.borrow().id));
let mut base = event
.borrow()
.event_line
Expand Down Expand Up @@ -362,49 +360,55 @@ impl EventList {
let Some(query) = self.query.as_ref() else {
return;
};
let mut indices = IndexMap::new();
let mut indices = indexset::BTreeSet::new();
// Events won't change during the search because this is Rust and we already have a reference to it.
// Rust really makes the code more easier to reason about.
let searched_len = self.events.len();
for (i, evt) in self.events.iter().enumerate() {
for evt in self.events.iter() {
if query.matches(&evt.borrow().event_line) {
indices.insert(i, 0);
indices.insert(evt.borrow().id);
}
}
let mut result = QueryResult {
indices,
searched_len,
searched_id: self
.events
.iter()
.last()
.map(|r| r.borrow().id)
.unwrap_or(0),
selection: None,
};
result.next_result();
let selection = result.selection();
self.query_result = Some(result);
self.should_refresh_list_cache = true;
self.scroll_to(selection);
self.scroll_to_id(selection);
}

/// Incremental search for newly added events
pub fn incremental_search(&mut self) {
let Some(query) = self.query.as_ref() else {
return;
};
let offset = self.id_index_offset();
let Some(existing_result) = self.query_result.as_mut() else {
self.search();
return;
};
let mut modified = false;
for (i, evt) in self
.events
.iter()
.enumerate()
.skip(existing_result.searched_len)
{
let start_search_index = existing_result.searched_id.saturating_sub(offset) as usize;
for evt in self.events.iter().skip(start_search_index) {
if query.matches(&evt.borrow().event_line) {
existing_result.indices.insert(i, 0);
existing_result.indices.insert(evt.borrow().id);
modified = true;
}
}
existing_result.searched_len = self.events.len();
existing_result.searched_id = self
.events
.iter()
.last()
.map(|r| r.borrow().id)
.unwrap_or(0);
if modified {
self.should_refresh_list_cache = true;
}
Expand All @@ -414,17 +418,17 @@ impl EventList {
if let Some(query_result) = self.query_result.as_mut() {
query_result.next_result();
let selection = query_result.selection();
self.scroll_to(selection);
self.stop_follow();
self.scroll_to_id(selection);
}
}

pub fn prev_match(&mut self) {
if let Some(query_result) = self.query_result.as_mut() {
query_result.prev_result();
let selection = query_result.selection();
self.scroll_to(selection);
self.stop_follow();
self.scroll_to_id(selection);
}
}
}
Expand Down Expand Up @@ -455,7 +459,11 @@ impl EventList {
};
if self.events.len() >= self.max_events as usize {
if let Some(e) = self.events.pop_front() {
self.event_map.remove(&e.borrow().id);
let id = e.borrow().id;
self.event_map.remove(&id);
if let Some(q) = &mut self.query_result {
q.indices.remove(&id);
}
}
self.should_refresh_list_cache = true;
}
Expand Down Expand Up @@ -487,7 +495,9 @@ impl EventList {
if let Some(e) = self.event_map.get(&i) {
let mut e = e.borrow_mut();
e.status = match update.update {
ProcessStateUpdate::Exit(ProcessExit::Code(0)) => Some(EventStatus::ProcessExitedNormally),
ProcessStateUpdate::Exit(ProcessExit::Code(0)) => {
Some(EventStatus::ProcessExitedNormally)
}
ProcessStateUpdate::Exit(ProcessExit::Code(c)) => {
Some(EventStatus::ProcessExitedAbnormally(c))
}
Expand Down Expand Up @@ -543,6 +553,24 @@ impl EventList {

/// Scrolling implementation for the EventList
impl EventList {
fn id_index_offset(&self) -> u64 {
self
.events
.get(self.window.0)
.map(|e| e.borrow().id)
.unwrap_or(0)
.saturating_sub(self.window.0 as u64)
}

fn scroll_to_id(&mut self, id: Option<u64>) {
let Some(id) = id else {
return;
};
// self.window.0 should be <= its id

self.scroll_to(Some((id - self.id_index_offset()) as usize));
}

/// Scroll to the given index and select it,
/// Usually the item will be at the top of the window,
/// but if there are not enough items or the item is already in current window,
Expand Down
41 changes: 17 additions & 24 deletions src/tui/query.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use std::error::Error;

use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use indexmap::IndexMap;
use itertools::Itertools;
use ratatui::{
style::Styled,
Expand Down Expand Up @@ -37,11 +36,11 @@ pub enum QueryKind {
#[derive(Debug)]
pub struct QueryResult {
/// The indices of matching events and the start of the match, use IndexMap to keep the order
pub indices: IndexMap<usize, usize>,
/// The length of all searched items, used to implement incremental query
pub searched_len: usize,
pub indices: indexset::BTreeSet<u64>,
/// The maximum of searched id
pub searched_id: u64,
/// The currently focused item in query result, an index of `indices`
pub selection: Option<usize>,
pub selection: Option<u64>,
}

impl Query {
Expand Down Expand Up @@ -82,35 +81,29 @@ impl Query {
impl QueryResult {
pub fn next_result(&mut self) {
if let Some(selection) = self.selection {
if selection + 1 < self.indices.len() {
self.selection = Some(selection + 1);
} else {
// If the current selection is the last one, loop back to the first one
self.selection = Some(0)
self.selection = match self.indices.range((selection + 1)..).next() {
Some(id) => Some(*id),
None => self.indices.first().copied(),
}
} else if !self.indices.is_empty() {
self.selection = Some(0);
self.selection = self.indices.first().copied();
}
}

pub fn prev_result(&mut self) {
if let Some(selection) = self.selection {
if selection > 0 {
self.selection = Some(selection - 1);
} else {
// If the current selection is the first one, loop back to the last one
self.selection = Some(self.indices.len() - 1);
}
self.selection = match self.indices.range(..selection).next() {
Some(id) => Some(*id),
None => self.indices.last().copied(),
};
} else if !self.indices.is_empty() {
self.selection = Some(self.indices.len() - 1);
self.selection = self.indices.last().copied();
}
}

/// Return the index of the currently selected item in the event list
pub fn selection(&self) -> Option<usize> {
self
.selection
.map(|index| *self.indices.get_index(index).unwrap().0)
/// Return the id of the currently selected event
pub fn selection(&self) -> Option<u64> {
self.selection
}

pub fn statistics(&self) -> Line {
Expand All @@ -124,7 +117,7 @@ impl QueryResult {
.set_style(THEME.query_match_total_cnt);
let selected = self
.selection
.map(|index| index + 1)
.map(|index| self.indices.rank(&index) + 1)
.unwrap_or(0)
.to_string()
.set_style(THEME.query_match_current_no);
Expand Down

0 comments on commit 8ca4a4c

Please sign in to comment.