mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-11 21:13:02 +00:00
Handle a file's line endings changing on disk
This commit is contained in:
parent
b0efa4f5c1
commit
0ba12eab22
2 changed files with 72 additions and 12 deletions
|
@ -98,7 +98,7 @@ pub enum IndentKind {
|
|||
Tab,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
#[derive(Copy, Debug, Clone, PartialEq, Eq)]
|
||||
pub enum NewlineStyle {
|
||||
Unix,
|
||||
Windows,
|
||||
|
@ -283,6 +283,7 @@ pub(crate) struct Diff {
|
|||
base_version: clock::Global,
|
||||
new_text: Arc<str>,
|
||||
changes: Vec<(ChangeTag, usize)>,
|
||||
newline_style: NewlineStyle,
|
||||
start_offset: usize,
|
||||
}
|
||||
|
||||
|
@ -973,6 +974,7 @@ impl Buffer {
|
|||
let base_version = self.version();
|
||||
cx.background().spawn(async move {
|
||||
let old_text = old_text.to_string();
|
||||
let newline_style = NewlineStyle::detect(&new_text);
|
||||
let new_text = new_text.replace("\r\n", "\n").replace('\r', "\n");
|
||||
let changes = TextDiff::from_lines(old_text.as_str(), new_text.as_str())
|
||||
.iter_all_changes()
|
||||
|
@ -982,6 +984,7 @@ impl Buffer {
|
|||
base_version,
|
||||
new_text: new_text.into(),
|
||||
changes,
|
||||
newline_style,
|
||||
start_offset: 0,
|
||||
}
|
||||
})
|
||||
|
@ -995,6 +998,7 @@ impl Buffer {
|
|||
if self.version == diff.base_version {
|
||||
self.finalize_last_transaction();
|
||||
self.start_transaction();
|
||||
self.newline_style = diff.newline_style;
|
||||
let mut offset = diff.start_offset;
|
||||
for (tag, len) in diff.changes {
|
||||
let range = offset..(offset + len);
|
||||
|
|
|
@ -2363,7 +2363,7 @@ async fn test_buffer_is_dirty(cx: &mut gpui::TestAppContext) {
|
|||
fs.remove_file("/dir/file2".as_ref(), Default::default())
|
||||
.await
|
||||
.unwrap();
|
||||
buffer2.condition(&cx, |b, _| b.is_dirty()).await;
|
||||
cx.foreground().run_until_parked();
|
||||
assert_eq!(
|
||||
*events.borrow(),
|
||||
&[
|
||||
|
@ -2393,9 +2393,7 @@ async fn test_buffer_is_dirty(cx: &mut gpui::TestAppContext) {
|
|||
fs.remove_file("/dir/file3".as_ref(), Default::default())
|
||||
.await
|
||||
.unwrap();
|
||||
buffer3
|
||||
.condition(&cx, |_, _| !events.borrow().is_empty())
|
||||
.await;
|
||||
cx.foreground().run_until_parked();
|
||||
assert_eq!(*events.borrow(), &[language::Event::FileHandleChanged]);
|
||||
cx.read(|cx| assert!(buffer3.read(cx).is_dirty()));
|
||||
}
|
||||
|
@ -2439,10 +2437,7 @@ async fn test_buffer_file_changes_on_disk(cx: &mut gpui::TestAppContext) {
|
|||
// Because the buffer was not modified, it is reloaded from disk. Its
|
||||
// contents are edited according to the diff between the old and new
|
||||
// file contents.
|
||||
buffer
|
||||
.condition(&cx, |buffer, _| buffer.text() == new_contents)
|
||||
.await;
|
||||
|
||||
cx.foreground().run_until_parked();
|
||||
buffer.update(cx, |buffer, _| {
|
||||
assert_eq!(buffer.text(), new_contents);
|
||||
assert!(!buffer.is_dirty());
|
||||
|
@ -2476,9 +2471,70 @@ async fn test_buffer_file_changes_on_disk(cx: &mut gpui::TestAppContext) {
|
|||
|
||||
// Because the buffer is modified, it doesn't reload from disk, but is
|
||||
// marked as having a conflict.
|
||||
buffer
|
||||
.condition(&cx, |buffer, _| buffer.has_conflict())
|
||||
.await;
|
||||
cx.foreground().run_until_parked();
|
||||
buffer.read_with(cx, |buffer, _| {
|
||||
assert!(buffer.has_conflict());
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_buffer_line_endings(cx: &mut gpui::TestAppContext) {
|
||||
let fs = FakeFs::new(cx.background());
|
||||
fs.insert_tree(
|
||||
"/dir",
|
||||
json!({
|
||||
"file1": "a\nb\nc\n",
|
||||
"file2": "one\r\ntwo\r\nthree\r\n",
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
|
||||
let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
|
||||
let buffer1 = project
|
||||
.update(cx, |p, cx| p.open_local_buffer("/dir/file1", cx))
|
||||
.await
|
||||
.unwrap();
|
||||
let buffer2 = project
|
||||
.update(cx, |p, cx| p.open_local_buffer("/dir/file2", cx))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
buffer1.read_with(cx, |buffer, _| {
|
||||
assert_eq!(buffer.text(), "a\nb\nc\n");
|
||||
assert_eq!(buffer.newline_style(), NewlineStyle::Unix);
|
||||
});
|
||||
buffer2.read_with(cx, |buffer, _| {
|
||||
assert_eq!(buffer.text(), "one\ntwo\nthree\n");
|
||||
assert_eq!(buffer.newline_style(), NewlineStyle::Windows);
|
||||
});
|
||||
|
||||
// Change a file's line endings on disk from unix to windows. The buffer's
|
||||
// state updates correctly.
|
||||
fs.save(
|
||||
"/dir/file1".as_ref(),
|
||||
&"aaa\nb\nc\n".into(),
|
||||
NewlineStyle::Windows,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
cx.foreground().run_until_parked();
|
||||
buffer1.read_with(cx, |buffer, _| {
|
||||
assert_eq!(buffer.text(), "aaa\nb\nc\n");
|
||||
assert_eq!(buffer.newline_style(), NewlineStyle::Windows);
|
||||
});
|
||||
|
||||
// Save a file with windows line endings. The file is written correctly.
|
||||
buffer2
|
||||
.update(cx, |buffer, cx| {
|
||||
buffer.set_text("one\ntwo\nthree\nfour\n", cx);
|
||||
buffer.save(cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
fs.load("/dir/file2".as_ref()).await.unwrap(),
|
||||
"one\r\ntwo\r\nthree\r\nfour\r\n",
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
|
|
Loading…
Reference in a new issue