Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 31 additions & 24 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -457,17 +457,18 @@ impl App<'_> {
_ => return,
};
let (commit, _, refs) = selected_commit_details(self.repository, &commit_list_state);
match build_external_command_parameters(
let result = build_external_command_parameters_and_exec_command(
&commit,
&refs,
user_command_number,
self.app_status.view_area,
&self.ctx,
) {
Ok(params) => {
);
match result {
Ok(output) => {
self.view = View::of_user_command(
commit_list_state,
params,
output,
user_command_number,
self.ctx.clone(),
self.ec.sender(),
Expand All @@ -487,14 +488,13 @@ impl App<'_> {
_ => return,
};
let (commit, _, refs) = selected_commit_details(self.repository, commit_list_state);
let result = build_external_command_parameters(
let result = build_external_command_parameters_and_exec_command(
&commit,
&refs,
user_command_number,
self.app_status.view_area,
&self.ctx,
)
.and_then(exec_user_command);
);
match result {
Ok(_) => {
if extract_user_command_refresh_by_number(user_command_number, &self.ctx) {
Expand Down Expand Up @@ -591,7 +591,7 @@ impl App<'_> {
view.select_older_commit(
self.repository,
self.app_status.view_area,
build_external_command_parameters,
build_external_command_parameters_and_exec_command,
);
}
}
Expand All @@ -603,7 +603,7 @@ impl App<'_> {
view.select_newer_commit(
self.repository,
self.app_status.view_area,
build_external_command_parameters,
build_external_command_parameters_and_exec_command,
);
}
}
Expand All @@ -615,7 +615,7 @@ impl App<'_> {
view.select_parent_commit(
self.repository,
self.app_status.view_area,
build_external_command_parameters,
build_external_command_parameters_and_exec_command,
);
}
}
Expand Down Expand Up @@ -730,23 +730,30 @@ fn extract_user_command_refresh_by_number(user_command_number: usize, ctx: &AppC
.unwrap_or_default()
}

fn build_external_command_parameters(
fn build_external_command_parameters_and_exec_command(
commit: &Commit,
refs: &[Ref],
user_command_number: usize,
view_area: Rect,
ctx: &AppContext,
) -> Result<ExternalCommandParameters, String> {
let command = extract_user_command_by_number(user_command_number, ctx)?
.commands
.iter()
.map(String::to_string)
.collect();
let target_hash = commit.commit_hash.as_str().to_string();
let parent_hashes: Vec<String> = commit
) -> Result<String, String> {
build_external_command_parameters(commit, refs, user_command_number, view_area, ctx)
.and_then(exec_user_command)
}

fn build_external_command_parameters<'a>(
commit: &'a Commit,
refs: &'a [Ref],
user_command_number: usize,
view_area: Rect,
ctx: &'a AppContext,
) -> Result<ExternalCommandParameters<'a>, String> {
let command = &extract_user_command_by_number(user_command_number, ctx)?.commands;
let target_hash = commit.commit_hash.as_str();
let parent_hashes = commit
.parent_commit_hashes
.iter()
.map(|c| c.as_str().to_string())
.map(|c| c.as_str())
.collect();

let mut all_refs = vec![];
Expand All @@ -755,12 +762,12 @@ fn build_external_command_parameters(
let mut tags = vec![];
for r in refs {
match r {
Ref::Tag { .. } => tags.push(r.name().to_string()),
Ref::Branch { .. } => branches.push(r.name().to_string()),
Ref::RemoteBranch { .. } => remote_branches.push(r.name().to_string()),
Ref::Tag { .. } => tags.push(r.name()),
Ref::Branch { .. } => branches.push(r.name()),
Ref::RemoteBranch { .. } => remote_branches.push(r.name()),
Ref::Stash { .. } => continue, // skip stashes
}
all_refs.push(r.name().to_string());
all_refs.push(r.name());
}

let area_width = view_area.width.saturating_sub(4); // minus the left and right padding
Expand Down
41 changes: 22 additions & 19 deletions src/external.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,14 @@ fn copy_to_clipboard_auto(value: String) -> Result<(), String> {
})
}

pub struct ExternalCommandParameters {
pub command: Vec<String>,
pub target_hash: String,
pub parent_hashes: Vec<String>,
pub all_refs: Vec<String>,
pub branches: Vec<String>,
pub remote_branches: Vec<String>,
pub tags: Vec<String>,
pub struct ExternalCommandParameters<'a> {
pub command: &'a [String],
pub target_hash: &'a str,
pub parent_hashes: Vec<&'a str>,
pub all_refs: Vec<&'a str>,
pub branches: Vec<&'a str>,
pub remote_branches: Vec<&'a str>,
pub tags: Vec<&'a str>,
pub area_width: u16,
pub area_height: u16,
}
Expand Down Expand Up @@ -119,16 +119,23 @@ pub fn exec_user_command_suspend(params: ExternalCommandParameters) -> Result<()
}

fn build_user_command(params: &ExternalCommandParameters) -> Vec<String> {
fn to_vec(ss: &[&str]) -> Vec<String> {
ss.iter().map(|s| s.to_string()).collect()
}
let mut command = Vec::new();
for arg in &params.command {
for arg in params.command {
if !arg.contains(USER_COMMAND_MARKER_PREFIX) {
command.push(arg.clone());
continue;
}
match arg.as_str() {
// If the marker is used as a standalone argument, expand it into multiple arguments.
// This allows the command to receive each item as a separate argument and correctly handle items that contain spaces.
USER_COMMAND_BRANCHES_MARKER => command.extend(params.branches.clone()),
USER_COMMAND_REMOTE_BRANCHES_MARKER => command.extend(params.remote_branches.clone()),
USER_COMMAND_TAGS_MARKER => command.extend(params.tags.clone()),
USER_COMMAND_REFS_MARKER => command.extend(params.all_refs.clone()),
USER_COMMAND_PARENT_HASHES_MARKER => command.extend(params.parent_hashes.clone()),
USER_COMMAND_BRANCHES_MARKER => command.extend(to_vec(&params.branches)),
USER_COMMAND_REMOTE_BRANCHES_MARKER => command.extend(to_vec(&params.remote_branches)),
USER_COMMAND_TAGS_MARKER => command.extend(to_vec(&params.tags)),
USER_COMMAND_REFS_MARKER => command.extend(to_vec(&params.all_refs)),
USER_COMMAND_PARENT_HASHES_MARKER => command.extend(to_vec(&params.parent_hashes)),
// Otherwise, replace the marker within the single argument string.
_ => command.push(replace_command_arg(arg, params)),
}
Expand All @@ -137,12 +144,8 @@ fn build_user_command(params: &ExternalCommandParameters) -> Vec<String> {
}

fn replace_command_arg(s: &str, params: &ExternalCommandParameters) -> String {
if !s.contains(USER_COMMAND_MARKER_PREFIX) {
return s.to_string();
}

let sep = " ";
let target_hash = &params.target_hash;
let target_hash = params.target_hash;
let first_parent_hash = &params.parent_hashes.first().cloned().unwrap_or_default();
let parent_hashes = &params.parent_hashes.join(sep);
let all_refs = &params.all_refs.join(sep);
Expand Down
44 changes: 19 additions & 25 deletions src/view/user_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ use ratatui::{
use crate::{
app::AppContext,
event::{AppEvent, Sender, UserEvent, UserEventWithCount},
external::{exec_user_command, ExternalCommandParameters},
git::{Commit, Ref, Repository},
view::{ListRefreshViewContext, RefreshViewContext, UserCommandRefreshViewContext},
widget::{
Expand All @@ -21,8 +20,7 @@ use crate::{
},
};

type BuildParamsFn =
fn(&Commit, &[Ref], usize, Rect, &AppContext) -> Result<ExternalCommandParameters, String>;
type ExecCommandFn = fn(&Commit, &[Ref], usize, Rect, &AppContext) -> Result<String, String>;

#[derive(Debug)]
pub struct UserCommandView<'a> {
Expand All @@ -40,13 +38,13 @@ pub struct UserCommandView<'a> {
impl<'a> UserCommandView<'a> {
pub fn new(
commit_list_state: CommitListState<'a>,
params: ExternalCommandParameters,
command_output: String,
user_command_number: usize,
ctx: Rc<AppContext>,
tx: Sender,
) -> UserCommandView<'a> {
let user_command_output_lines = build_user_command_output_lines(params, ctx.clone())
.unwrap_or_else(|err| {
let user_command_output_lines =
build_user_command_output_lines(command_output, ctx.clone()).unwrap_or_else(|err| {
tx.send(AppEvent::NotifyError(err));
vec![]
});
Expand Down Expand Up @@ -182,9 +180,9 @@ impl<'a> UserCommandView<'a> {
&mut self,
repository: &Repository,
view_area: Rect,
build_parameters: BuildParamsFn,
exec_command: ExecCommandFn,
) {
self.update_selected_commit(repository, view_area, build_parameters, |state| {
self.update_selected_commit(repository, view_area, exec_command, |state| {
state.select_next()
});
}
Expand All @@ -193,9 +191,9 @@ impl<'a> UserCommandView<'a> {
&mut self,
repository: &Repository,
view_area: Rect,
build_parameters: BuildParamsFn,
exec_command: ExecCommandFn,
) {
self.update_selected_commit(repository, view_area, build_parameters, |state| {
self.update_selected_commit(repository, view_area, exec_command, |state| {
state.select_prev()
});
}
Expand All @@ -204,9 +202,9 @@ impl<'a> UserCommandView<'a> {
&mut self,
repository: &Repository,
view_area: Rect,
build_parameters: BuildParamsFn,
exec_command: ExecCommandFn,
) {
self.update_selected_commit(repository, view_area, build_parameters, |state| {
self.update_selected_commit(repository, view_area, exec_command, |state| {
state.select_parent()
});
}
Expand All @@ -215,7 +213,7 @@ impl<'a> UserCommandView<'a> {
&mut self,
repository: &Repository,
view_area: Rect,
build_parameters: BuildParamsFn,
exec_command: ExecCommandFn,
update_commit_list_state: F,
) where
F: FnOnce(&mut CommitListState<'a>),
Expand All @@ -226,14 +224,14 @@ impl<'a> UserCommandView<'a> {
let selected = commit_list_state.selected_commit_hash().clone();
let (commit, _) = repository.commit_detail(&selected);
let refs: Vec<Ref> = repository.refs(&selected).into_iter().cloned().collect();
self.user_command_output_lines = build_parameters(
self.user_command_output_lines = exec_command(
&commit,
&refs,
self.user_command_number,
view_area,
&self.ctx,
)
.and_then(|params| build_user_command_output_lines(params, self.ctx.clone()))
.and_then(|output| build_user_command_output_lines(output, self.ctx.clone()))
.unwrap_or_else(|err| {
self.tx.send(AppEvent::NotifyError(err));
vec![]
Expand Down Expand Up @@ -267,17 +265,13 @@ impl<'a> UserCommandView<'a> {
}

fn build_user_command_output_lines<'a>(
params: ExternalCommandParameters,
command_output: String,
ctx: Rc<AppContext>,
) -> Result<Vec<Line<'a>>, String> {
let tab_spaces = " ".repeat(ctx.core_config.user_command.tab_width as usize);
exec_user_command(params)
.and_then(|output| {
output
.replace('\t', &tab_spaces) // tab is not rendered correctly, so replace it
.into_text()
.map(|t| t.into_iter().collect())
.map_err(|e| e.to_string())
})
.map_err(|err| format!("Failed to execute command: {err}"))
command_output
.replace('\t', &tab_spaces) // tab is not rendered correctly, so replace it
.into_text()
.map(|t| t.into_iter().collect())
.map_err(|e| e.to_string())
}
5 changes: 2 additions & 3 deletions src/view/views.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ use ratatui::{crossterm::event::KeyEvent, layout::Rect, Frame};
use crate::{
app::AppContext,
event::{Sender, UserEventWithCount},
external::ExternalCommandParameters,
git::{Commit, FileChange, Ref},
view::{
detail::DetailView, help::HelpView, list::ListView, refs::RefsView,
Expand Down Expand Up @@ -76,14 +75,14 @@ impl<'a> View<'a> {

pub fn of_user_command(
commit_list_state: CommitListState<'a>,
params: ExternalCommandParameters,
command_output: String,
user_command_number: usize,
ctx: Rc<AppContext>,
tx: Sender,
) -> Self {
View::UserCommand(Box::new(UserCommandView::new(
commit_list_state,
params,
command_output,
user_command_number,
ctx,
tx,
Expand Down
Loading