From 84e24b33dcda16d1d64805f34dcc02d82d0de8f1 Mon Sep 17 00:00:00 2001
From: Gabriel Dinner-David <82682503+gabydd@users.noreply.github.com>
Date: Mon, 8 Jan 2024 20:21:16 -0500
Subject: [PATCH] make sure to sync views when applying edits to unfocused
 views (#9173)

---
 helix-term/src/commands/lsp.rs   | 17 +++--------------
 helix-term/src/commands/typed.rs | 29 +++++++++--------------------
 helix-view/src/editor.rs         | 24 ++++++++++++++++++++++++
 3 files changed, 36 insertions(+), 34 deletions(-)

diff --git a/helix-term/src/commands/lsp.rs b/helix-term/src/commands/lsp.rs
index 34ffa529..0096e6aa 100644
--- a/helix-term/src/commands/lsp.rs
+++ b/helix-term/src/commands/lsp.rs
@@ -896,7 +896,6 @@ pub fn apply_workspace_edit(
             }
         };
 
-        let current_view_id = view!(editor).id;
         let doc_id = match editor.open(&path, Action::Load) {
             Ok(doc_id) => doc_id,
             Err(err) => {
@@ -907,7 +906,7 @@ pub fn apply_workspace_edit(
             }
         };
 
-        let doc = doc_mut!(editor, &doc_id);
+        let doc = doc!(editor, &doc_id);
         if let Some(version) = version {
             if version != doc.version() {
                 let err = format!("outdated workspace edit for {path:?}");
@@ -918,18 +917,8 @@ pub fn apply_workspace_edit(
         }
 
         // Need to determine a view for apply/append_changes_to_history
-        let selections = doc.selections();
-        let view_id = if selections.contains_key(&current_view_id) {
-            // use current if possible
-            current_view_id
-        } else {
-            // Hack: we take the first available view_id
-            selections
-                .keys()
-                .next()
-                .copied()
-                .expect("No view_id available")
-        };
+        let view_id = editor.get_synced_view_id(doc_id);
+        let doc = doc_mut!(editor, &doc_id);
 
         let transaction = helix_lsp::util::generate_transaction_from_edits(
             doc.text(),
diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs
index 208854e8..f1c3cb71 100644
--- a/helix-term/src/commands/typed.rs
+++ b/helix-term/src/commands/typed.rs
@@ -674,13 +674,15 @@ pub fn write_all_impl(
     let mut errors: Vec<&'static str> = Vec::new();
     let config = cx.editor.config();
     let jobs = &mut cx.jobs;
-    let current_view = view!(cx.editor);
-
     let saves: Vec<_> = cx
         .editor
         .documents
-        .values_mut()
-        .filter_map(|doc| {
+        .keys()
+        .cloned()
+        .collect::<Vec<_>>()
+        .into_iter()
+        .filter_map(|id| {
+            let doc = doc!(cx.editor, &id);
             if !doc.is_modified() {
                 return None;
             }
@@ -691,22 +693,9 @@ pub fn write_all_impl(
                 return None;
             }
 
-            // Look for a view to apply the formatting change to. If the document
-            // is in the current view, just use that. Otherwise, since we don't
-            // have any other metric available for better selection, just pick
-            // the first view arbitrarily so that we still commit the document
-            // state for undos. If somehow we have a document that has not been
-            // initialized with any view, initialize it with the current view.
-            let target_view = if doc.selections().contains_key(&current_view.id) {
-                current_view.id
-            } else if let Some(view) = doc.selections().keys().next() {
-                *view
-            } else {
-                doc.ensure_view_init(current_view.id);
-                current_view.id
-            };
-
-            Some((doc.id(), target_view))
+            // Look for a view to apply the formatting change to.
+            let target_view = cx.editor.get_synced_view_id(doc.id());
+            Some((id, target_view))
         })
         .collect();
 
diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs
index c018668c..f13df213 100644
--- a/helix-view/src/editor.rs
+++ b/helix-view/src/editor.rs
@@ -1910,6 +1910,30 @@ impl Editor {
             .as_ref()
             .and_then(|debugger| debugger.current_stack_frame())
     }
+
+    /// Returns the id of a view that this doc contains a selection for,
+    /// making sure it is synced with the current changes
+    /// if possible or there are no selections returns current_view
+    /// otherwise uses an arbitrary view
+    pub fn get_synced_view_id(&mut self, id: DocumentId) -> ViewId {
+        let current_view = view_mut!(self);
+        let doc = self.documents.get_mut(&id).unwrap();
+        if doc.selections().contains_key(&current_view.id) {
+            // only need to sync current view if this is not the current doc
+            if current_view.doc != id {
+                current_view.sync_changes(doc);
+            }
+            current_view.id
+        } else if let Some(view_id) = doc.selections().keys().next() {
+            let view_id = *view_id;
+            let view = self.tree.get_mut(view_id);
+            view.sync_changes(doc);
+            view_id
+        } else {
+            doc.ensure_view_init(current_view.id);
+            current_view.id
+        }
+    }
 }
 
 fn try_restore_indent(doc: &mut Document, view: &mut View) {