diff --git a/CHANGELOG.md b/CHANGELOG.md index 013abdfcb3..de0d49a41e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,12 +21,13 @@ Both commands can be overwritten via `newline` and `commit` in the key bindings. These defaults require some adoption from existing users but feel more natural to new users. ### Added +* support for new-line in text-input (e.g. commit message editor) [[@pm100]](https://github/pm100) ([#1662](https://github.com/extrawurst/gitui/issues/1662)). +* add syntax highlighting for blame view [[@tdtrung17693](https://github.com/tdtrung17693)] ([#745](https://github.com/extrawurst/gitui/issues/745)) +* allow aborting pending commit log search [[@StemCll](https://github.com/StemCll)] ([#1860](https://github.com/extrawurst/gitui/issues/1860)) * `theme.ron` now supports customizing line break symbol ([#1894](https://github.com/extrawurst/gitui/issues/1894)) * add confirmation for dialog for undo commit [[@TeFiLeDo](https://github.com/TeFiLeDo)] ([#1912](https://github.com/extrawurst/gitui/issues/1912)) * support `prepare-commit-msg` hook ([#1873](https://github.com/extrawurst/gitui/issues/1873)) -* support for new-line in text-input (e.g. commit message editor) [[@pm100]](https://github/pm100) ([#1662](https://github.com/extrawurst/gitui/issues/1662)). * new style `block_title_focused` to allow customizing title text of focused frame/block ([#2052](https://github.com/extrawurst/gitui/issues/2052)). -* add syntax highlighting for blame view [[@tdtrung17693](https://github.com/tdtrung17693)] ([#745](https://github.com/extrawurst/gitui/issues/745)) * allow `fetch` command in both tabs of branchlist popup ([#2067](https://github.com/extrawurst/gitui/issues/2067)) ### Changed diff --git a/asyncgit/src/filter_commits.rs b/asyncgit/src/filter_commits.rs index 3746b25071..1b48412663 100644 --- a/asyncgit/src/filter_commits.rs +++ b/asyncgit/src/filter_commits.rs @@ -10,7 +10,10 @@ use crate::{ AsyncGitNotification, ProgressPercent, }; use std::{ - sync::{atomic::AtomicUsize, Arc, Mutex}, + sync::{ + atomic::{AtomicBool, AtomicUsize, Ordering}, + Arc, Mutex, + }, time::{Duration, Instant}, }; @@ -35,6 +38,7 @@ enum JobState { pub struct AsyncCommitFilterJob { state: Arc>>, filter: SharedCommitFilterFn, + cancellation_flag: Arc, } /// @@ -44,6 +48,7 @@ impl AsyncCommitFilterJob { repo_path: RepoPath, commits: Vec, filter: SharedCommitFilterFn, + cancellation_flag: Arc, ) -> Self { Self { state: Arc::new(Mutex::new(Some(JobState::Request { @@ -51,6 +56,7 @@ impl AsyncCommitFilterJob { commits, }))), filter, + cancellation_flag, } } @@ -90,6 +96,8 @@ impl AsyncCommitFilterJob { commits: Vec, params: &RunParams, ) -> Result<(Instant, Vec)> { + scopetime::scope_time!("filter_commits"); + let total_amount = commits.len(); let start = Instant::now(); @@ -115,6 +123,13 @@ impl AsyncCommitFilterJob { std::sync::atomic::Ordering::Relaxed, ); + if self + .cancellation_flag + .load(Ordering::Relaxed) + { + return None; + } + Self::update_progress( params, ProgressPercent::new( diff --git a/src/tabs/revlog.rs b/src/tabs/revlog.rs index 2d13500039..41694862bc 100644 --- a/src/tabs/revlog.rs +++ b/src/tabs/revlog.rs @@ -32,7 +32,14 @@ use ratatui::{ widgets::{Block, Borders, Paragraph}, Frame, }; -use std::{rc::Rc, time::Duration}; +use std::{ + rc::Rc, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, + time::Duration, +}; use sync::CommitTags; struct LogSearchResult { @@ -47,6 +54,7 @@ enum LogSearch { AsyncSingleJob, LogFilterSearchOptions, Option, + Arc, ), Results(LogSearchResult), } @@ -113,7 +121,7 @@ impl Revlog { } const fn is_search_pending(&self) -> bool { - matches!(self.search, LogSearch::Searching(_, _, _)) + matches!(self.search, LogSearch::Searching(_, _, _, _)) } /// @@ -247,23 +255,48 @@ impl Revlog { LogFilterSearch::new(options.clone()), ); + let cancellation_flag = Arc::new(AtomicBool::new(false)); + let mut job = AsyncSingleJob::new(self.sender.clone()); job.spawn(AsyncCommitFilterJob::new( self.repo.borrow().clone(), self.list.copy_items(), filter, + Arc::clone(&cancellation_flag), )); - self.search = LogSearch::Searching(job, options, None); + self.search = LogSearch::Searching( + job, + options, + None, + Arc::clone(&cancellation_flag), + ); + + self.list.set_highlighting(None); + } + } + fn cancel_search(&mut self) -> bool { + if let LogSearch::Searching(_, _, _, cancellation_flag) = + &self.search + { + cancellation_flag.store(true, Ordering::Relaxed); self.list.set_highlighting(None); + return true; } + + false } fn update_search_state(&mut self) { match &mut self.search { LogSearch::Off | LogSearch::Results(_) => (), - LogSearch::Searching(search, options, progress) => { + LogSearch::Searching( + search, + options, + progress, + cancel, + ) => { if search.is_pending() { //update progress *progress = search.progress(); @@ -273,20 +306,26 @@ impl Revlog { { match search { Ok(search) => { - self.list.set_highlighting(Some( - Rc::new( - search - .result - .into_iter() - .collect::>(), - ), - )); + let was_aborted = + cancel.load(Ordering::Relaxed); + + self.search = if was_aborted { + LogSearch::Off + } else { + self.list.set_highlighting(Some( + Rc::new( + search + .result + .into_iter() + .collect::>(), + ), + )); - self.search = LogSearch::Results(LogSearchResult { options: options.clone(), duration: search.duration, - }); + }) + }; } Err(err) => { self.queue.push( @@ -309,7 +348,7 @@ impl Revlog { fn draw_search(&self, f: &mut Frame, area: Rect) { let (text, title) = match &self.search { - LogSearch::Searching(_, options, progress) => ( + LogSearch::Searching(_, options, progress, _) => ( format!("'{}'", options.search_pattern.clone()), format!( "({}%)", @@ -357,12 +396,12 @@ impl Revlog { ); } - fn can_leave_search(&self) -> bool { + fn can_close_search(&self) -> bool { self.is_in_search_mode() && !self.is_search_pending() } fn can_start_search(&self) -> bool { - !self.git_log.is_pending() + !self.git_log.is_pending() && !self.is_search_pending() } } @@ -425,11 +464,13 @@ impl Component for Revlog { k, self.key_config.keys.exit_popup, ) { - if self.can_leave_search() { - self.search = LogSearch::Off; + if self.is_search_pending() { + self.cancel_search(); + } else if self.can_close_search() { self.list.set_highlighting(None); - return Ok(EventState::Consumed); + self.search = LogSearch::Off; } + return Ok(EventState::Consumed); } else if key_match(k, self.key_config.keys.copy) { try_or_popup!( self, @@ -462,13 +503,15 @@ impl Component for Revlog { } else if key_match( k, self.key_config.keys.select_branch, - ) { + ) && !self.is_search_pending() + { self.queue.push(InternalEvent::SelectBranch); return Ok(EventState::Consumed); } else if key_match( k, self.key_config.keys.status_reset_item, - ) { + ) && !self.is_search_pending() + { try_or_popup!( self, "revert error:", @@ -479,7 +522,8 @@ impl Component for Revlog { } else if key_match( k, self.key_config.keys.open_file_tree, - ) { + ) && !self.is_search_pending() + { return self.selected_commit().map_or( Ok(EventState::NotConsumed), |id| { @@ -499,7 +543,8 @@ impl Component for Revlog { } else if key_match( k, self.key_config.keys.log_reset_comit, - ) { + ) && !self.is_search_pending() + { return self.selected_commit().map_or( Ok(EventState::NotConsumed), |id| { @@ -512,7 +557,8 @@ impl Component for Revlog { } else if key_match( k, self.key_config.keys.log_reword_comit, - ) { + ) && !self.is_search_pending() + { return self.selected_commit().map_or( Ok(EventState::NotConsumed), |id| { @@ -532,6 +578,7 @@ impl Component for Revlog { k, self.key_config.keys.compare_commits, ) && self.list.marked_count() > 0 + && !self.is_search_pending() { if self.list.marked_count() == 1 { // compare against head @@ -577,7 +624,9 @@ impl Component for Revlog { CommandInfo::new( strings::commands::log_close_search(&self.key_config), true, - (self.visible && self.can_leave_search()) + (self.visible + && (self.can_close_search() + || self.is_search_pending())) || force_all, ) .order(order::PRIORITY), @@ -601,20 +650,24 @@ impl Component for Revlog { &self.key_config, ), true, - self.visible || force_all, + (self.visible && !self.is_search_pending()) || force_all, )); out.push(CommandInfo::new( strings::commands::compare_with_head(&self.key_config), self.list.marked_count() == 1, - (self.visible && self.list.marked_count() <= 1) + (self.visible + && !self.is_search_pending() + && self.list.marked_count() <= 1) || force_all, )); out.push(CommandInfo::new( strings::commands::compare_commits(&self.key_config), true, - (self.visible && self.list.marked_count() == 2) + (self.visible + && !self.is_search_pending() + && self.list.marked_count() == 2) || force_all, )); @@ -651,24 +704,24 @@ impl Component for Revlog { out.push(CommandInfo::new( strings::commands::inspect_file_tree(&self.key_config), self.selected_commit().is_some(), - self.visible || force_all, + (self.visible && !self.is_search_pending()) || force_all, )); out.push(CommandInfo::new( strings::commands::revert_commit(&self.key_config), self.selected_commit().is_some(), - self.visible || force_all, + (self.visible && !self.is_search_pending()) || force_all, )); out.push(CommandInfo::new( strings::commands::log_reset_commit(&self.key_config), self.selected_commit().is_some(), - self.visible || force_all, + (self.visible && !self.is_search_pending()) || force_all, )); out.push(CommandInfo::new( strings::commands::log_reword_commit(&self.key_config), self.selected_commit().is_some(), - self.visible || force_all, + (self.visible && !self.is_search_pending()) || force_all, )); out.push(CommandInfo::new( strings::commands::log_find_commit(&self.key_config),