From 2675934027e83f6b7119dec45055d34ee1f7b20f Mon Sep 17 00:00:00 2001 From: extrawurst <776816+extrawurst@users.noreply.github.com> Date: Sun, 27 Aug 2023 11:25:16 +0200 Subject: [PATCH] Index of search result (#1840) --- Cargo.lock | 1 + Cargo.toml | 1 + src/components/commitlist.rs | 44 +++++++++++++++++++--------- src/components/utils/logitems.rs | 5 ++-- src/tabs/revlog.rs | 50 ++++++++++++++++++-------------- 5 files changed, 64 insertions(+), 37 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1296a001ac..5c14ad1d57 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -744,6 +744,7 @@ dependencies = [ "filetreelist", "fuzzy-matcher", "gh-emoji", + "indexmap", "itertools", "log", "notify", diff --git a/Cargo.toml b/Cargo.toml index 7a2e958715..6e8a455ae5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ easy-cast = "0.5" filetreelist = { path = "./filetreelist", version = "0.5" } fuzzy-matcher = "0.3" gh-emoji = { version = "1.0", optional = true } +indexmap = "1.9" itertools = "0.11" log = "0.4" notify = "5.1" diff --git a/src/components/commitlist.rs b/src/components/commitlist.rs index 27208e6023..c4e4fd290e 100644 --- a/src/components/commitlist.rs +++ b/src/components/commitlist.rs @@ -18,6 +18,7 @@ use asyncgit::sync::{ }; use chrono::{DateTime, Local}; use crossterm::event::Event; +use indexmap::IndexSet; use itertools::Itertools; use ratatui::{ backend::Backend, @@ -28,12 +29,8 @@ use ratatui::{ Frame, }; use std::{ - borrow::Cow, - cell::Cell, - cmp, - collections::{BTreeMap, HashSet}, - convert::TryFrom, - time::Instant, + borrow::Cow, cell::Cell, cmp, collections::BTreeMap, + convert::TryFrom, rc::Rc, time::Instant, }; const ELEMENTS_PER_LINE: usize = 9; @@ -44,8 +41,9 @@ pub struct CommitList { repo: RepoPathRef, title: Box, selection: usize, + highlighted_selection: Option, items: ItemBatch, - highlights: Option>, + highlights: Option>>, commits: Vec, marked: Vec<(usize, CommitId)>, scroll_state: (Instant, f32), @@ -73,6 +71,7 @@ impl CommitList { items: ItemBatch::default(), marked: Vec::with_capacity(2), selection: 0, + highlighted_selection: None, commits: Vec::new(), highlights: None, scroll_state: (Instant::now(), 0_f32), @@ -240,10 +239,11 @@ impl CommitList { /// pub fn set_highlighting( &mut self, - highlighting: Option>, + highlighting: Option>>, ) { self.highlights = highlighting; self.select_next_highlight(); + self.set_highlighted_selection_index(); self.fetch_commits(); } @@ -253,12 +253,32 @@ impl CommitList { if let Some(position) = position { self.selection = position; + self.set_highlighted_selection_index(); Ok(()) } else { anyhow::bail!("Could not select commit. It might not be loaded yet or it might be on a different branch."); } } + /// + pub fn highlighted_selection_info(&self) -> (usize, usize) { + let amount = self + .highlights + .as_ref() + .map(|highlights| highlights.len()) + .unwrap_or_default(); + (self.highlighted_selection.unwrap_or_default(), amount) + } + + fn set_highlighted_selection_index(&mut self) { + self.highlighted_selection = + self.highlights.as_ref().and_then(|highlights| { + highlights.iter().position(|entry| { + entry == &self.commits[self.selection] + }) + }); + } + const fn selection(&self) -> usize { self.selection } @@ -318,6 +338,7 @@ impl CommitList { self.selection = new_selection; if self.selection_highlighted() { + self.set_highlighted_selection_index(); return Ok(true); } } @@ -703,12 +724,7 @@ impl CommitList { ); if let Ok(commits) = commits { - self.items.set_items( - want_min, - commits, - //TODO: optimize via sharable data (BTreeMap that preserves order and lookup) - &self.highlights.clone(), - ); + self.items.set_items(want_min, commits, &self.highlights); } } } diff --git a/src/components/utils/logitems.rs b/src/components/utils/logitems.rs index fb7115d05a..b6b113f661 100644 --- a/src/components/utils/logitems.rs +++ b/src/components/utils/logitems.rs @@ -1,6 +1,7 @@ use asyncgit::sync::{CommitId, CommitInfo}; use chrono::{DateTime, Duration, Local, NaiveDateTime, Utc}; -use std::{collections::HashSet, slice::Iter}; +use indexmap::IndexSet; +use std::{rc::Rc, slice::Iter}; #[cfg(feature = "ghemoji")] use super::emoji::emojifi_string; @@ -111,7 +112,7 @@ impl ItemBatch { &mut self, start_index: usize, commits: Vec, - highlighted: &Option>, + highlighted: &Option>>, ) { self.items.clear(); self.items.extend(commits.into_iter().map(|c| { diff --git a/src/tabs/revlog.rs b/src/tabs/revlog.rs index 0bdb87f572..7c38c850b6 100644 --- a/src/tabs/revlog.rs +++ b/src/tabs/revlog.rs @@ -23,6 +23,7 @@ use asyncgit::{ }; use crossbeam_channel::Sender; use crossterm::event::Event; +use indexmap::IndexSet; use ratatui::{ backend::Backend, layout::{Alignment, Constraint, Direction, Layout, Rect}, @@ -30,11 +31,10 @@ use ratatui::{ widgets::{Block, Borders, Paragraph}, Frame, }; -use std::{collections::HashSet, rc::Rc, time::Duration}; +use std::{rc::Rc, time::Duration}; use sync::CommitTags; struct LogSearchResult { - commits: usize, options: LogFilterSearchOptions, duration: Duration, } @@ -280,16 +280,14 @@ impl Revlog { false } else { let results = search.extract_items()?; - let commits = results.len(); let duration = search.get_last_duration()?; - self.list.set_highlighting(Some( - results.into_iter().collect::>(), - )); + self.list.set_highlighting(Some(Rc::new( + results.into_iter().collect::>(), + ))); self.search = LogSearch::Results(LogSearchResult { - commits, options: options.clone(), duration, }); @@ -306,22 +304,28 @@ impl Revlog { } fn draw_search(&self, f: &mut Frame, area: Rect) { - let text = match &self.search { - LogSearch::Searching(_, options) => { - format!( - "'{}' (pending results...)", - options.search_pattern.clone() - ) - } + let (text, title) = match &self.search { + LogSearch::Searching(_, options) => ( + format!("'{}'", options.search_pattern.clone()), + String::from("(pending results...)"), + ), LogSearch::Results(results) => { - format!( - "'{}' (hits: {}) (duration: {:?})", - results.options.search_pattern.clone(), - results.commits, - results.duration, + let info = self.list.highlighted_selection_info(); + + ( + format!( + "'{}' (duration: {:?})", + results.options.search_pattern.clone(), + results.duration, + ), + format!( + "({}/{})", + (info.0 + 1).min(info.1), + info.1 + ), ) } - LogSearch::Off => String::new(), + LogSearch::Off => (String::new(), String::new()), }; f.render_widget( @@ -329,7 +333,11 @@ impl Revlog { .block( Block::default() .title(Span::styled( - strings::POPUP_TITLE_LOG_SEARCH, + format!( + "{} {}", + strings::POPUP_TITLE_LOG_SEARCH, + title + ), self.theme.title(true), )) .borders(Borders::ALL)