This commit is contained in:
吴杨帆 2026-05-17 18:21:59 +08:00 committed by GitHub
commit c10ae8027c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 110 additions and 45 deletions

View file

@ -437,6 +437,10 @@ impl App {
self.select_branch_popup.update_git(ev)?;
}
if let AsyncNotification::App(AsyncAppNotification::StashSaveDone) = ev {
self.stashmsg_popup.on_stash_save_done();
}
self.files_tab.update_async(ev)?;
self.blame_file_popup.update_async(ev)?;
self.revision_files_popup.update(ev)?;

View file

@ -134,6 +134,8 @@ pub enum SyntaxHighlightProgress {
pub enum AsyncAppNotification {
///
SyntaxHighlighting(SyntaxHighlightProgress),
/// background stash in `StashMsgPopup` finished
StashSaveDone,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]

View file

@ -5,14 +5,22 @@ use crate::components::{
use crate::{
app::Environment,
keys::{key_match, SharedKeyConfig},
queue::{AppTabs, InternalEvent, Queue},
queue::{AppTabs, InternalEvent, NeedsUpdate, Queue},
strings,
tabs::StashingOptions,
AsyncAppNotification,
};
use anyhow::Result;
use asyncgit::sync::{self, RepoPathRef};
use crossbeam_channel::Sender;
use crossterm::event::Event;
use ratatui::{layout::Rect, Frame};
use std::{
sync::{Arc, Mutex},
thread,
};
const STASHING_MSG: &str = "Stashing...";
pub struct StashMsgPopup {
repo: RepoPathRef,
@ -20,6 +28,9 @@ pub struct StashMsgPopup {
input: TextInputComponent,
queue: Queue,
key_config: SharedKeyConfig,
sender_app: Sender<AsyncAppNotification>,
stashing: bool,
stash_result: Arc<Mutex<Option<Result<(), String>>>>,
}
impl DrawableComponent for StashMsgPopup {
@ -39,13 +50,15 @@ impl Component for StashMsgPopup {
if self.is_visible() || force_all {
self.input.commands(out, force_all);
out.push(CommandInfo::new(
strings::commands::stashing_confirm_msg(
&self.key_config,
),
true,
true,
));
if !self.stashing {
out.push(CommandInfo::new(
strings::commands::stashing_confirm_msg(
&self.key_config,
),
true,
true,
));
}
}
visibility_blocking(self)
@ -53,51 +66,19 @@ impl Component for StashMsgPopup {
fn event(&mut self, ev: &Event) -> Result<EventState> {
if self.is_visible() {
if self.stashing {
return Ok(EventState::Consumed);
}
if self.input.event(ev)?.is_consumed() {
return Ok(EventState::Consumed);
}
if let Event::Key(e) = ev {
if key_match(e, self.key_config.keys.enter) {
let result = sync::stash_save(
&self.repo.borrow(),
if self.input.get_text().is_empty() {
None
} else {
Some(self.input.get_text())
},
self.options.stash_untracked,
self.options.keep_index,
);
match result {
Ok(_) => {
self.input.clear();
self.hide();
self.queue.push(
InternalEvent::TabSwitch(
AppTabs::Stashlist,
),
);
}
Err(e) => {
self.hide();
log::error!(
"e: {} (options: {:?})",
e,
self.options
);
self.queue.push(
InternalEvent::ShowErrorMsg(format!(
"stash error:\n{}\noptions:\n{:?}",
e, self.options
)),
);
}
}
self.start_stash_async()?;
}
// stop key event propagation
return Ok(EventState::Consumed);
}
}
@ -110,9 +91,11 @@ impl Component for StashMsgPopup {
fn hide(&mut self) {
self.input.hide();
self.stashing = false;
}
fn show(&mut self) -> Result<()> {
self.stashing = false;
self.input.show()?;
Ok(())
@ -134,6 +117,9 @@ impl StashMsgPopup {
.with_input_type(InputType::Singleline),
key_config: env.key_config.clone(),
repo: env.repo.clone(),
sender_app: env.sender_app.clone(),
stashing: false,
stash_result: Arc::new(Mutex::new(None)),
}
}
@ -141,4 +127,77 @@ impl StashMsgPopup {
pub const fn options(&mut self, options: StashingOptions) {
self.options = options;
}
///
pub fn on_stash_save_done(&mut self) {
if !self.stashing {
return;
}
let error = self
.stash_result
.lock()
.ok()
.and_then(|mut guard| guard.take())
.and_then(Result::err);
self.complete_stash(error);
}
fn complete_stash(&mut self, error: Option<String>) {
self.stashing = false;
if let Some(msg) = error {
self.hide();
self.queue.push(InternalEvent::ShowErrorMsg(msg));
} else {
self.input.clear();
self.hide();
self.queue
.push(InternalEvent::TabSwitch(AppTabs::Stashlist));
self.queue.push(InternalEvent::Update(NeedsUpdate::ALL));
}
}
fn start_stash_async(&mut self) -> Result<()> {
let repo_path = self.repo.borrow().clone();
let message = if self.input.get_text().is_empty() {
None
} else {
Some(self.input.get_text().to_string())
};
self.stashing = true;
self.input.enabled(false);
self.input.set_text(STASHING_MSG.into());
let options = self.options;
let sender_app = self.sender_app.clone();
let stash_result = Arc::clone(&self.stash_result);
*stash_result.lock().map_err(|_| {
anyhow::anyhow!("stash result lock poisoned")
})? = None;
thread::spawn(move || {
let result = sync::stash_save(
&repo_path,
message.as_deref(),
options.stash_untracked,
options.keep_index,
)
.map(|_| ())
.map_err(|e| {
format!(
"stash error:\n{e}\noptions:\n{options:?}",
)
});
if let Ok(mut guard) = stash_result.lock() {
*guard = Some(result);
}
let _ = sender_app.send(AsyncAppNotification::StashSaveDone);
});
Ok(())
}
}