diff --git a/CHANGELOG.md b/CHANGELOG.md index 4aef0cc15..d15a6f235 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Rojo Changelog ## Unreleased Changes +* Rojo now converts any line endings to LF, preventing spurious diffs when syncing Lua files on Windows ([#854]) * Fixed Rojo plugin failing to connect when project contains certain unreadable properties ([#848]) * Added popout diff visualizer for table properties like Attributes and Tags ([#834]) * Updated Theme to use Studio colors ([#838]) @@ -59,6 +60,7 @@ [#840]: https://github.com/rojo-rbx/rojo/pull/840 [#847]: https://github.com/rojo-rbx/rojo/pull/847 [#848]: https://github.com/rojo-rbx/rojo/pull/848 +[#854]: https://github.com/rojo-rbx/rojo/pull/854 ## [7.4.0] - January 16, 2024 diff --git a/crates/memofs/CHANGELOG.md b/crates/memofs/CHANGELOG.md index c9961d0ed..d678f8c9d 100644 --- a/crates/memofs/CHANGELOG.md +++ b/crates/memofs/CHANGELOG.md @@ -2,8 +2,10 @@ ## Unreleased Changes * Changed `StdBackend` file watching component to use minimal recursive watches. [#830] +* Added `Vfs::read_to_string` and `Vfs::read_to_string_lf_normalized` [#854] [#830]: https://github.com/rojo-rbx/rojo/pull/830 +[#854]: https://github.com/rojo-rbx/rojo/pull/854 ## 0.2.0 (2021-08-23) * Updated to `crossbeam-channel` 0.5.1. diff --git a/crates/memofs/src/lib.rs b/crates/memofs/src/lib.rs index 744ca60fc..6d41c853b 100644 --- a/crates/memofs/src/lib.rs +++ b/crates/memofs/src/lib.rs @@ -22,9 +22,9 @@ mod noop_backend; mod snapshot; mod std_backend; -use std::io; use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex, MutexGuard}; +use std::{io, str}; pub use in_memory_fs::InMemoryFs; pub use noop_backend::NoopBackend; @@ -155,6 +155,24 @@ impl VfsInner { Ok(Arc::new(contents)) } + fn read_to_string>(&mut self, path: P) -> io::Result> { + let path = path.as_ref(); + let contents = self.backend.read(path)?; + + if self.watch_enabled { + self.backend.watch(path)?; + } + + let contents_str = str::from_utf8(&contents).map_err(|_| { + io::Error::new( + io::ErrorKind::InvalidData, + format!("File was not valid UTF-8: {}", path.display()), + ) + })?; + + Ok(Arc::new(contents_str.into())) + } + fn write, C: AsRef<[u8]>>(&mut self, path: P, contents: C) -> io::Result<()> { let path = path.as_ref(); let contents = contents.as_ref(); @@ -258,6 +276,33 @@ impl Vfs { self.inner.lock().unwrap().read(path) } + /// Read a file from the VFS (or from the underlying backend if it isn't + /// resident) into a string. + /// + /// Roughly equivalent to [`std::fs::read_to_string`][std::fs::read_to_string]. + /// + /// [std::fs::read_to_string]: https://doc.rust-lang.org/stable/std/fs/fn.read_to_string.html + #[inline] + pub fn read_to_string>(&self, path: P) -> io::Result> { + let path = path.as_ref(); + self.inner.lock().unwrap().read_to_string(path) + } + + /// Read a file from the VFS (or the underlying backend if it isn't + /// resident) into a string, and normalize its line endings to LF. + /// + /// Roughly equivalent to [`std::fs::read_to_string`][std::fs::read_to_string], but also performs + /// line ending normalization. + /// + /// [std::fs::read_to_string]: https://doc.rust-lang.org/stable/std/fs/fn.read_to_string.html + #[inline] + pub fn read_to_string_lf_normalized>(&self, path: P) -> io::Result> { + let path = path.as_ref(); + let contents = self.inner.lock().unwrap().read_to_string(path)?; + + Ok(contents.lines().collect::>().join("\n").into()) + } + /// Write a file to the VFS and the underlying backend. /// /// Roughly equivalent to [`std::fs::write`][std::fs::write]. diff --git a/src/snapshot_middleware/lua.rs b/src/snapshot_middleware/lua.rs index ab7f15a1f..3f8d26adf 100644 --- a/src/snapshot_middleware/lua.rs +++ b/src/snapshot_middleware/lua.rs @@ -1,6 +1,5 @@ use std::{collections::HashMap, path::Path, str}; -use anyhow::Context; use memofs::{IoResultExt, Vfs}; use rbx_dom_weak::types::Enum; @@ -40,10 +39,8 @@ pub fn snapshot_lua( (_, ScriptType::Module) => ("ModuleScript", None), }; - let contents = vfs.read(path)?; - let contents_str = str::from_utf8(&contents) - .with_context(|| format!("File was not valid UTF-8: {}", path.display()))? - .to_owned(); + let contents = vfs.read_to_string_lf_normalized(path)?; + let contents_str = contents.as_str(); let mut properties = HashMap::with_capacity(2); properties.insert("Source".to_owned(), contents_str.into()); diff --git a/src/snapshot_middleware/txt.rs b/src/snapshot_middleware/txt.rs index 207243f9f..a69b14d1b 100644 --- a/src/snapshot_middleware/txt.rs +++ b/src/snapshot_middleware/txt.rs @@ -1,6 +1,5 @@ use std::{path::Path, str}; -use anyhow::Context; use maplit::hashmap; use memofs::{IoResultExt, Vfs}; @@ -14,10 +13,8 @@ pub fn snapshot_txt( path: &Path, name: &str, ) -> anyhow::Result> { - let contents = vfs.read(path)?; - let contents_str = str::from_utf8(&contents) - .with_context(|| format!("File was not valid UTF-8: {}", path.display()))? - .to_owned(); + let contents = vfs.read_to_string(path)?; + let contents_str = contents.as_str(); let properties = hashmap! { "Value".to_owned() => contents_str.into(),