@@ -3,6 +3,7 @@ use futures_util::future::BoxFuture;
3use futures_util::FutureExt;
3use futures_util::FutureExt;
4use helix_core::auto_pairs::AutoPairs;
4use helix_core::auto_pairs::AutoPairs;
5use helix_core::Range;
5use helix_core::Range;
6use serde::de::{self, Deserialize, Deserializer};
7use serde::de::{self, Deserialize, Deserializer};
7use serde::Serialize;
8use serde::Serialize;
8use std::borrow::Cow;
9use std::borrow::Cow;
@@ -13,6 +14,8 @@ use std::future::Future;
13use std::path::{Path, PathBuf};
14use std::path::{Path, PathBuf};
14use std::str::FromStr;
15use std::str::FromStr;
15use std::sync::Arc;
16use std::sync::Arc;
17+use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
18+use tokio::sync::Mutex;
17use helix_core::{
20use helix_core::{
@@ -83,6 +86,16 @@ impl Serialize for Mode {
89+/// A snapshot of the text of a document that we want to write out to disk
90+#[derive(Debug, Clone)]
91+pub struct DocumentSaveEvent {
93+ pub doc_id: DocumentId,
96+pub type DocumentSaveEventResult = Result<DocumentSaveEvent, anyhow::Error>;
97+pub type DocumentSaveEventFuture = BoxFuture<'static, DocumentSaveEventResult>;
86pub struct Document {
99pub struct Document {
87 pub(crate) id: DocumentId,
100 pub(crate) id: DocumentId,
88 text: Rope,
101 text: Rope,
@@ -118,6 +131,9 @@ pub struct Document {
118 last_saved_revision: usize,
131 last_saved_revision: usize,
119 version: i32, // should be usize?
132 version: i32, // should be usize?
120 pub(crate) modified_since_accessed: bool,
133 pub(crate) modified_since_accessed: bool,
134+ save_sender: Option<UnboundedSender<DocumentSaveEventFuture>>,
135+ save_receiver: Option<UnboundedReceiver<DocumentSaveEventFuture>>,
136+ current_save: Arc<Mutex<Option<DocumentSaveEventFuture>>>,
122 diagnostics: Vec<Diagnostic>,
138 diagnostics: Vec<Diagnostic>,
123 language_server: Option<Arc<helix_lsp::Client>>,
139 language_server: Option<Arc<helix_lsp::Client>>,
@@ -338,6 +354,7 @@ impl Document {
338 let encoding = encoding.unwrap_or(encoding::UTF_8);
354 let encoding = encoding.unwrap_or(encoding::UTF_8);
339 let changes = ChangeSet::new(&text);
355 let changes = ChangeSet::new(&text);
340 let old_state = None;
356 let old_state = None;
357+ let (save_sender, save_receiver) = tokio::sync::mpsc::unbounded_channel();
343 id: DocumentId::default(),
360 id: DocumentId::default(),
@@ -358,6 +375,9 @@ impl Document {
358 savepoint: None,
375 savepoint: None,
359 last_saved_revision: 0,
376 last_saved_revision: 0,
360 modified_since_accessed: false,
377 modified_since_accessed: false,
378+ save_sender: Some(save_sender),
379+ save_receiver: Some(save_receiver),
380+ current_save: Arc::new(Mutex::new(None)),
361 language_server: None,
381 language_server: None,
@@ -492,29 +512,34 @@ impl Document {
492 Some(fut.boxed())
512 Some(fut.boxed())
495− pub fn save(&mut self, force: bool) -> impl Future<Output = Result<(), anyhow::Error>> {
515+ pub fn save(&mut self, force: bool) -> Result<(), anyhow::Error> {
496 self.save_impl::<futures_util::future::Ready<_>>(None, force)
516 self.save_impl::<futures_util::future::Ready<_>>(None, force)
499 pub fn format_and_save(
519 pub fn format_and_save(
500 &mut self,
520 &mut self,
501− formatting: Option<impl Future<Output = Result<Transaction, FormatterError>>>,
522+ impl Future<Output = Result<Transaction, FormatterError>> + 'static + Send,
502 force: bool,
524 force: bool,
503− ) -> impl Future<Output = anyhow::Result<()>> {
525+ ) -> anyhow::Result<()> {
504 self.save_impl(formatting, force)
526 self.save_impl(formatting, force)
507− // TODO: do we need some way of ensuring two save operations on the same doc can't run at once?
529+ // TODO: impl Drop to handle ensuring writes when closed
508− // or is that handled by the OS/async layer
509 /// The `Document`'s text is encoded according to its encoding and written to the file located
530 /// The `Document`'s text is encoded according to its encoding and written to the file located
510 /// at its `path()`.
531 /// at its `path()`.
512 /// If `formatting` is present, it supplies some changes that we apply to the text before saving.
533 /// If `formatting` is present, it supplies some changes that we apply to the text before saving.
513− fn save_impl<F: Future<Output = Result<Transaction, FormatterError>>>(
534+ fn save_impl<F: Future<Output = Result<Transaction, FormatterError>> + 'static + Send>(
514 &mut self,
535 &mut self,
515 formatting: Option<F>,
536 formatting: Option<F>,
516 force: bool,
537 force: bool,
517− ) -> impl Future<Output = Result<(), anyhow::Error>> {
538+ ) -> Result<(), anyhow::Error> {
539+ if self.save_sender.is_none() {
540+ bail!("saves are closed for this document!");
518 // we clone and move text + path into the future so that we asynchronously save the current
543 // we clone and move text + path into the future so that we asynchronously save the current
519 // state without blocking any further edits.
544 // state without blocking any further edits.
@@ -525,12 +550,13 @@ impl Document {
525 let language_server = self.language_server.clone();
550 let language_server = self.language_server.clone();
527 // mark changes up to now as saved
552 // mark changes up to now as saved
528− self.reset_modified();
553+ let current_rev = self.get_current_revision();
554+ let doc_id = self.id();
530 let encoding = self.encoding;
556 let encoding = self.encoding;
532 // We encode the file according to the `Document`'s encoding.
558 // We encode the file according to the `Document`'s encoding.
533− async move {
559+ let save_event = async move {
534 use tokio::fs::File;
560 use tokio::fs::File;
535 if let Some(parent) = path.parent() {
561 if let Some(parent) = path.parent() {
536 // TODO: display a prompt asking the user if the directories should be created
562 // TODO: display a prompt asking the user if the directories should be created
@@ -563,9 +589,14 @@ impl Document {
563 let mut file = File::create(path).await?;
589 let mut file = File::create(path).await?;
564 to_writer(&mut file, encoding, &text).await?;
590 to_writer(&mut file, encoding, &text).await?;
592+ let event = DocumentSaveEvent {
593+ revision: current_rev,
566 if let Some(language_server) = language_server {
597 if let Some(language_server) = language_server {
567 if !language_server.is_initialized() {
598 if !language_server.is_initialized() {
568− return Ok(());
599+ return Ok(event);
570 if let Some(notification) =
601 if let Some(notification) =
571 language_server.text_document_did_save(identifier, &text)
602 language_server.text_document_did_save(identifier, &text)
@@ -574,8 +605,70 @@ impl Document {
577− Ok(())
608+ Ok(event)
614+ .send(Box::pin(save_event))
615+ .map_err(|err| anyhow!("failed to send save event: {}", err))
618+ pub async fn await_save(&mut self) -> Option<DocumentSaveEventResult> {
619+ let mut current_save = self.current_save.lock().await;
620+ if let Some(ref mut save) = *current_save {
621+ let result = save.await;
622+ *current_save = None;
623+ debug!("save of '{:?}' result: {:?}", self.path(), result);
624+ return Some(result);
627+ // return early if the receiver is closed
628+ self.save_receiver.as_ref()?;
630+ let save = match self.save_receiver.as_mut().unwrap().recv().await {
633+ self.save_receiver = None;
638+ // save a handle to the future so that when a poll on this
639+ // function gets cancelled, we don't lose it
640+ *current_save = Some(save);
641+ debug!("awaiting save of '{:?}'", self.path());
643+ let result = (*current_save).as_mut().unwrap().await;
644+ *current_save = None;
646+ debug!("save of '{:?}' result: {:?}", self.path(), result);
651+ /// Prepares the Document for being closed by stopping any new writes
652+ /// and flushing through the queue of pending writes. If any fail,
653+ /// it stops early before emptying the rest of the queue. Callers
654+ /// should keep calling until it returns None.
655+ pub async fn close(&mut self) -> Option<DocumentSaveEventResult> {
656+ if self.save_sender.is_some() {
657+ self.save_sender = None;
660+ let mut final_result = None;
662+ while let Some(save_event) = self.await_save().await {
663+ let is_err = save_event.is_err();
664+ final_result = Some(save_event);
581 /// Detect the programming language based on the file type.
674 /// Detect the programming language based on the file type.
@@ -941,6 +1034,19 @@ impl Document {
941 self.last_saved_revision = current_revision;
1034 self.last_saved_revision = current_revision;
1037+ /// Set the document's latest saved revision to the given one.
1038+ pub fn set_last_saved_revision(&mut self, rev: usize) {
1039+ self.last_saved_revision = rev;
1042+ /// Get the current revision number
1043+ pub fn get_current_revision(&mut self) -> usize {
1044+ let history = self.history.take();
1045+ let current_revision = history.current_revision();
1046+ self.history.set(history);
944 /// Corresponding language scope name. Usually `source.<lang>`.
1050 /// Corresponding language scope name. Usually `source.<lang>`.
945 pub fn language_scope(&self) -> Option<&str> {
1051 pub fn language_scope(&self) -> Option<&str> {
946 self.language
1052 self.language