From 25c6af48d17508a30cac35035fb6a88bc03a88ac Mon Sep 17 00:00:00 2001 From: R Aadarsh Date: Sat, 18 Oct 2025 20:23:37 +0530 Subject: [PATCH] Fix an issue that caused a reopened buffer to use UTF-8 even if the associated file was in a different encoding, rather than showing an error. --- 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(-) diff --git a/crates/fs/src/encodings.rs b/crates/fs/src/encodings.rs index 53dcca407a339f6770e73ecb7a0517eaa36fb805..16aab79e77ad929a95b25ebd5976949ba03c933b 100644 --- a/crates/fs/src/encodings.rs +++ b/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>>, ) -> Result { // 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), diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 5daac9765f6e9ada6c7f0cf366140eeed23cc879..2668e4dc75d41bf6a87aefada19b79aea3d99ba4 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -128,6 +128,7 @@ pub struct Buffer { change_bits: Vec>>, _subscriptions: Vec, pub encoding: Arc>, + pub observe_file_encoding: Option, } #[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>> { + 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)] diff --git a/crates/project/src/buffer_store.rs b/crates/project/src/buffer_store.rs index 1919f9b0a8efbd16ca17cf33b27139f2b3b787f8..08455009fb36055ad00c871ebde0f6558717515a 100644 --- a/crates/project/src/buffer_store.rs +++ b/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, ) diff --git a/crates/project/src/image_store.rs b/crates/project/src/image_store.rs index 8fcf9c8a6172f866d819e34cbf3b0b4810a8fc8d..2f24927e04306407de9ebb92a88d427f58dda756 100644 --- a/crates/project/src/image_store.rs +++ b/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, } }; diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 50683242333a8f3e92dd33baca52572f5ae0f443..60111bf47dd498b520a2684a5cf884f733a74d00 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -1945,6 +1945,10 @@ impl Workspace { window: &mut Window, cx: &mut Context, ) -> Task> { + *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)); diff --git a/crates/worktree/src/worktree.rs b/crates/worktree/src/worktree.rs index e38a960c18ba9d05fb2f14f9f1ce3d94bc663c20..2891a3975770f34c1c9b63d126d67ae3a0545e9c 100644 --- a/crates/worktree/src/worktree.rs +++ b/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, pub path: Arc, @@ -3090,8 +3097,35 @@ pub struct File { pub entry_id: Option, pub is_local: bool, pub is_private: bool, + pub encoding: Option>>, } +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>> { + 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, }) }