fix(command): write-quit: do not quit if write fails

During write-quit, if the file fails to be written for any reason, helix
will still quit without saving the changes. This fixes this behavior by
introducing fallibility to the asynchronous job queues. This will also
benefit all contexts which may depend on these job queues.

Fixes #1575
pull/2359/head
Skyler Hawthorne 3 years ago
parent fac36bc5ea
commit 41bf1d5811

@ -814,7 +814,7 @@ impl Application {
} }
pub async fn close(&mut self) -> anyhow::Result<()> { pub async fn close(&mut self) -> anyhow::Result<()> {
self.jobs.finish().await; self.jobs.finish().await?;
if self.editor.close_language_servers(None).await.is_err() { if self.editor.close_language_servers(None).await.is_err() {
log::error!("Timed out waiting for language servers to shutdown"); log::error!("Timed out waiting for language servers to shutdown");

@ -233,6 +233,7 @@ fn write_impl(
doc.detect_language(cx.editor.syn_loader.clone()); doc.detect_language(cx.editor.syn_loader.clone());
let _ = cx.editor.refresh_language_server(id); let _ = cx.editor.refresh_language_server(id);
} }
Ok(()) Ok(())
} }
@ -422,6 +423,7 @@ fn write_quit(
event: PromptEvent, event: PromptEvent,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
write_impl(cx, args.first(), false)?; write_impl(cx, args.first(), false)?;
helix_lsp::block_on(cx.jobs.finish())?;
quit(cx, &[], event) quit(cx, &[], event)
} }

@ -2,7 +2,7 @@ use helix_view::Editor;
use crate::compositor::Compositor; use crate::compositor::Compositor;
use futures_util::future::{self, BoxFuture, Future, FutureExt}; use futures_util::future::{BoxFuture, Future, FutureExt};
use futures_util::stream::{FuturesUnordered, StreamExt}; use futures_util::stream::{FuturesUnordered, StreamExt};
pub type Callback = Box<dyn FnOnce(&mut Editor, &mut Compositor) + Send>; pub type Callback = Box<dyn FnOnce(&mut Editor, &mut Compositor) + Send>;
@ -93,9 +93,21 @@ impl Jobs {
} }
/// Blocks until all the jobs that need to be waited on are done. /// Blocks until all the jobs that need to be waited on are done.
pub async fn finish(&mut self) { pub async fn finish(&mut self) -> anyhow::Result<()> {
let wait_futures = std::mem::take(&mut self.wait_futures);
log::debug!("waiting on jobs..."); log::debug!("waiting on jobs...");
wait_futures.for_each(|_| future::ready(())).await let mut wait_futures = std::mem::take(&mut self.wait_futures);
while let (Some(job), tail) = wait_futures.into_future().await {
match job {
Ok(_) => {
wait_futures = tail;
}
Err(e) => {
self.wait_futures = tail;
return Err(e);
}
}
}
Ok(())
} }
} }

@ -9,7 +9,6 @@ use helix_term::application::Application;
use super::*; use super::*;
#[tokio::test] #[tokio::test]
#[ignore]
async fn test_write_quit_fail() -> anyhow::Result<()> { async fn test_write_quit_fail() -> anyhow::Result<()> {
let file = helpers::new_readonly_tempfile()?; let file = helpers::new_readonly_tempfile()?;

Loading…
Cancel
Save