Fix an issue that caused a reopened buffer to use UTF-8 even if the

R Aadarsh created

associated file was in a different encoding, rather than showing an
error.

Change summary

crates/fs/src/encodings.rs         |  3 -
crates/language/src/buffer.rs      | 17 ++++++++++++
crates/project/src/buffer_store.rs | 11 ++++++++
crates/project/src/image_store.rs  |  2 +
crates/workspace/src/workspace.rs  |  4 ++
crates/worktree/src/worktree.rs    | 44 +++++++++++++++++++++++++++++++
6 files changed, 78 insertions(+), 3 deletions(-)

Detailed changes

crates/fs/src/encodings.rs 🔗

@@ -11,6 +11,7 @@ use encoding_rs::Encoding;
 
 /// A wrapper around `encoding_rs::Encoding` to implement `Send` and `Sync`.
 /// Since the reference is static, it is safe to send it across threads.
+#[derive(Copy)]
 pub struct EncodingWrapper(pub &'static Encoding);
 
 impl Debug for EncodingWrapper {
@@ -59,8 +60,6 @@ impl EncodingWrapper {
         buffer_encoding: Option<Arc<Mutex<&'static Encoding>>>,
     ) -> Result<String> {
         // Check if the input starts with a BOM for UTF-16 encodings only if detect_utf16 is true.
-        println!("{}", force);
-        println!("{}", detect_utf16);
         if detect_utf16 {
             if let Some(encoding) = match input.get(..2) {
                 Some([0xFF, 0xFE]) => Some(encoding_rs::UTF_16LE),

crates/language/src/buffer.rs 🔗

@@ -128,6 +128,7 @@ pub struct Buffer {
     change_bits: Vec<rc::Weak<Cell<bool>>>,
     _subscriptions: Vec<gpui::Subscription>,
     pub encoding: Arc<std::sync::Mutex<&'static Encoding>>,
+    pub observe_file_encoding: Option<gpui::Subscription>,
 }
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
@@ -373,6 +374,10 @@ pub trait File: Send + Sync + Any {
 
     /// Return whether Zed considers this to be a private file.
     fn is_private(&self) -> bool;
+
+    fn encoding(&self) -> Option<Arc<std::sync::Mutex<&'static Encoding>>> {
+        unimplemented!()
+    }
 }
 
 /// The file's storage status - whether it's stored (`Present`), and if so when it was last
@@ -1028,6 +1033,7 @@ impl Buffer {
             change_bits: Default::default(),
             _subscriptions: Vec::new(),
             encoding: Arc::new(std::sync::Mutex::new(encoding_rs::UTF_8)),
+            observe_file_encoding: None,
         }
     }
 
@@ -2928,6 +2934,17 @@ impl Buffer {
     pub fn preserve_preview(&self) -> bool {
         !self.has_edits_since(&self.preview_version)
     }
+
+    /// Update the `encoding` field, whenever the `encoding` field of the file changes
+    pub fn update_encoding(&mut self) {
+        if let Some(file) = self.file() {
+            if let Some(encoding) = file.encoding() {
+                *self.encoding.lock().unwrap() = *encoding.lock().unwrap();
+            } else {
+                *self.encoding.lock().unwrap() = encoding_rs::UTF_8;
+            };
+        }
+    }
 }
 
 #[doc(hidden)]

crates/project/src/buffer_store.rs 🔗

@@ -533,6 +533,7 @@ impl LocalBufferStore {
                     path: entry.path.clone(),
                     worktree: worktree.clone(),
                     is_private: entry.is_private,
+                    encoding: None,
                 }
             } else {
                 File {
@@ -542,6 +543,7 @@ impl LocalBufferStore {
                     path: old_file.path.clone(),
                     worktree: worktree.clone(),
                     is_private: old_file.is_private,
+                    encoding: None,
                 }
             };
 
@@ -670,6 +672,8 @@ impl LocalBufferStore {
                     buffer
                         .replace_text_buffer(text::Buffer::new(0, buffer_id, loaded_file.text), cx);
 
+                    buffer.update_encoding();
+
                     reload_task = Some(buffer.reload(cx));
                 })?;
 
@@ -697,6 +701,13 @@ impl LocalBufferStore {
                             entry_id: None,
                             is_local: true,
                             is_private: false,
+                            encoding: Some(Arc::new(std::sync::Mutex::new(
+                                if let Some(encoding) = encoding {
+                                    encoding.0
+                                } else {
+                                    encoding_rs::UTF_8
+                                },
+                            ))),
                         })),
                         Capability::ReadWrite,
                     )

crates/project/src/image_store.rs 🔗

@@ -605,6 +605,7 @@ impl LocalImageStore {
                     path: entry.path.clone(),
                     worktree: worktree.clone(),
                     is_private: entry.is_private,
+                    encoding: None,
                 }
             } else {
                 worktree::File {
@@ -614,6 +615,7 @@ impl LocalImageStore {
                     path: old_file.path.clone(),
                     worktree: worktree.clone(),
                     is_private: old_file.is_private,
+                    encoding: None,
                 }
             };
 

crates/workspace/src/workspace.rs 🔗

@@ -1945,6 +1945,10 @@ impl Workspace {
         window: &mut Window,
         cx: &mut Context<Workspace>,
     ) -> Task<Result<()>> {
+        *self.encoding_options.force.get_mut() = false;
+        *self.encoding_options.detect_utf16.get_mut() = true;
+        *self.encoding_options.encoding.lock().unwrap() = EncodingWrapper::new(encoding_rs::UTF_8);
+
         let to_load = if let Some(pane) = pane.upgrade() {
             pane.update(cx, |pane, cx| {
                 window.focus(&pane.focus_handle(cx));

crates/worktree/src/worktree.rs 🔗

@@ -1318,6 +1318,7 @@ impl LocalWorktree {
                         },
                         is_local: true,
                         is_private,
+                        encoding: None,
                     })
                 }
             };
@@ -1393,6 +1394,11 @@ impl LocalWorktree {
                         },
                         is_local: true,
                         is_private,
+                        encoding: if let Some(encoding) = encoding {
+                            Some(Arc::new(std::sync::Mutex::new(encoding.0)))
+                        } else {
+                            None
+                        },
                     })
                 }
             };
@@ -1529,6 +1535,7 @@ impl LocalWorktree {
                     entry_id: None,
                     is_local: true,
                     is_private,
+                    encoding: Some(Arc::new(std::sync::Mutex::new(encoding))),
                 }))
             }
         })
@@ -3082,7 +3089,7 @@ impl fmt::Debug for Snapshot {
     }
 }
 
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone)]
 pub struct File {
     pub worktree: Entity<Worktree>,
     pub path: Arc<RelPath>,
@@ -3090,8 +3097,35 @@ pub struct File {
     pub entry_id: Option<ProjectEntryId>,
     pub is_local: bool,
     pub is_private: bool,
+    pub encoding: Option<Arc<std::sync::Mutex<&'static Encoding>>>,
 }
 
+impl PartialEq for File {
+    fn eq(&self, other: &Self) -> bool {
+        if self.worktree == other.worktree
+            && self.path == other.path
+            && self.disk_state == other.disk_state
+            && self.entry_id == other.entry_id
+            && self.is_local == other.is_local
+            && self.is_private == other.is_private
+            && if let Some(encoding) = &self.encoding
+                && let Some(other_encoding) = &other.encoding
+            {
+                if *encoding.lock().unwrap() != *other_encoding.lock().unwrap() {
+                    false
+                } else {
+                    true
+                }
+            } else {
+                true
+            }
+        {
+            true
+        } else {
+            false
+        }
+    }
+}
 impl language::File for File {
     fn as_local(&self) -> Option<&dyn language::LocalFile> {
         if self.is_local { Some(self) } else { None }
@@ -3137,6 +3171,12 @@ impl language::File for File {
 
     fn path_style(&self, cx: &App) -> PathStyle {
         self.worktree.read(cx).path_style()
+    fn encoding(&self) -> Option<Arc<std::sync::Mutex<&'static Encoding>>> {
+        if let Some(encoding) = &self.encoding {
+            Some(encoding.clone())
+        } else {
+            None
+        }
     }
 }
 
@@ -3183,6 +3223,7 @@ impl File {
             entry_id: Some(entry.id),
             is_local: true,
             is_private: entry.is_private,
+            encoding: None,
         })
     }
 
@@ -3213,6 +3254,7 @@ impl File {
             entry_id: proto.entry_id.map(ProjectEntryId::from_proto),
             is_local: false,
             is_private: false,
+            encoding: None,
         })
     }