mirror of
https://github.com/gitui-org/gitui
synced 2026-05-24 09:28:21 +00:00
Jump to commit via sha (#1818)
This commit is contained in:
parent
005047f015
commit
c68fa3e87b
6 changed files with 366 additions and 108 deletions
|
|
@ -33,6 +33,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
* added to [anaconda](https://anaconda.org/conda-forge/gitui) [[@TheBlackSheep3](https://github.com/TheBlackSheep3/)] ([#1626](https://github.com/extrawurst/gitui/issues/1626))
|
* added to [anaconda](https://anaconda.org/conda-forge/gitui) [[@TheBlackSheep3](https://github.com/TheBlackSheep3/)] ([#1626](https://github.com/extrawurst/gitui/issues/1626))
|
||||||
* visualize empty line substituted with content in diff better ([#1359](https://github.com/extrawurst/gitui/issues/1359))
|
* visualize empty line substituted with content in diff better ([#1359](https://github.com/extrawurst/gitui/issues/1359))
|
||||||
* checkout branch works with non-empty status report [[@lightsnowball](https://github.com/lightsnowball)] ([#1399](https://github.com/extrawurst/gitui/issues/1399))
|
* checkout branch works with non-empty status report [[@lightsnowball](https://github.com/lightsnowball)] ([#1399](https://github.com/extrawurst/gitui/issues/1399))
|
||||||
|
* jump to commit by SHA [[@AmmarAbouZor](https://github.com/AmmarAbouZor)] ([#1818](https://github.com/extrawurst/gitui/pull/1818))
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
* fix commit dialog char count for multibyte characters ([#1726](https://github.com/extrawurst/gitui/issues/1726))
|
* fix commit dialog char count for multibyte characters ([#1726](https://github.com/extrawurst/gitui/issues/1726))
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,19 @@ impl CommitId {
|
||||||
pub fn get_short_string(&self) -> String {
|
pub fn get_short_string(&self) -> String {
|
||||||
self.to_string().chars().take(7).collect()
|
self.to_string().chars().take(7).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tries to retrieve the `CommitId` form the revision if exists in the given repository
|
||||||
|
pub fn from_revision(
|
||||||
|
repo_path: &RepoPath,
|
||||||
|
revision: &str,
|
||||||
|
) -> Result<Self> {
|
||||||
|
scope_time!("CommitId::from_revision");
|
||||||
|
|
||||||
|
let repo = repo(repo_path)?;
|
||||||
|
|
||||||
|
let commit_obj = repo.revparse_single(revision)?;
|
||||||
|
Ok(commit_obj.id().into())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToString for CommitId {
|
impl ToString for CommitId {
|
||||||
|
|
@ -144,7 +157,7 @@ mod tests {
|
||||||
error::Result,
|
error::Result,
|
||||||
sync::{
|
sync::{
|
||||||
commit, stage_add_file, tests::repo_init_empty,
|
commit, stage_add_file, tests::repo_init_empty,
|
||||||
utils::get_head_repo, RepoPath,
|
utils::get_head_repo, CommitId, RepoPath,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use std::{fs::File, io::Write, path::Path};
|
use std::{fs::File, io::Write, path::Path};
|
||||||
|
|
@ -221,4 +234,32 @@ mod tests {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_commit_from_revision() -> Result<()> {
|
||||||
|
let (_td, repo) = repo_init_empty().unwrap();
|
||||||
|
let root = repo.path().parent().unwrap();
|
||||||
|
let repo_path: &RepoPath =
|
||||||
|
&root.as_os_str().to_str().unwrap().into();
|
||||||
|
|
||||||
|
let foo_file = Path::new("foo");
|
||||||
|
File::create(root.join(foo_file))?.write_all(b"a")?;
|
||||||
|
stage_add_file(repo_path, foo_file).unwrap();
|
||||||
|
let c1 = commit(repo_path, "subject: foo\nbody").unwrap();
|
||||||
|
let c1_rev = c1.get_short_string();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
CommitId::from_revision(repo_path, c1_rev.as_str())
|
||||||
|
.unwrap(),
|
||||||
|
c1
|
||||||
|
);
|
||||||
|
|
||||||
|
const FOREIGN_HASH: &str =
|
||||||
|
"d6d7d55cb6e4ba7301d6a11a657aab4211e5777e";
|
||||||
|
assert!(
|
||||||
|
CommitId::from_revision(repo_path, FOREIGN_HASH).is_err()
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -273,6 +273,7 @@ impl App {
|
||||||
key_config.clone(),
|
key_config.clone(),
|
||||||
),
|
),
|
||||||
log_search_popup: LogSearchPopupComponent::new(
|
log_search_popup: LogSearchPopupComponent::new(
|
||||||
|
repo.clone(),
|
||||||
&queue,
|
&queue,
|
||||||
theme.clone(),
|
theme.clone(),
|
||||||
key_config.clone(),
|
key_config.clone(),
|
||||||
|
|
|
||||||
|
|
@ -5,14 +5,16 @@ use super::{
|
||||||
use crate::{
|
use crate::{
|
||||||
keys::{key_match, SharedKeyConfig},
|
keys::{key_match, SharedKeyConfig},
|
||||||
queue::{InternalEvent, Queue},
|
queue::{InternalEvent, Queue},
|
||||||
strings::{self},
|
strings::{self, POPUP_COMMIT_SHA_INVALID},
|
||||||
ui::{self, style::SharedTheme},
|
ui::{self, style::SharedTheme},
|
||||||
};
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use asyncgit::sync::{
|
use asyncgit::sync::{
|
||||||
LogFilterSearchOptions, SearchFields, SearchOptions,
|
CommitId, LogFilterSearchOptions, RepoPathRef, SearchFields,
|
||||||
|
SearchOptions,
|
||||||
};
|
};
|
||||||
use crossterm::event::Event;
|
use crossterm::event::Event;
|
||||||
|
use easy_cast::Cast;
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
backend::Backend,
|
backend::Backend,
|
||||||
layout::{
|
layout::{
|
||||||
|
|
@ -32,19 +34,28 @@ enum Selection {
|
||||||
AuthorsSearch,
|
AuthorsSearch,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum PopupMode {
|
||||||
|
Search,
|
||||||
|
JumpCommitSha,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct LogSearchPopupComponent {
|
pub struct LogSearchPopupComponent {
|
||||||
|
repo: RepoPathRef,
|
||||||
queue: Queue,
|
queue: Queue,
|
||||||
visible: bool,
|
visible: bool,
|
||||||
|
mode: PopupMode,
|
||||||
selection: Selection,
|
selection: Selection,
|
||||||
key_config: SharedKeyConfig,
|
key_config: SharedKeyConfig,
|
||||||
find_text: TextInputComponent,
|
find_text: TextInputComponent,
|
||||||
options: (SearchFields, SearchOptions),
|
options: (SearchFields, SearchOptions),
|
||||||
theme: SharedTheme,
|
theme: SharedTheme,
|
||||||
|
jump_commit_id: Option<CommitId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LogSearchPopupComponent {
|
impl LogSearchPopupComponent {
|
||||||
///
|
///
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
repo: RepoPathRef,
|
||||||
queue: &Queue,
|
queue: &Queue,
|
||||||
theme: SharedTheme,
|
theme: SharedTheme,
|
||||||
key_config: SharedKeyConfig,
|
key_config: SharedKeyConfig,
|
||||||
|
|
@ -60,8 +71,10 @@ impl LogSearchPopupComponent {
|
||||||
find_text.enabled(true);
|
find_text.enabled(true);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
repo,
|
||||||
queue: queue.clone(),
|
queue: queue.clone(),
|
||||||
visible: false,
|
visible: false,
|
||||||
|
mode: PopupMode::Search,
|
||||||
key_config,
|
key_config,
|
||||||
options: (
|
options: (
|
||||||
SearchFields::default(),
|
SearchFields::default(),
|
||||||
|
|
@ -70,6 +83,7 @@ impl LogSearchPopupComponent {
|
||||||
theme,
|
theme,
|
||||||
find_text,
|
find_text,
|
||||||
selection: Selection::EnterText,
|
selection: Selection::EnterText,
|
||||||
|
jump_commit_id: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -80,23 +94,81 @@ impl LogSearchPopupComponent {
|
||||||
self.find_text.set_text(String::new());
|
self.find_text.set_text(String::new());
|
||||||
self.find_text.enabled(true);
|
self.find_text.enabled(true);
|
||||||
|
|
||||||
|
self.set_mode(&PopupMode::Search);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute_search(&mut self) {
|
fn set_mode(&mut self, mode: &PopupMode) {
|
||||||
|
self.find_text.set_text(String::new());
|
||||||
|
|
||||||
|
match mode {
|
||||||
|
PopupMode::Search => {
|
||||||
|
self.mode = PopupMode::Search;
|
||||||
|
self.find_text.set_default_msg("search text".into());
|
||||||
|
self.find_text.enabled(matches!(
|
||||||
|
self.selection,
|
||||||
|
Selection::EnterText
|
||||||
|
));
|
||||||
|
}
|
||||||
|
PopupMode::JumpCommitSha => {
|
||||||
|
self.mode = PopupMode::JumpCommitSha;
|
||||||
|
self.jump_commit_id = None;
|
||||||
|
self.find_text.set_default_msg("commit sha".into());
|
||||||
|
self.find_text.enabled(false);
|
||||||
|
self.selection = Selection::EnterText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn execute_confirm(&mut self) {
|
||||||
self.hide();
|
self.hide();
|
||||||
|
|
||||||
if !self.find_text.get_text().trim().is_empty() {
|
if !self.is_valid() {
|
||||||
self.queue.push(InternalEvent::CommitSearch(
|
return;
|
||||||
LogFilterSearchOptions {
|
}
|
||||||
fields: self.options.0,
|
|
||||||
options: self.options.1,
|
match self.mode {
|
||||||
search_pattern: self
|
PopupMode::Search => {
|
||||||
.find_text
|
self.queue.push(InternalEvent::CommitSearch(
|
||||||
.get_text()
|
LogFilterSearchOptions {
|
||||||
.to_string(),
|
fields: self.options.0,
|
||||||
},
|
options: self.options.1,
|
||||||
));
|
search_pattern: self
|
||||||
|
.find_text
|
||||||
|
.get_text()
|
||||||
|
.to_string(),
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
PopupMode::JumpCommitSha => {
|
||||||
|
let commit_id = self.jump_commit_id
|
||||||
|
.expect("Commit id must have value here because it's already validated");
|
||||||
|
self.queue.push(InternalEvent::SelectCommitInRevlog(
|
||||||
|
commit_id,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_valid(&self) -> bool {
|
||||||
|
match self.mode {
|
||||||
|
PopupMode::Search => {
|
||||||
|
!self.find_text.get_text().trim().is_empty()
|
||||||
|
}
|
||||||
|
PopupMode::JumpCommitSha => self.jump_commit_id.is_some(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_commit_sha(&mut self) {
|
||||||
|
let path = self.repo.borrow();
|
||||||
|
if let Ok(commit_id) = CommitId::from_revision(
|
||||||
|
&path,
|
||||||
|
self.find_text.get_text().trim(),
|
||||||
|
) {
|
||||||
|
self.jump_commit_id = Some(commit_id);
|
||||||
|
} else {
|
||||||
|
self.jump_commit_id = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -255,6 +327,177 @@ impl LogSearchPopupComponent {
|
||||||
self.find_text
|
self.find_text
|
||||||
.enabled(matches!(self.selection, Selection::EnterText));
|
.enabled(matches!(self.selection, Selection::EnterText));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn draw_search_mode<B: Backend>(
|
||||||
|
&self,
|
||||||
|
f: &mut Frame<B>,
|
||||||
|
area: Rect,
|
||||||
|
) -> Result<()> {
|
||||||
|
const SIZE: (u16, u16) = (60, 10);
|
||||||
|
let area = ui::centered_rect_absolute(SIZE.0, SIZE.1, area);
|
||||||
|
|
||||||
|
f.render_widget(Clear, area);
|
||||||
|
f.render_widget(
|
||||||
|
Block::default()
|
||||||
|
.borders(Borders::all())
|
||||||
|
.style(self.theme.title(true))
|
||||||
|
.title(Span::styled(
|
||||||
|
strings::POPUP_TITLE_LOG_SEARCH,
|
||||||
|
self.theme.title(true),
|
||||||
|
)),
|
||||||
|
area,
|
||||||
|
);
|
||||||
|
|
||||||
|
let chunks = Layout::default()
|
||||||
|
.direction(Direction::Vertical)
|
||||||
|
.constraints(
|
||||||
|
[Constraint::Length(1), Constraint::Percentage(100)]
|
||||||
|
.as_ref(),
|
||||||
|
)
|
||||||
|
.split(area.inner(&Margin {
|
||||||
|
horizontal: 1,
|
||||||
|
vertical: 1,
|
||||||
|
}));
|
||||||
|
|
||||||
|
self.find_text.draw(f, chunks[0])?;
|
||||||
|
|
||||||
|
f.render_widget(
|
||||||
|
Paragraph::new(self.get_text_options())
|
||||||
|
.block(
|
||||||
|
Block::default()
|
||||||
|
.borders(Borders::TOP)
|
||||||
|
.border_style(self.theme.block(true)),
|
||||||
|
)
|
||||||
|
.alignment(Alignment::Left),
|
||||||
|
chunks[1],
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_commit_sha_mode<B: Backend>(
|
||||||
|
&self,
|
||||||
|
f: &mut Frame<B>,
|
||||||
|
area: Rect,
|
||||||
|
) -> Result<()> {
|
||||||
|
const SIZE: (u16, u16) = (60, 3);
|
||||||
|
let area = ui::centered_rect_absolute(SIZE.0, SIZE.1, area);
|
||||||
|
|
||||||
|
let mut block_style = self.theme.title(true);
|
||||||
|
|
||||||
|
let show_invalid = !self.is_valid()
|
||||||
|
&& !self.find_text.get_text().trim().is_empty();
|
||||||
|
|
||||||
|
if show_invalid {
|
||||||
|
block_style = block_style.patch(self.theme.text_danger());
|
||||||
|
}
|
||||||
|
|
||||||
|
f.render_widget(Clear, area);
|
||||||
|
f.render_widget(
|
||||||
|
Block::default()
|
||||||
|
.borders(Borders::all())
|
||||||
|
.style(block_style)
|
||||||
|
.title(Span::styled(
|
||||||
|
strings::POPUP_TITLE_LOG_SEARCH,
|
||||||
|
self.theme.title(true),
|
||||||
|
)),
|
||||||
|
area,
|
||||||
|
);
|
||||||
|
|
||||||
|
let chunks = Layout::default()
|
||||||
|
.direction(Direction::Vertical)
|
||||||
|
.constraints([Constraint::Length(1)].as_ref())
|
||||||
|
.split(area.inner(&Margin {
|
||||||
|
horizontal: 1,
|
||||||
|
vertical: 1,
|
||||||
|
}));
|
||||||
|
|
||||||
|
self.find_text.draw(f, chunks[0])?;
|
||||||
|
|
||||||
|
if show_invalid {
|
||||||
|
self.draw_invalid_sha(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_invalid_sha<B: Backend>(&self, f: &mut Frame<B>) {
|
||||||
|
let msg_length: u16 = POPUP_COMMIT_SHA_INVALID.len().cast();
|
||||||
|
let w = Paragraph::new(POPUP_COMMIT_SHA_INVALID)
|
||||||
|
.style(self.theme.text_danger());
|
||||||
|
|
||||||
|
let rect = {
|
||||||
|
let mut rect = self.find_text.get_area();
|
||||||
|
rect.y += rect.height;
|
||||||
|
rect.height = 1;
|
||||||
|
let offset = rect.width.saturating_sub(msg_length);
|
||||||
|
rect.width = rect.width.saturating_sub(offset);
|
||||||
|
rect.x += offset;
|
||||||
|
|
||||||
|
rect
|
||||||
|
};
|
||||||
|
|
||||||
|
f.render_widget(w, rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn event_search_mode(
|
||||||
|
&mut self,
|
||||||
|
event: &crossterm::event::Event,
|
||||||
|
) -> Result<EventState> {
|
||||||
|
if let Event::Key(key) = &event {
|
||||||
|
if key_match(key, self.key_config.keys.exit_popup) {
|
||||||
|
self.hide();
|
||||||
|
} else if key_match(key, self.key_config.keys.enter)
|
||||||
|
&& self.is_valid()
|
||||||
|
{
|
||||||
|
self.execute_confirm();
|
||||||
|
} else if key_match(key, self.key_config.keys.popup_up) {
|
||||||
|
self.move_selection(true);
|
||||||
|
} else if key_match(
|
||||||
|
key,
|
||||||
|
self.key_config.keys.find_commit_sha,
|
||||||
|
) {
|
||||||
|
self.set_mode(&PopupMode::JumpCommitSha);
|
||||||
|
} else if key_match(key, self.key_config.keys.popup_down)
|
||||||
|
{
|
||||||
|
self.move_selection(false);
|
||||||
|
} else if key_match(
|
||||||
|
key,
|
||||||
|
self.key_config.keys.log_mark_commit,
|
||||||
|
) && self.option_selected()
|
||||||
|
{
|
||||||
|
self.toggle_option();
|
||||||
|
} else if !self.option_selected() {
|
||||||
|
self.find_text.event(event)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(EventState::Consumed)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn event_commit_sha_mode(
|
||||||
|
&mut self,
|
||||||
|
event: &crossterm::event::Event,
|
||||||
|
) -> Result<EventState> {
|
||||||
|
if let Event::Key(key) = &event {
|
||||||
|
if key_match(key, self.key_config.keys.exit_popup) {
|
||||||
|
self.set_mode(&PopupMode::Search);
|
||||||
|
} else if key_match(key, self.key_config.keys.enter)
|
||||||
|
&& self.is_valid()
|
||||||
|
{
|
||||||
|
self.execute_confirm();
|
||||||
|
} else if self.find_text.event(event)?.is_consumed() {
|
||||||
|
self.validate_commit_sha();
|
||||||
|
self.find_text.enabled(
|
||||||
|
!self.find_text.get_text().trim().is_empty(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(EventState::Consumed)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DrawableComponent for LogSearchPopupComponent {
|
impl DrawableComponent for LogSearchPopupComponent {
|
||||||
|
|
@ -264,48 +507,14 @@ impl DrawableComponent for LogSearchPopupComponent {
|
||||||
area: Rect,
|
area: Rect,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
if self.is_visible() {
|
if self.is_visible() {
|
||||||
const SIZE: (u16, u16) = (60, 10);
|
match self.mode {
|
||||||
let area =
|
PopupMode::Search => {
|
||||||
ui::centered_rect_absolute(SIZE.0, SIZE.1, area);
|
self.draw_search_mode(f, area)?;
|
||||||
|
}
|
||||||
f.render_widget(Clear, area);
|
PopupMode::JumpCommitSha => {
|
||||||
f.render_widget(
|
self.draw_commit_sha_mode(f, area)?;
|
||||||
Block::default()
|
}
|
||||||
.borders(Borders::all())
|
}
|
||||||
.style(self.theme.title(true))
|
|
||||||
.title(Span::styled(
|
|
||||||
strings::POPUP_TITLE_LOG_SEARCH,
|
|
||||||
self.theme.title(true),
|
|
||||||
)),
|
|
||||||
area,
|
|
||||||
);
|
|
||||||
|
|
||||||
let chunks = Layout::default()
|
|
||||||
.direction(Direction::Vertical)
|
|
||||||
.constraints(
|
|
||||||
[
|
|
||||||
Constraint::Length(1),
|
|
||||||
Constraint::Percentage(100),
|
|
||||||
]
|
|
||||||
.as_ref(),
|
|
||||||
)
|
|
||||||
.split(area.inner(&Margin {
|
|
||||||
horizontal: 1,
|
|
||||||
vertical: 1,
|
|
||||||
}));
|
|
||||||
|
|
||||||
self.find_text.draw(f, chunks[0])?;
|
|
||||||
|
|
||||||
f.render_widget(
|
|
||||||
Paragraph::new(self.get_text_options())
|
|
||||||
.block(
|
|
||||||
Block::default()
|
|
||||||
.borders(Borders::TOP)
|
|
||||||
.border_style(self.theme.block(true)),
|
|
||||||
)
|
|
||||||
.alignment(Alignment::Left),
|
|
||||||
chunks[1],
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -328,29 +537,42 @@ impl Component for LogSearchPopupComponent {
|
||||||
.order(1),
|
.order(1),
|
||||||
);
|
);
|
||||||
|
|
||||||
out.push(
|
if matches!(self.mode, PopupMode::Search) {
|
||||||
CommandInfo::new(
|
out.push(
|
||||||
strings::commands::scroll_popup(&self.key_config),
|
CommandInfo::new(
|
||||||
true,
|
strings::commands::scroll_popup(
|
||||||
true,
|
&self.key_config,
|
||||||
)
|
),
|
||||||
.order(1),
|
true,
|
||||||
);
|
true,
|
||||||
|
)
|
||||||
out.push(
|
.order(1),
|
||||||
CommandInfo::new(
|
);
|
||||||
strings::commands::toggle_option(
|
out.push(
|
||||||
&self.key_config,
|
CommandInfo::new(
|
||||||
),
|
strings::commands::toggle_option(
|
||||||
self.option_selected(),
|
&self.key_config,
|
||||||
true,
|
),
|
||||||
)
|
self.option_selected(),
|
||||||
.order(1),
|
true,
|
||||||
);
|
)
|
||||||
|
.order(1),
|
||||||
|
);
|
||||||
|
out.push(
|
||||||
|
CommandInfo::new(
|
||||||
|
strings::commands::find_commit_sha(
|
||||||
|
&self.key_config,
|
||||||
|
),
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.order(1),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
out.push(CommandInfo::new(
|
out.push(CommandInfo::new(
|
||||||
strings::commands::confirm_action(&self.key_config),
|
strings::commands::confirm_action(&self.key_config),
|
||||||
!self.find_text.get_text().trim().is_empty(),
|
self.is_valid(),
|
||||||
self.visible,
|
self.visible,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
@ -362,39 +584,16 @@ impl Component for LogSearchPopupComponent {
|
||||||
&mut self,
|
&mut self,
|
||||||
event: &crossterm::event::Event,
|
event: &crossterm::event::Event,
|
||||||
) -> Result<EventState> {
|
) -> Result<EventState> {
|
||||||
if self.is_visible() {
|
if !self.is_visible() {
|
||||||
if let Event::Key(key) = &event {
|
return Ok(EventState::NotConsumed);
|
||||||
if key_match(key, self.key_config.keys.exit_popup) {
|
|
||||||
self.hide();
|
|
||||||
} else if key_match(key, self.key_config.keys.enter)
|
|
||||||
&& !self.find_text.get_text().trim().is_empty()
|
|
||||||
{
|
|
||||||
self.execute_search();
|
|
||||||
} else if key_match(
|
|
||||||
key,
|
|
||||||
self.key_config.keys.popup_up,
|
|
||||||
) {
|
|
||||||
self.move_selection(true);
|
|
||||||
} else if key_match(
|
|
||||||
key,
|
|
||||||
self.key_config.keys.popup_down,
|
|
||||||
) {
|
|
||||||
self.move_selection(false);
|
|
||||||
} else if key_match(
|
|
||||||
key,
|
|
||||||
self.key_config.keys.log_mark_commit,
|
|
||||||
) && self.option_selected()
|
|
||||||
{
|
|
||||||
self.toggle_option();
|
|
||||||
} else if !self.option_selected() {
|
|
||||||
self.find_text.event(event)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(EventState::Consumed);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(EventState::NotConsumed)
|
match self.mode {
|
||||||
|
PopupMode::Search => self.event_search_mode(event),
|
||||||
|
PopupMode::JumpCommitSha => {
|
||||||
|
self.event_commit_sha_mode(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_visible(&self) -> bool {
|
fn is_visible(&self) -> bool {
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,7 @@ pub struct KeysList {
|
||||||
pub log_reset_comit: GituiKeyEvent,
|
pub log_reset_comit: GituiKeyEvent,
|
||||||
pub log_reword_comit: GituiKeyEvent,
|
pub log_reword_comit: GituiKeyEvent,
|
||||||
pub log_find: GituiKeyEvent,
|
pub log_find: GituiKeyEvent,
|
||||||
|
pub find_commit_sha: GituiKeyEvent,
|
||||||
pub commit_amend: GituiKeyEvent,
|
pub commit_amend: GituiKeyEvent,
|
||||||
pub toggle_signoff: GituiKeyEvent,
|
pub toggle_signoff: GituiKeyEvent,
|
||||||
pub toggle_verify: GituiKeyEvent,
|
pub toggle_verify: GituiKeyEvent,
|
||||||
|
|
@ -176,6 +177,7 @@ impl Default for KeysList {
|
||||||
log_reset_comit: GituiKeyEvent { code: KeyCode::Char('R'), modifiers: KeyModifiers::SHIFT },
|
log_reset_comit: GituiKeyEvent { code: KeyCode::Char('R'), modifiers: KeyModifiers::SHIFT },
|
||||||
log_reword_comit: GituiKeyEvent { code: KeyCode::Char('r'), modifiers: KeyModifiers::empty() },
|
log_reword_comit: GituiKeyEvent { code: KeyCode::Char('r'), modifiers: KeyModifiers::empty() },
|
||||||
log_find: GituiKeyEvent { code: KeyCode::Char('f'), modifiers: KeyModifiers::empty() },
|
log_find: GituiKeyEvent { code: KeyCode::Char('f'), modifiers: KeyModifiers::empty() },
|
||||||
|
find_commit_sha: GituiKeyEvent::new(KeyCode::Char('j'), KeyModifiers::CONTROL),
|
||||||
commit_amend: GituiKeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL),
|
commit_amend: GituiKeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL),
|
||||||
toggle_signoff: GituiKeyEvent::new(KeyCode::Char('s'), KeyModifiers::CONTROL),
|
toggle_signoff: GituiKeyEvent::new(KeyCode::Char('s'), KeyModifiers::CONTROL),
|
||||||
toggle_verify: GituiKeyEvent::new(KeyCode::Char('f'), KeyModifiers::CONTROL),
|
toggle_verify: GituiKeyEvent::new(KeyCode::Char('f'), KeyModifiers::CONTROL),
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ pub static POPUP_TITLE_LOG_SEARCH: &str = "Search";
|
||||||
|
|
||||||
pub static POPUP_FAIL_COPY: &str = "Failed to copy text";
|
pub static POPUP_FAIL_COPY: &str = "Failed to copy text";
|
||||||
pub static POPUP_SUCCESS_COPY: &str = "Copied Text";
|
pub static POPUP_SUCCESS_COPY: &str = "Copied Text";
|
||||||
|
pub static POPUP_COMMIT_SHA_INVALID: &str = "Invalid commit sha";
|
||||||
|
|
||||||
pub mod symbol {
|
pub mod symbol {
|
||||||
pub const WHITESPACE: &str = "\u{00B7}"; //·
|
pub const WHITESPACE: &str = "\u{00B7}"; //·
|
||||||
|
|
@ -1672,4 +1673,17 @@ pub mod commands {
|
||||||
CMD_GROUP_BRANCHES,
|
CMD_GROUP_BRANCHES,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn find_commit_sha(
|
||||||
|
key_config: &SharedKeyConfig,
|
||||||
|
) -> CommandText {
|
||||||
|
CommandText::new(
|
||||||
|
format!(
|
||||||
|
"Search Hash [{}]",
|
||||||
|
key_config.get_hint(key_config.keys.find_commit_sha),
|
||||||
|
),
|
||||||
|
"find commit from sha",
|
||||||
|
CMD_GROUP_LOG,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue