mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-12 21:32:40 +00:00
Change buffer atomically when reloading from disk
This commit is contained in:
parent
19f51bc480
commit
4884a05a73
1 changed files with 61 additions and 56 deletions
|
@ -18,7 +18,7 @@ use crate::{
|
|||
worktree::FileHandle,
|
||||
};
|
||||
use anyhow::{anyhow, Result};
|
||||
use gpui::{Entity, ModelContext, Task};
|
||||
use gpui::{AppContext, Entity, ModelContext, Task};
|
||||
use lazy_static::lazy_static;
|
||||
use rand::prelude::*;
|
||||
use std::{
|
||||
|
@ -274,6 +274,12 @@ impl Edit {
|
|||
}
|
||||
}
|
||||
|
||||
struct Diff {
|
||||
base_version: time::Global,
|
||||
new_text: Arc<str>,
|
||||
changes: Vec<(ChangeTag, usize)>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Debug)]
|
||||
pub struct Insertion {
|
||||
id: time::Local,
|
||||
|
@ -391,18 +397,16 @@ impl Buffer {
|
|||
});
|
||||
if let (Ok(history), true) = (history.await, current_version == version)
|
||||
{
|
||||
let operations = handle
|
||||
.update(&mut ctx, |this, ctx| {
|
||||
this.set_text_via_diff(history.base_text, ctx)
|
||||
})
|
||||
let diff = handle
|
||||
.read_with(&ctx, |this, ctx| this.diff(history.base_text, ctx))
|
||||
.await;
|
||||
if operations.is_some() {
|
||||
handle.update(&mut ctx, |this, ctx| {
|
||||
handle.update(&mut ctx, |this, ctx| {
|
||||
if let Some(_ops) = this.set_text_via_diff(diff, ctx) {
|
||||
this.saved_version = this.version.clone();
|
||||
this.saved_mtime = file.mtime();
|
||||
ctx.emit(Event::Reloaded);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
@ -539,54 +543,53 @@ impl Buffer {
|
|||
ctx.emit(Event::Saved);
|
||||
}
|
||||
|
||||
fn diff(&self, new_text: Arc<str>, ctx: &AppContext) -> Task<Diff> {
|
||||
// TODO: it would be nice to not allocate here.
|
||||
let old_text = self.text();
|
||||
let base_version = self.version();
|
||||
ctx.background_executor().spawn(async move {
|
||||
let changes = TextDiff::from_lines(old_text.as_str(), new_text.as_ref())
|
||||
.iter_all_changes()
|
||||
.map(|c| (c.tag(), c.value().len()))
|
||||
.collect::<Vec<_>>();
|
||||
Diff {
|
||||
base_version,
|
||||
new_text,
|
||||
changes,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn set_text_via_diff(
|
||||
&mut self,
|
||||
new_text: Arc<str>,
|
||||
diff: Diff,
|
||||
ctx: &mut ModelContext<Self>,
|
||||
) -> Task<Option<Vec<Operation>>> {
|
||||
let version = self.version.clone();
|
||||
let old_text = self.text();
|
||||
ctx.spawn(|handle, mut ctx| async move {
|
||||
let diff = ctx
|
||||
.background_executor()
|
||||
.spawn({
|
||||
let new_text = new_text.clone();
|
||||
async move {
|
||||
TextDiff::from_lines(old_text.as_str(), new_text.as_ref())
|
||||
.iter_all_changes()
|
||||
.map(|c| (c.tag(), c.value().len()))
|
||||
.collect::<Vec<_>>()
|
||||
) -> Option<Vec<Operation>> {
|
||||
if self.version == diff.base_version {
|
||||
self.start_transaction(None).unwrap();
|
||||
let mut operations = Vec::new();
|
||||
let mut offset = 0;
|
||||
for (tag, len) in diff.changes {
|
||||
let range = offset..(offset + len);
|
||||
match tag {
|
||||
ChangeTag::Equal => offset += len,
|
||||
ChangeTag::Delete => operations
|
||||
.extend_from_slice(&self.edit(Some(range), "", Some(ctx)).unwrap()),
|
||||
ChangeTag::Insert => {
|
||||
operations.extend_from_slice(
|
||||
&self
|
||||
.edit(Some(offset..offset), &diff.new_text[range], Some(ctx))
|
||||
.unwrap(),
|
||||
);
|
||||
offset += len;
|
||||
}
|
||||
})
|
||||
.await;
|
||||
handle.update(&mut ctx, |this, ctx| {
|
||||
if this.version == version {
|
||||
this.start_transaction(None).unwrap();
|
||||
let mut operations = Vec::new();
|
||||
let mut offset = 0;
|
||||
for (tag, len) in diff {
|
||||
let range = offset..(offset + len);
|
||||
match tag {
|
||||
ChangeTag::Equal => offset += len,
|
||||
ChangeTag::Delete => operations
|
||||
.extend_from_slice(&this.edit(Some(range), "", Some(ctx)).unwrap()),
|
||||
ChangeTag::Insert => {
|
||||
operations.extend_from_slice(
|
||||
&this
|
||||
.edit(Some(offset..offset), &new_text[range], Some(ctx))
|
||||
.unwrap(),
|
||||
);
|
||||
offset += len;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.end_transaction(None, Some(ctx)).unwrap();
|
||||
Some(operations)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
self.end_transaction(None, Some(ctx)).unwrap();
|
||||
Some(operations)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_dirty(&self) -> bool {
|
||||
|
@ -3278,15 +3281,17 @@ mod tests {
|
|||
let buffer = app.add_model(|ctx| Buffer::new(0, text, ctx));
|
||||
|
||||
let text = "a\nccc\ndddd\nffffff\n";
|
||||
buffer
|
||||
.update(&mut app, |b, ctx| b.set_text_via_diff(text.into(), ctx))
|
||||
let diff = buffer
|
||||
.read_with(&app, |b, ctx| b.diff(text.into(), ctx))
|
||||
.await;
|
||||
buffer.update(&mut app, |b, ctx| b.set_text_via_diff(diff, ctx));
|
||||
app.read(|ctx| assert_eq!(buffer.read(ctx).text(), text));
|
||||
|
||||
let text = "a\n1\n\nccc\ndd2dd\nffffff\n";
|
||||
buffer
|
||||
.update(&mut app, |b, ctx| b.set_text_via_diff(text.into(), ctx))
|
||||
let diff = buffer
|
||||
.read_with(&app, |b, ctx| b.diff(text.into(), ctx))
|
||||
.await;
|
||||
buffer.update(&mut app, |b, ctx| b.set_text_via_diff(diff, ctx));
|
||||
app.read(|ctx| assert_eq!(buffer.read(ctx).text(), text));
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue