From 294738d61f4943fa6ac0fdc71149b16d2df58155 Mon Sep 17 00:00:00 2001 From: remique Date: Fri, 19 Aug 2022 13:37:49 +0200 Subject: [PATCH 1/6] Add copy marked --- src/components/commitlist.rs | 86 ++++++++++++++++++++++++++++++++---- src/tabs/revlog.rs | 6 +-- src/tabs/stashlist.rs | 5 ++- 3 files changed, 84 insertions(+), 13 deletions(-) diff --git a/src/components/commitlist.rs b/src/components/commitlist.rs index ed7a69611c..297a2edbb6 100644 --- a/src/components/commitlist.rs +++ b/src/components/commitlist.rs @@ -34,7 +34,7 @@ pub struct CommitList { branch: Option, count_total: usize, items: ItemBatch, - marked: Vec, + marked: Vec<(usize, CommitId)>, scroll_state: (Instant, f32), tags: Option, current_size: Cell<(u16, u16)>, @@ -134,7 +134,7 @@ impl CommitList { } /// - pub fn marked(&self) -> &[CommitId] { + pub fn marked(&self) -> &[(usize, CommitId)] { &self.marked } @@ -143,11 +143,75 @@ impl CommitList { self.marked.clear(); } + /// + pub fn marked_indexes(&self) -> Vec { + let (indexes, _): (Vec, Vec<_>) = + self.marked.iter().copied().unzip(); + + indexes + } + + /// + pub fn marked_commits(&self) -> Vec { + let (_, commits): (Vec<_>, Vec) = + self.marked.iter().copied().unzip(); + + commits + } + + fn marked_consecutive(&self) -> bool { + let mut marked = self.marked_indexes(); + marked.sort_unstable(); + + for i in 1..marked.len() { + if marked[i - 1] + 1 != marked[i] { + return false; + } + } + + true + } + pub fn copy_entry_hash(&self) -> Result<()> { - if let Some(e) = self.items.iter().nth( - self.selection.saturating_sub(self.items.index_offset()), - ) { - crate::clipboard::copy_string(&e.hash_short)?; + if self.marked_count() > 1 { + if self.marked_consecutive() { + let mut sorted = self.marked_indexes(); + sorted.sort_unstable(); + + let yank = format!( + "{}^..{}", + self.marked()[sorted[0]].1.to_string(), + self.marked()[sorted.len() - 1].1.to_string() + ); + + crate::clipboard::copy_string(&yank)?; + } else { + let separate = self + .marked_commits() + .iter() + .map(std::string::ToString::to_string) + .join(" "); + + crate::clipboard::copy_string(&separate)?; + } + } else { + match self.marked_count() { + 0 => { + if let Some(e) = self.items.iter().nth( + self.selection.saturating_sub( + self.items.index_offset(), + ), + ) { + crate::clipboard::copy_string(&e.hash_short)?; + } + } + 1 => { + crate::clipboard::copy_string( + &self.marked()[0].1.to_string(), + )?; + } + _ => {} + } } Ok(()) } @@ -191,10 +255,13 @@ impl CommitList { fn mark(&mut self) { if let Some(e) = self.selected_entry() { let id = e.id; + let selected = self + .selection + .saturating_sub(self.items.index_offset()); if self.is_marked(&id).unwrap_or_default() { - self.marked.retain(|marked| marked != &id); + self.marked.retain(|marked| marked.1 != id); } else { - self.marked.push(id); + self.marked.push((selected, id)); } } } @@ -227,7 +294,8 @@ impl CommitList { if self.marked.is_empty() { None } else { - let found = self.marked.iter().any(|entry| entry == id); + let found = + self.marked.iter().any(|entry| entry.1 == *id); Some(found) } } diff --git a/src/tabs/revlog.rs b/src/tabs/revlog.rs index 47baeff20a..28e5f26a1d 100644 --- a/src/tabs/revlog.rs +++ b/src/tabs/revlog.rs @@ -328,7 +328,7 @@ impl Component for Revlog { self.queue.push(InternalEvent::OpenPopup( StackablePopupOpen::CompareCommits( InspectCommitOpen::new( - self.list.marked()[0], + self.list.marked()[0].1, ), ), )); @@ -339,8 +339,8 @@ impl Component for Revlog { self.queue.push(InternalEvent::OpenPopup( StackablePopupOpen::CompareCommits( InspectCommitOpen { - commit_id: marked[0], - compare_id: Some(marked[1]), + commit_id: marked[0].1, + compare_id: Some(marked[1].1), tags: None, }, ), diff --git a/src/tabs/stashlist.rs b/src/tabs/stashlist.rs index dc3bc98180..75aa60f9b7 100644 --- a/src/tabs/stashlist.rs +++ b/src/tabs/stashlist.rs @@ -77,8 +77,11 @@ impl StashList { fn drop_stash(&mut self) { if self.list.marked_count() > 0 { + let (_, commits): (Vec<_>, Vec) = + self.list.marked().to_vec().iter().copied().unzip(); + self.queue.push(InternalEvent::ConfirmAction( - Action::StashDrop(self.list.marked().to_vec()), + Action::StashDrop(commits), )); } else if let Some(e) = self.list.selected_entry() { self.queue.push(InternalEvent::ConfirmAction( From 031d43b538d52a44b72294c04363c3dde2ab1c2b Mon Sep 17 00:00:00 2001 From: remique Date: Mon, 22 Aug 2022 12:42:25 +0200 Subject: [PATCH 2/6] Fix copying marked hashes --- src/components/commitlist.rs | 115 +++++++++++++++++++---------------- src/tabs/revlog.rs | 12 ++-- src/tabs/stashlist.rs | 5 +- 3 files changed, 70 insertions(+), 62 deletions(-) diff --git a/src/components/commitlist.rs b/src/components/commitlist.rs index 297a2edbb6..fbfee9b0bd 100644 --- a/src/components/commitlist.rs +++ b/src/components/commitlist.rs @@ -34,7 +34,8 @@ pub struct CommitList { branch: Option, count_total: usize, items: ItemBatch, - marked: Vec<(usize, CommitId)>, + marked: Vec, + marked_indexes: Vec, scroll_state: (Instant, f32), tags: Option, current_size: Cell<(u16, u16)>, @@ -53,6 +54,7 @@ impl CommitList { Self { items: ItemBatch::default(), marked: Vec::with_capacity(2), + marked_indexes: Vec::new(), selection: 0, branch: None, count_total: 0, @@ -134,34 +136,23 @@ impl CommitList { } /// - pub fn marked(&self) -> &[(usize, CommitId)] { + pub fn marked(&self) -> &[CommitId] { &self.marked } /// pub fn clear_marked(&mut self) { self.marked.clear(); + self.marked_indexes.clear(); } /// - pub fn marked_indexes(&self) -> Vec { - let (indexes, _): (Vec, Vec<_>) = - self.marked.iter().copied().unzip(); - - indexes - } - - /// - pub fn marked_commits(&self) -> Vec { - let (_, commits): (Vec<_>, Vec) = - self.marked.iter().copied().unzip(); - - commits + pub fn marked_indexes(&self) -> &Vec { + &self.marked_indexes } fn marked_consecutive(&self) -> bool { - let mut marked = self.marked_indexes(); - marked.sort_unstable(); + let marked = self.marked_indexes(); for i in 1..marked.len() { if marked[i - 1] + 1 != marked[i] { @@ -172,46 +163,58 @@ impl CommitList { true } - pub fn copy_entry_hash(&self) -> Result<()> { - if self.marked_count() > 1 { - if self.marked_consecutive() { - let mut sorted = self.marked_indexes(); - sorted.sort_unstable(); - - let yank = format!( - "{}^..{}", - self.marked()[sorted[0]].1.to_string(), - self.marked()[sorted.len() - 1].1.to_string() - ); + pub fn copy_marked_hashes(&self) -> Result<()> { + if self.marked_consecutive() { + let m = self.marked_indexes(); - crate::clipboard::copy_string(&yank)?; - } else { - let separate = self - .marked_commits() - .iter() - .map(std::string::ToString::to_string) - .join(" "); + let first = self.items.iter().nth(m[0]); - crate::clipboard::copy_string(&separate)?; - } + let last = self.items.iter().nth(m[m.len() - 1]); + + if let (Some(f), Some(l)) = (first, last) { + let yank = + format!("{}^..{}", f.hash_short, l.hash_short); + crate::clipboard::copy_string(&yank)?; + }; } else { - match self.marked_count() { - 0 => { - if let Some(e) = self.items.iter().nth( - self.selection.saturating_sub( - self.items.index_offset(), - ), - ) { - crate::clipboard::copy_string(&e.hash_short)?; + let separate = self + .marked_indexes() + .iter() + .map(|e| { + let nth = self.items.iter().nth(*e); + + if let Some(get) = nth { + get.hash_short.to_string() + } else { + String::from("") } + }) + .join(" "); + + crate::clipboard::copy_string(&separate)?; + } + + Ok(()) + } + + pub fn copy_entry_hash(&self) -> Result<()> { + match self.marked_count() { + 0 => { + if let Some(e) = self.items.iter().nth( + self.selection + .saturating_sub(self.items.index_offset()), + ) { + crate::clipboard::copy_string(&e.hash_short)?; } - 1 => { - crate::clipboard::copy_string( - &self.marked()[0].1.to_string(), - )?; + } + 1 => { + if let Some(e) = + self.items.iter().nth(self.marked_indexes()[0]) + { + crate::clipboard::copy_string(&e.hash_short)?; } - _ => {} } + _ => {} } Ok(()) } @@ -259,9 +262,14 @@ impl CommitList { .selection .saturating_sub(self.items.index_offset()); if self.is_marked(&id).unwrap_or_default() { - self.marked.retain(|marked| marked.1 != id); + self.marked.retain(|marked| marked != &id); + + self.marked_indexes.retain(|m| m != &selected); } else { - self.marked.push((selected, id)); + self.marked.push(id); + + self.marked_indexes.push(selected); + self.marked_indexes.sort_unstable(); } } } @@ -294,8 +302,7 @@ impl CommitList { if self.marked.is_empty() { None } else { - let found = - self.marked.iter().any(|entry| entry.1 == *id); + let found = self.marked.iter().any(|entry| entry == id); Some(found) } } diff --git a/src/tabs/revlog.rs b/src/tabs/revlog.rs index 28e5f26a1d..6dea284370 100644 --- a/src/tabs/revlog.rs +++ b/src/tabs/revlog.rs @@ -165,7 +165,11 @@ impl Revlog { } fn copy_commit_hash(&self) -> Result<()> { - self.list.copy_entry_hash()?; + if self.list.marked_count() > 1 { + self.list.copy_marked_hashes()?; + } else { + self.list.copy_entry_hash()?; + } Ok(()) } @@ -328,7 +332,7 @@ impl Component for Revlog { self.queue.push(InternalEvent::OpenPopup( StackablePopupOpen::CompareCommits( InspectCommitOpen::new( - self.list.marked()[0].1, + self.list.marked()[0], ), ), )); @@ -339,8 +343,8 @@ impl Component for Revlog { self.queue.push(InternalEvent::OpenPopup( StackablePopupOpen::CompareCommits( InspectCommitOpen { - commit_id: marked[0].1, - compare_id: Some(marked[1].1), + commit_id: marked[0], + compare_id: Some(marked[1]), tags: None, }, ), diff --git a/src/tabs/stashlist.rs b/src/tabs/stashlist.rs index 75aa60f9b7..dc3bc98180 100644 --- a/src/tabs/stashlist.rs +++ b/src/tabs/stashlist.rs @@ -77,11 +77,8 @@ impl StashList { fn drop_stash(&mut self) { if self.list.marked_count() > 0 { - let (_, commits): (Vec<_>, Vec) = - self.list.marked().to_vec().iter().copied().unzip(); - self.queue.push(InternalEvent::ConfirmAction( - Action::StashDrop(commits), + Action::StashDrop(self.list.marked().to_vec()), )); } else if let Some(e) = self.list.selected_entry() { self.queue.push(InternalEvent::ConfirmAction( From 729020dbd00ed81586c73acb008eca3d71806b51 Mon Sep 17 00:00:00 2001 From: remique Date: Mon, 22 Aug 2022 12:47:19 +0200 Subject: [PATCH 3/6] Fix make check --- src/components/commitlist.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/components/commitlist.rs b/src/components/commitlist.rs index fbfee9b0bd..350ac9c6a0 100644 --- a/src/components/commitlist.rs +++ b/src/components/commitlist.rs @@ -147,7 +147,7 @@ impl CommitList { } /// - pub fn marked_indexes(&self) -> &Vec { + pub const fn marked_indexes(&self) -> &Vec { &self.marked_indexes } @@ -181,13 +181,10 @@ impl CommitList { .marked_indexes() .iter() .map(|e| { - let nth = self.items.iter().nth(*e); - - if let Some(get) = nth { - get.hash_short.to_string() - } else { - String::from("") - } + self.items.iter().nth(*e).map_or_else( + || String::from(""), + |le| le.hash_short.to_string(), + ) }) .join(" "); From 08844396a8da8d224016905da2d1e17afa7f99fd Mon Sep 17 00:00:00 2001 From: remique Date: Fri, 26 Aug 2022 10:11:40 +0200 Subject: [PATCH 4/6] Update Changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f96c35f0c8..a16a0828be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added +* allow copying marked commits [[@remique](https://github.com/remique)] ([#1288](https://github.com/extrawurst/gitui/issues/1288)) + ## [0.21.0] - 2021-08-17 **popup stacking** From e5520956bbddc6bb8632300a4f1baf4f798bc3e8 Mon Sep 17 00:00:00 2001 From: remique Date: Sun, 18 Sep 2022 23:50:04 +0200 Subject: [PATCH 5/6] Use tuple as marked --- src/components/commitlist.rs | 36 ++++++++++++++++++++++-------------- src/tabs/revlog.rs | 6 +++--- src/tabs/stashlist.rs | 2 +- 3 files changed, 26 insertions(+), 18 deletions(-) diff --git a/src/components/commitlist.rs b/src/components/commitlist.rs index 350ac9c6a0..2c364db6a7 100644 --- a/src/components/commitlist.rs +++ b/src/components/commitlist.rs @@ -34,8 +34,7 @@ pub struct CommitList { branch: Option, count_total: usize, items: ItemBatch, - marked: Vec, - marked_indexes: Vec, + marked: Vec<(usize, CommitId)>, scroll_state: (Instant, f32), tags: Option, current_size: Cell<(u16, u16)>, @@ -54,7 +53,6 @@ impl CommitList { Self { items: ItemBatch::default(), marked: Vec::with_capacity(2), - marked_indexes: Vec::new(), selection: 0, branch: None, count_total: 0, @@ -136,19 +134,29 @@ impl CommitList { } /// - pub fn marked(&self) -> &[CommitId] { + pub fn marked(&self) -> &[(usize, CommitId)] { &self.marked } /// pub fn clear_marked(&mut self) { self.marked.clear(); - self.marked_indexes.clear(); } /// - pub const fn marked_indexes(&self) -> &Vec { - &self.marked_indexes + pub fn marked_indexes(&self) -> Vec { + let (indexes, _): (Vec, Vec<_>) = + self.marked.iter().copied().unzip(); + + indexes + } + + /// + pub fn marked_commits(&self) -> Vec { + let (_, commits): (Vec<_>, Vec) = + self.marked.iter().copied().unzip(); + + commits } fn marked_consecutive(&self) -> bool { @@ -259,14 +267,13 @@ impl CommitList { .selection .saturating_sub(self.items.index_offset()); if self.is_marked(&id).unwrap_or_default() { - self.marked.retain(|marked| marked != &id); - - self.marked_indexes.retain(|m| m != &selected); + self.marked.retain(|marked| marked.1 != id); } else { - self.marked.push(id); + self.marked.push((selected, id)); - self.marked_indexes.push(selected); - self.marked_indexes.sort_unstable(); + self.marked.sort_unstable_by(|first, second| { + first.0.cmp(&second.0) + }); } } } @@ -299,7 +306,8 @@ impl CommitList { if self.marked.is_empty() { None } else { - let found = self.marked.iter().any(|entry| entry == id); + let found = + self.marked.iter().any(|entry| entry.1 == *id); Some(found) } } diff --git a/src/tabs/revlog.rs b/src/tabs/revlog.rs index 6dea284370..4919775b42 100644 --- a/src/tabs/revlog.rs +++ b/src/tabs/revlog.rs @@ -332,7 +332,7 @@ impl Component for Revlog { self.queue.push(InternalEvent::OpenPopup( StackablePopupOpen::CompareCommits( InspectCommitOpen::new( - self.list.marked()[0], + self.list.marked()[0].1, ), ), )); @@ -343,8 +343,8 @@ impl Component for Revlog { self.queue.push(InternalEvent::OpenPopup( StackablePopupOpen::CompareCommits( InspectCommitOpen { - commit_id: marked[0], - compare_id: Some(marked[1]), + commit_id: marked[0].1, + compare_id: Some(marked[1].1), tags: None, }, ), diff --git a/src/tabs/stashlist.rs b/src/tabs/stashlist.rs index dc3bc98180..1d61d988ac 100644 --- a/src/tabs/stashlist.rs +++ b/src/tabs/stashlist.rs @@ -78,7 +78,7 @@ impl StashList { fn drop_stash(&mut self) { if self.list.marked_count() > 0 { self.queue.push(InternalEvent::ConfirmAction( - Action::StashDrop(self.list.marked().to_vec()), + Action::StashDrop(self.list.marked_commits()), )); } else if let Some(e) = self.list.selected_entry() { self.queue.push(InternalEvent::ConfirmAction( From 60b1208da7d6f20d249bd2b9afa0141f5491fcac Mon Sep 17 00:00:00 2001 From: remique Date: Mon, 19 Sep 2022 10:38:14 +0200 Subject: [PATCH 6/6] Fix clippy errors --- src/components/commitlist.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/commitlist.rs b/src/components/commitlist.rs index a7c7801ca1..ebe4edc48a 100644 --- a/src/components/commitlist.rs +++ b/src/components/commitlist.rs @@ -189,10 +189,12 @@ impl CommitList { .marked_indexes() .iter() .map(|e| { - self.items.iter().nth(*e).map_or_else( - || String::from(""), - |le| le.hash_short.to_string(), - ) + self.items + .iter() + .nth(*e) + .map_or_else(String::new, |le| { + le.hash_short.to_string() + }) }) .join(" ");