@@ -3,6 +3,7 @@ use futures_util::future::BoxFuture;
33use futures_util::FutureExt;
44use helix_core::auto_pairs::AutoPairs;
55use helix_core::Range;
6+use log::debug;
67use serde::de::{self, Deserialize, Deserializer};
78use serde::Serialize;
89use std::borrow::Cow;
@@ -13,6 +14,8 @@ use std::future::Future;
1314use std::path::{Path, PathBuf};
1415use std::str::FromStr;
1516use std::sync::Arc;
17+use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
18+use tokio::sync::Mutex;
1619
1720use helix_core::{
1821 encoding,
@@ -83,6 +86,16 @@ impl Serialize for Mode {
8386 }
8487}
8588
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 {
92+ pub revision: usize,
93+ pub doc_id: DocumentId,
94+}
95+
96+pub type DocumentSaveEventResult = Result<DocumentSaveEvent, anyhow::Error>;
97+pub type DocumentSaveEventFuture = BoxFuture<'static, DocumentSaveEventResult>;
98+
8699pub struct Document {
87100 pub(crate) id: DocumentId,
88101 text: Rope,
@@ -118,6 +131,9 @@ pub struct Document {
118131 last_saved_revision: usize,
119132 version: i32, // should be usize?
120133 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>>>,
121137
122138 diagnostics: Vec<Diagnostic>,
123139 language_server: Option<Arc<helix_lsp::Client>>,
@@ -338,6 +354,7 @@ impl Document {
338354 let encoding = encoding.unwrap_or(encoding::UTF_8);
339355 let changes = ChangeSet::new(&text);
340356 let old_state = None;
357+ let (save_sender, save_receiver) = tokio::sync::mpsc::unbounded_channel();
341358
342359 Self {
343360 id: DocumentId::default(),
@@ -358,6 +375,9 @@ impl Document {
358375 savepoint: None,
359376 last_saved_revision: 0,
360377 modified_since_accessed: false,
378+ save_sender: Some(save_sender),
379+ save_receiver: Some(save_receiver),
380+ current_save: Arc::new(Mutex::new(None)),
361381 language_server: None,
362382 }
363383 }
@@ -492,29 +512,34 @@ impl Document {
492512 Some(fut.boxed())
493513 }
494514
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> {
496516 self.save_impl::<futures_util::future::Ready<_>>(None, force)
497517 }
498518
499519 pub fn format_and_save(
500520 &mut self,
501− formatting: Option<impl Future<Output = Result<Transaction, FormatterError>>>,
521+ formatting: Option<
522+ impl Future<Output = Result<Transaction, FormatterError>> + 'static + Send,
523+ >,
502524 force: bool,
503− ) -> impl Future<Output = anyhow::Result<()>> {
525+ ) -> anyhow::Result<()> {
504526 self.save_impl(formatting, force)
505527 }
506528
507− // TODO: do we need some way of ensuring two save operations on the same doc can't run at once?
508− // or is that handled by the OS/async layer
529+ // TODO: impl Drop to handle ensuring writes when closed
509530 /// The `Document`'s text is encoded according to its encoding and written to the file located
510531 /// at its `path()`.
511532 ///
512533 /// 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>(
514535 &mut self,
515536 formatting: Option<F>,
516537 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!");
541+ }
542+
518543 // we clone and move text + path into the future so that we asynchronously save the current
519544 // state without blocking any further edits.
520545
@@ -525,12 +550,13 @@ impl Document {
525550 let language_server = self.language_server.clone();
526551
527552 // 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();
529555
530556 let encoding = self.encoding;
531557
532558 // We encode the file according to the `Document`'s encoding.
533− async move {
559+ let save_event = async move {
534560 use tokio::fs::File;
535561 if let Some(parent) = path.parent() {
536562 // TODO: display a prompt asking the user if the directories should be created
@@ -563,9 +589,14 @@ impl Document {
563589 let mut file = File::create(path).await?;
564590 to_writer(&mut file, encoding, &text).await?;
565591
592+ let event = DocumentSaveEvent {
593+ revision: current_rev,
594+ doc_id,
595+ };
596+
566597 if let Some(language_server) = language_server {
567598 if !language_server.is_initialized() {
568− return Ok(());
599+ return Ok(event);
569600 }
570601 if let Some(notification) =
571602 language_server.text_document_did_save(identifier, &text)
@@ -574,8 +605,70 @@ impl Document {
574605 }
575606 }
576607
577− Ok(())
608+ Ok(event)
609+ };
610+
611+ self.save_sender
612+ .as_mut()
613+ .unwrap()
614+ .send(Box::pin(save_event))
615+ .map_err(|err| anyhow!("failed to send save event: {}", err))
616+ }
617+
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);
625+ }
626+
627+ // return early if the receiver is closed
628+ self.save_receiver.as_ref()?;
629+
630+ let save = match self.save_receiver.as_mut().unwrap().recv().await {
631+ Some(save) => save,
632+ None => {
633+ self.save_receiver = None;
634+ return None;
635+ }
636+ };
637+
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());
642+
643+ let result = (*current_save).as_mut().unwrap().await;
644+ *current_save = None;
645+
646+ debug!("save of '{:?}' result: {:?}", self.path(), result);
647+
648+ Some(result)
649+ }
650+
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;
578658 }
659+
660+ let mut final_result = None;
661+
662+ while let Some(save_event) = self.await_save().await {
663+ let is_err = save_event.is_err();
664+ final_result = Some(save_event);
665+
666+ if is_err {
667+ break;
668+ }
669+ }
670+
671+ final_result
579672 }
580673
581674 /// Detect the programming language based on the file type.
@@ -941,6 +1034,19 @@ impl Document {
9411034 self.last_saved_revision = current_revision;
9421035 }
9431036
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;
1040+ }
1041+
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);
1047+ current_revision
1048+ }
1049+
9441050 /// Corresponding language scope name. Usually `source.<lang>`.
9451051 pub fn language_scope(&self) -> Option<&str> {
9461052 self.language