sum_tree: Replace rayon with futures (#41586)

Release Notes:

- N/A *or* Added/Fixed/Improved ...

Co-authored by: Kate <kate@zed.dev>
This commit is contained in:
Lukas Wirth
2025-10-31 10:39:01 +00:00
committed by GitHub
parent 7c29c6d7a6
commit f2ce06c7b0
67 changed files with 1271 additions and 640 deletions
+184 -97
View File
@@ -14,24 +14,29 @@ fn init_logger() {
zlog::init_test();
}
#[test]
fn test_edit() {
let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "abc");
#[gpui::test]
fn test_edit(cx: &mut gpui::TestAppContext) {
let mut buffer = Buffer::new(
ReplicaId::LOCAL,
BufferId::new(1).unwrap(),
"abc",
cx.background_executor(),
);
assert_eq!(buffer.text(), "abc");
buffer.edit([(3..3, "def")]);
buffer.edit([(3..3, "def")], cx.background_executor());
assert_eq!(buffer.text(), "abcdef");
buffer.edit([(0..0, "ghi")]);
buffer.edit([(0..0, "ghi")], cx.background_executor());
assert_eq!(buffer.text(), "ghiabcdef");
buffer.edit([(5..5, "jkl")]);
buffer.edit([(5..5, "jkl")], cx.background_executor());
assert_eq!(buffer.text(), "ghiabjklcdef");
buffer.edit([(6..7, "")]);
buffer.edit([(6..7, "")], cx.background_executor());
assert_eq!(buffer.text(), "ghiabjlcdef");
buffer.edit([(4..9, "mno")]);
buffer.edit([(4..9, "mno")], cx.background_executor());
assert_eq!(buffer.text(), "ghiamnoef");
}
#[gpui::test(iterations = 100)]
fn test_random_edits(mut rng: StdRng) {
fn test_random_edits(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
let operations = env::var("OPERATIONS")
.map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
.unwrap_or(10);
@@ -44,6 +49,7 @@ fn test_random_edits(mut rng: StdRng) {
ReplicaId::LOCAL,
BufferId::new(1).unwrap(),
reference_string.clone(),
cx.background_executor(),
);
LineEnding::normalize(&mut reference_string);
@@ -56,7 +62,7 @@ fn test_random_edits(mut rng: StdRng) {
);
for _i in 0..operations {
let (edits, _) = buffer.randomly_edit(&mut rng, 5);
let (edits, _) = buffer.randomly_edit(&mut rng, 5, cx.background_executor());
for (old_range, new_text) in edits.iter().rev() {
reference_string.replace_range(old_range.clone(), new_text);
}
@@ -106,7 +112,11 @@ fn test_random_edits(mut rng: StdRng) {
let mut text = old_buffer.visible_text.clone();
for edit in edits {
let new_text: String = buffer.text_for_range(edit.new.clone()).collect();
text.replace(edit.new.start..edit.new.start + edit.old.len(), &new_text);
text.replace(
edit.new.start..edit.new.start + edit.old.len(),
&new_text,
cx.background_executor(),
);
}
assert_eq!(text.to_string(), buffer.text());
@@ -161,14 +171,18 @@ fn test_random_edits(mut rng: StdRng) {
let mut text = old_buffer.visible_text.clone();
for edit in subscription_edits.into_inner() {
let new_text: String = buffer.text_for_range(edit.new.clone()).collect();
text.replace(edit.new.start..edit.new.start + edit.old.len(), &new_text);
text.replace(
edit.new.start..edit.new.start + edit.old.len(),
&new_text,
cx.background_executor(),
);
}
assert_eq!(text.to_string(), buffer.text());
}
}
#[test]
fn test_line_endings() {
#[gpui::test]
fn test_line_endings(cx: &mut gpui::TestAppContext) {
assert_eq!(LineEnding::detect(&"🍐✅\n".repeat(1000)), LineEnding::Unix);
assert_eq!(LineEnding::detect(&"abcd\n".repeat(1000)), LineEnding::Unix);
assert_eq!(
@@ -184,25 +198,34 @@ fn test_line_endings() {
ReplicaId::LOCAL,
BufferId::new(1).unwrap(),
"one\r\ntwo\rthree",
cx.background_executor(),
);
assert_eq!(buffer.text(), "one\ntwo\nthree");
assert_eq!(buffer.line_ending(), LineEnding::Windows);
buffer.check_invariants();
buffer.edit([(buffer.len()..buffer.len(), "\r\nfour")]);
buffer.edit([(0..0, "zero\r\n")]);
buffer.edit(
[(buffer.len()..buffer.len(), "\r\nfour")],
cx.background_executor(),
);
buffer.edit([(0..0, "zero\r\n")], cx.background_executor());
assert_eq!(buffer.text(), "zero\none\ntwo\nthree\nfour");
assert_eq!(buffer.line_ending(), LineEnding::Windows);
buffer.check_invariants();
}
#[test]
fn test_line_len() {
let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "");
buffer.edit([(0..0, "abcd\nefg\nhij")]);
buffer.edit([(12..12, "kl\nmno")]);
buffer.edit([(18..18, "\npqrs\n")]);
buffer.edit([(18..21, "\nPQ")]);
#[gpui::test]
fn test_line_len(cx: &mut gpui::TestAppContext) {
let mut buffer = Buffer::new(
ReplicaId::LOCAL,
BufferId::new(1).unwrap(),
"",
cx.background_executor(),
);
buffer.edit([(0..0, "abcd\nefg\nhij")], cx.background_executor());
buffer.edit([(12..12, "kl\nmno")], cx.background_executor());
buffer.edit([(18..18, "\npqrs\n")], cx.background_executor());
buffer.edit([(18..21, "\nPQ")], cx.background_executor());
assert_eq!(buffer.line_len(0), 4);
assert_eq!(buffer.line_len(1), 3);
@@ -212,10 +235,15 @@ fn test_line_len() {
assert_eq!(buffer.line_len(5), 0);
}
#[test]
fn test_common_prefix_at_position() {
#[gpui::test]
fn test_common_prefix_at_position(cx: &mut gpui::TestAppContext) {
let text = "a = str; b = δα";
let buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), text);
let buffer = Buffer::new(
ReplicaId::LOCAL,
BufferId::new(1).unwrap(),
text,
cx.background_executor(),
);
let offset1 = offset_after(text, "str");
let offset2 = offset_after(text, "δα");
@@ -261,12 +289,13 @@ fn test_common_prefix_at_position() {
}
}
#[test]
fn test_text_summary_for_range() {
#[gpui::test]
fn test_text_summary_for_range(cx: &mut gpui::TestAppContext) {
let buffer = Buffer::new(
ReplicaId::LOCAL,
BufferId::new(1).unwrap(),
"ab\nefg\nhklm\nnopqrs\ntuvwxyz",
cx.background_executor(),
);
assert_eq!(
buffer.text_summary_for_range::<TextSummary, _>(0..2),
@@ -354,13 +383,18 @@ fn test_text_summary_for_range() {
);
}
#[test]
fn test_chars_at() {
let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "");
buffer.edit([(0..0, "abcd\nefgh\nij")]);
buffer.edit([(12..12, "kl\nmno")]);
buffer.edit([(18..18, "\npqrs")]);
buffer.edit([(18..21, "\nPQ")]);
#[gpui::test]
fn test_chars_at(cx: &mut gpui::TestAppContext) {
let mut buffer = Buffer::new(
ReplicaId::LOCAL,
BufferId::new(1).unwrap(),
"",
cx.background_executor(),
);
buffer.edit([(0..0, "abcd\nefgh\nij")], cx.background_executor());
buffer.edit([(12..12, "kl\nmno")], cx.background_executor());
buffer.edit([(18..18, "\npqrs")], cx.background_executor());
buffer.edit([(18..21, "\nPQ")], cx.background_executor());
let chars = buffer.chars_at(Point::new(0, 0));
assert_eq!(chars.collect::<String>(), "abcd\nefgh\nijkl\nmno\nPQrs");
@@ -378,43 +412,53 @@ fn test_chars_at() {
assert_eq!(chars.collect::<String>(), "PQrs");
// Regression test:
let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "");
buffer.edit([(0..0, "[workspace]\nmembers = [\n \"xray_core\",\n \"xray_server\",\n \"xray_cli\",\n \"xray_wasm\",\n]\n")]);
buffer.edit([(60..60, "\n")]);
let mut buffer = Buffer::new(
ReplicaId::LOCAL,
BufferId::new(1).unwrap(),
"",
cx.background_executor(),
);
buffer.edit([(0..0, "[workspace]\nmembers = [\n \"xray_core\",\n \"xray_server\",\n \"xray_cli\",\n \"xray_wasm\",\n]\n")], cx.background_executor());
buffer.edit([(60..60, "\n")], cx.background_executor());
let chars = buffer.chars_at(Point::new(6, 0));
assert_eq!(chars.collect::<String>(), " \"xray_wasm\",\n]\n");
}
#[test]
fn test_anchors() {
let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "");
buffer.edit([(0..0, "abc")]);
#[gpui::test]
fn test_anchors(cx: &mut gpui::TestAppContext) {
let mut buffer = Buffer::new(
ReplicaId::LOCAL,
BufferId::new(1).unwrap(),
"",
cx.background_executor(),
);
buffer.edit([(0..0, "abc")], cx.background_executor());
let left_anchor = buffer.anchor_before(2);
let right_anchor = buffer.anchor_after(2);
buffer.edit([(1..1, "def\n")]);
buffer.edit([(1..1, "def\n")], cx.background_executor());
assert_eq!(buffer.text(), "adef\nbc");
assert_eq!(left_anchor.to_offset(&buffer), 6);
assert_eq!(right_anchor.to_offset(&buffer), 6);
assert_eq!(left_anchor.to_point(&buffer), Point { row: 1, column: 1 });
assert_eq!(right_anchor.to_point(&buffer), Point { row: 1, column: 1 });
buffer.edit([(2..3, "")]);
buffer.edit([(2..3, "")], cx.background_executor());
assert_eq!(buffer.text(), "adf\nbc");
assert_eq!(left_anchor.to_offset(&buffer), 5);
assert_eq!(right_anchor.to_offset(&buffer), 5);
assert_eq!(left_anchor.to_point(&buffer), Point { row: 1, column: 1 });
assert_eq!(right_anchor.to_point(&buffer), Point { row: 1, column: 1 });
buffer.edit([(5..5, "ghi\n")]);
buffer.edit([(5..5, "ghi\n")], cx.background_executor());
assert_eq!(buffer.text(), "adf\nbghi\nc");
assert_eq!(left_anchor.to_offset(&buffer), 5);
assert_eq!(right_anchor.to_offset(&buffer), 9);
assert_eq!(left_anchor.to_point(&buffer), Point { row: 1, column: 1 });
assert_eq!(right_anchor.to_point(&buffer), Point { row: 2, column: 0 });
buffer.edit([(7..9, "")]);
buffer.edit([(7..9, "")], cx.background_executor());
assert_eq!(buffer.text(), "adf\nbghc");
assert_eq!(left_anchor.to_offset(&buffer), 5);
assert_eq!(right_anchor.to_offset(&buffer), 7);
@@ -504,13 +548,18 @@ fn test_anchors() {
);
}
#[test]
fn test_anchors_at_start_and_end() {
let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "");
#[gpui::test]
fn test_anchors_at_start_and_end(cx: &mut gpui::TestAppContext) {
let mut buffer = Buffer::new(
ReplicaId::LOCAL,
BufferId::new(1).unwrap(),
"",
cx.background_executor(),
);
let before_start_anchor = buffer.anchor_before(0);
let after_end_anchor = buffer.anchor_after(0);
buffer.edit([(0..0, "abc")]);
buffer.edit([(0..0, "abc")], cx.background_executor());
assert_eq!(buffer.text(), "abc");
assert_eq!(before_start_anchor.to_offset(&buffer), 0);
assert_eq!(after_end_anchor.to_offset(&buffer), 3);
@@ -518,8 +567,8 @@ fn test_anchors_at_start_and_end() {
let after_start_anchor = buffer.anchor_after(0);
let before_end_anchor = buffer.anchor_before(3);
buffer.edit([(3..3, "def")]);
buffer.edit([(0..0, "ghi")]);
buffer.edit([(3..3, "def")], cx.background_executor());
buffer.edit([(0..0, "ghi")], cx.background_executor());
assert_eq!(buffer.text(), "ghiabcdef");
assert_eq!(before_start_anchor.to_offset(&buffer), 0);
assert_eq!(after_start_anchor.to_offset(&buffer), 3);
@@ -527,15 +576,20 @@ fn test_anchors_at_start_and_end() {
assert_eq!(after_end_anchor.to_offset(&buffer), 9);
}
#[test]
fn test_undo_redo() {
let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "1234");
#[gpui::test]
fn test_undo_redo(cx: &mut gpui::TestAppContext) {
let mut buffer = Buffer::new(
ReplicaId::LOCAL,
BufferId::new(1).unwrap(),
"1234",
cx.background_executor(),
);
// Set group interval to zero so as to not group edits in the undo stack.
buffer.set_group_interval(Duration::from_secs(0));
buffer.edit([(1..1, "abx")]);
buffer.edit([(3..4, "yzef")]);
buffer.edit([(3..5, "cd")]);
buffer.edit([(1..1, "abx")], cx.background_executor());
buffer.edit([(3..4, "yzef")], cx.background_executor());
buffer.edit([(3..5, "cd")], cx.background_executor());
assert_eq!(buffer.text(), "1abcdef234");
let entries = buffer.history.undo_stack.clone();
@@ -563,26 +617,31 @@ fn test_undo_redo() {
assert_eq!(buffer.text(), "1234");
}
#[test]
fn test_history() {
#[gpui::test]
fn test_history(cx: &mut gpui::TestAppContext) {
let mut now = Instant::now();
let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "123456");
let mut buffer = Buffer::new(
ReplicaId::LOCAL,
BufferId::new(1).unwrap(),
"123456",
cx.background_executor(),
);
buffer.set_group_interval(Duration::from_millis(300));
let transaction_1 = buffer.start_transaction_at(now).unwrap();
buffer.edit([(2..4, "cd")]);
buffer.edit([(2..4, "cd")], cx.background_executor());
buffer.end_transaction_at(now);
assert_eq!(buffer.text(), "12cd56");
buffer.start_transaction_at(now);
buffer.edit([(4..5, "e")]);
buffer.edit([(4..5, "e")], cx.background_executor());
buffer.end_transaction_at(now).unwrap();
assert_eq!(buffer.text(), "12cde6");
now += buffer.transaction_group_interval() + Duration::from_millis(1);
buffer.start_transaction_at(now);
buffer.edit([(0..1, "a")]);
buffer.edit([(1..1, "b")]);
buffer.edit([(0..1, "a")], cx.background_executor());
buffer.edit([(1..1, "b")], cx.background_executor());
buffer.end_transaction_at(now).unwrap();
assert_eq!(buffer.text(), "ab2cde6");
@@ -609,7 +668,7 @@ fn test_history() {
// Redo stack gets cleared after performing an edit.
buffer.start_transaction_at(now);
buffer.edit([(0..0, "X")]);
buffer.edit([(0..0, "X")], cx.background_executor());
buffer.end_transaction_at(now);
assert_eq!(buffer.text(), "X12cde6");
buffer.redo();
@@ -630,26 +689,31 @@ fn test_history() {
assert_eq!(buffer.text(), "X12cde6");
}
#[test]
fn test_finalize_last_transaction() {
#[gpui::test]
fn test_finalize_last_transaction(cx: &mut gpui::TestAppContext) {
let now = Instant::now();
let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "123456");
let mut buffer = Buffer::new(
ReplicaId::LOCAL,
BufferId::new(1).unwrap(),
"123456",
cx.background_executor(),
);
buffer.history.group_interval = Duration::from_millis(1);
buffer.start_transaction_at(now);
buffer.edit([(2..4, "cd")]);
buffer.edit([(2..4, "cd")], cx.background_executor());
buffer.end_transaction_at(now);
assert_eq!(buffer.text(), "12cd56");
buffer.finalize_last_transaction();
buffer.start_transaction_at(now);
buffer.edit([(4..5, "e")]);
buffer.edit([(4..5, "e")], cx.background_executor());
buffer.end_transaction_at(now).unwrap();
assert_eq!(buffer.text(), "12cde6");
buffer.start_transaction_at(now);
buffer.edit([(0..1, "a")]);
buffer.edit([(1..1, "b")]);
buffer.edit([(0..1, "a")], cx.background_executor());
buffer.edit([(1..1, "b")], cx.background_executor());
buffer.end_transaction_at(now).unwrap();
assert_eq!(buffer.text(), "ab2cde6");
@@ -666,14 +730,19 @@ fn test_finalize_last_transaction() {
assert_eq!(buffer.text(), "ab2cde6");
}
#[test]
fn test_edited_ranges_for_transaction() {
#[gpui::test]
fn test_edited_ranges_for_transaction(cx: &mut gpui::TestAppContext) {
let now = Instant::now();
let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "1234567");
let mut buffer = Buffer::new(
ReplicaId::LOCAL,
BufferId::new(1).unwrap(),
"1234567",
cx.background_executor(),
);
buffer.start_transaction_at(now);
buffer.edit([(2..4, "cd")]);
buffer.edit([(6..6, "efg")]);
buffer.edit([(2..4, "cd")], cx.background_executor());
buffer.edit([(6..6, "efg")], cx.background_executor());
buffer.end_transaction_at(now);
assert_eq!(buffer.text(), "12cd56efg7");
@@ -685,7 +754,7 @@ fn test_edited_ranges_for_transaction() {
[2..4, 6..9]
);
buffer.edit([(5..5, "hijk")]);
buffer.edit([(5..5, "hijk")], cx.background_executor());
assert_eq!(buffer.text(), "12cd5hijk6efg7");
assert_eq!(
buffer
@@ -694,7 +763,7 @@ fn test_edited_ranges_for_transaction() {
[2..4, 10..13]
);
buffer.edit([(4..4, "l")]);
buffer.edit([(4..4, "l")], cx.background_executor());
assert_eq!(buffer.text(), "12cdl5hijk6efg7");
assert_eq!(
buffer
@@ -704,27 +773,42 @@ fn test_edited_ranges_for_transaction() {
);
}
#[test]
fn test_concurrent_edits() {
#[gpui::test]
fn test_concurrent_edits(cx: &mut gpui::TestAppContext) {
let text = "abcdef";
let mut buffer1 = Buffer::new(ReplicaId::new(1), BufferId::new(1).unwrap(), text);
let mut buffer2 = Buffer::new(ReplicaId::new(2), BufferId::new(1).unwrap(), text);
let mut buffer3 = Buffer::new(ReplicaId::new(3), BufferId::new(1).unwrap(), text);
let mut buffer1 = Buffer::new(
ReplicaId::new(1),
BufferId::new(1).unwrap(),
text,
cx.background_executor(),
);
let mut buffer2 = Buffer::new(
ReplicaId::new(2),
BufferId::new(1).unwrap(),
text,
cx.background_executor(),
);
let mut buffer3 = Buffer::new(
ReplicaId::new(3),
BufferId::new(1).unwrap(),
text,
cx.background_executor(),
);
let buf1_op = buffer1.edit([(1..2, "12")]);
let buf1_op = buffer1.edit([(1..2, "12")], cx.background_executor());
assert_eq!(buffer1.text(), "a12cdef");
let buf2_op = buffer2.edit([(3..4, "34")]);
let buf2_op = buffer2.edit([(3..4, "34")], cx.background_executor());
assert_eq!(buffer2.text(), "abc34ef");
let buf3_op = buffer3.edit([(5..6, "56")]);
let buf3_op = buffer3.edit([(5..6, "56")], cx.background_executor());
assert_eq!(buffer3.text(), "abcde56");
buffer1.apply_op(buf2_op.clone());
buffer1.apply_op(buf3_op.clone());
buffer2.apply_op(buf1_op.clone());
buffer2.apply_op(buf3_op);
buffer3.apply_op(buf1_op);
buffer3.apply_op(buf2_op);
buffer1.apply_op(buf2_op.clone(), Some(cx.background_executor()));
buffer1.apply_op(buf3_op.clone(), Some(cx.background_executor()));
buffer2.apply_op(buf1_op.clone(), Some(cx.background_executor()));
buffer2.apply_op(buf3_op, Some(cx.background_executor()));
buffer3.apply_op(buf1_op, Some(cx.background_executor()));
buffer3.apply_op(buf2_op, Some(cx.background_executor()));
assert_eq!(buffer1.text(), "a12c34e56");
assert_eq!(buffer2.text(), "a12c34e56");
@@ -732,7 +816,7 @@ fn test_concurrent_edits() {
}
#[gpui::test(iterations = 100)]
fn test_random_concurrent_edits(mut rng: StdRng) {
fn test_random_concurrent_edits(mut rng: StdRng, cx: &mut gpui::TestAppContext) {
let peers = env::var("PEERS")
.map(|i| i.parse().expect("invalid `PEERS` variable"))
.unwrap_or(5);
@@ -753,6 +837,7 @@ fn test_random_concurrent_edits(mut rng: StdRng) {
ReplicaId::new(i as u16),
BufferId::new(1).unwrap(),
base_text.clone(),
cx.background_executor(),
);
buffer.history.group_interval = Duration::from_millis(rng.random_range(0..=200));
buffers.push(buffer);
@@ -769,7 +854,9 @@ fn test_random_concurrent_edits(mut rng: StdRng) {
let buffer = &mut buffers[replica_index];
match rng.random_range(0..=100) {
0..=50 if mutation_count != 0 => {
let op = buffer.randomly_edit(&mut rng, 5).1;
let op = buffer
.randomly_edit(&mut rng, 5, cx.background_executor())
.1;
network.broadcast(buffer.replica_id, vec![op]);
log::info!("buffer {:?} text: {:?}", buffer.replica_id, buffer.text());
mutation_count -= 1;
@@ -787,7 +874,7 @@ fn test_random_concurrent_edits(mut rng: StdRng) {
replica_id,
ops.len()
);
buffer.apply_ops(ops);
buffer.apply_ops(ops, Some(cx.background_executor()));
}
}
_ => {}
+63 -17
View File
@@ -15,6 +15,7 @@ use anyhow::{Context as _, Result};
use clock::Lamport;
pub use clock::ReplicaId;
use collections::{HashMap, HashSet};
use gpui::BackgroundExecutor;
use locator::Locator;
use operation_queue::OperationQueue;
pub use patch::Patch;
@@ -709,11 +710,41 @@ impl FromIterator<char> for LineIndent {
}
impl Buffer {
pub fn new(replica_id: ReplicaId, remote_id: BufferId, base_text: impl Into<String>) -> Buffer {
/// Create a new buffer from a string.
pub fn new(
replica_id: ReplicaId,
remote_id: BufferId,
base_text: impl Into<String>,
executor: &BackgroundExecutor,
) -> Buffer {
let mut base_text = base_text.into();
let line_ending = LineEnding::detect(&base_text);
LineEnding::normalize(&mut base_text);
Self::new_normalized(replica_id, remote_id, line_ending, Rope::from(&*base_text))
Self::new_normalized(
replica_id,
remote_id,
line_ending,
Rope::from_str(&base_text, executor),
)
}
/// Create a new buffer from a string.
///
/// Unlike [`Buffer::new`], this does not construct the backing rope in parallel if it is large enough.
pub fn new_slow(
replica_id: ReplicaId,
remote_id: BufferId,
base_text: impl Into<String>,
) -> Buffer {
let mut base_text = base_text.into();
let line_ending = LineEnding::detect(&base_text);
LineEnding::normalize(&mut base_text);
Self::new_normalized(
replica_id,
remote_id,
line_ending,
Rope::from_str_small(&base_text),
)
}
pub fn new_normalized(
@@ -808,7 +839,7 @@ impl Buffer {
self.history.group_interval
}
pub fn edit<R, I, S, T>(&mut self, edits: R) -> Operation
pub fn edit<R, I, S, T>(&mut self, edits: R, cx: &BackgroundExecutor) -> Operation
where
R: IntoIterator<IntoIter = I>,
I: ExactSizeIterator<Item = (Range<S>, T)>,
@@ -821,7 +852,7 @@ impl Buffer {
self.start_transaction();
let timestamp = self.lamport_clock.tick();
let operation = Operation::Edit(self.apply_local_edit(edits, timestamp));
let operation = Operation::Edit(self.apply_local_edit(edits, timestamp, cx));
self.history.push(operation.clone());
self.history.push_undo(operation.timestamp());
@@ -834,6 +865,7 @@ impl Buffer {
&mut self,
edits: impl ExactSizeIterator<Item = (Range<S>, T)>,
timestamp: clock::Lamport,
executor: &BackgroundExecutor,
) -> EditOperation {
let mut edits_patch = Patch::default();
let mut edit_op = EditOperation {
@@ -922,7 +954,7 @@ impl Buffer {
});
insertion_slices.push(InsertionSlice::from_fragment(timestamp, &fragment));
new_insertions.push(InsertionFragment::insert_new(&fragment));
new_ropes.push_str(new_text.as_ref());
new_ropes.push_str(new_text.as_ref(), executor);
new_fragments.push(fragment, &None);
insertion_offset += new_text.len();
}
@@ -1001,22 +1033,26 @@ impl Buffer {
self.snapshot.line_ending = line_ending;
}
pub fn apply_ops<I: IntoIterator<Item = Operation>>(&mut self, ops: I) {
pub fn apply_ops<I: IntoIterator<Item = Operation>>(
&mut self,
ops: I,
executor: Option<&BackgroundExecutor>,
) {
let mut deferred_ops = Vec::new();
for op in ops {
self.history.push(op.clone());
if self.can_apply_op(&op) {
self.apply_op(op);
self.apply_op(op, executor);
} else {
self.deferred_replicas.insert(op.replica_id());
deferred_ops.push(op);
}
}
self.deferred_ops.insert(deferred_ops);
self.flush_deferred_ops();
self.flush_deferred_ops(executor);
}
fn apply_op(&mut self, op: Operation) {
fn apply_op(&mut self, op: Operation, executor: Option<&BackgroundExecutor>) {
match op {
Operation::Edit(edit) => {
if !self.version.observed(edit.timestamp) {
@@ -1025,6 +1061,7 @@ impl Buffer {
&edit.ranges,
&edit.new_text,
edit.timestamp,
executor,
);
self.snapshot.version.observe(edit.timestamp);
self.lamport_clock.observe(edit.timestamp);
@@ -1055,6 +1092,7 @@ impl Buffer {
ranges: &[Range<FullOffset>],
new_text: &[Arc<str>],
timestamp: clock::Lamport,
executor: Option<&BackgroundExecutor>,
) {
if ranges.is_empty() {
return;
@@ -1170,7 +1208,10 @@ impl Buffer {
});
insertion_slices.push(InsertionSlice::from_fragment(timestamp, &fragment));
new_insertions.push(InsertionFragment::insert_new(&fragment));
new_ropes.push_str(new_text);
match executor {
Some(executor) => new_ropes.push_str(new_text, executor),
None => new_ropes.push_str_small(new_text),
}
new_fragments.push(fragment, &None);
insertion_offset += new_text.len();
}
@@ -1348,12 +1389,12 @@ impl Buffer {
self.subscriptions.publish_mut(&edits);
}
fn flush_deferred_ops(&mut self) {
fn flush_deferred_ops(&mut self, executor: Option<&BackgroundExecutor>) {
self.deferred_replicas.clear();
let mut deferred_ops = Vec::new();
for op in self.deferred_ops.drain().iter().cloned() {
if self.can_apply_op(&op) {
self.apply_op(op);
self.apply_op(op, executor);
} else {
self.deferred_replicas.insert(op.replica_id());
deferred_ops.push(op);
@@ -1711,9 +1752,9 @@ impl Buffer {
#[cfg(any(test, feature = "test-support"))]
impl Buffer {
#[track_caller]
pub fn edit_via_marked_text(&mut self, marked_string: &str) {
pub fn edit_via_marked_text(&mut self, marked_string: &str, cx: &BackgroundExecutor) {
let edits = self.edits_for_marked_text(marked_string);
self.edit(edits);
self.edit(edits, cx);
}
#[track_caller]
@@ -1850,6 +1891,7 @@ impl Buffer {
&mut self,
rng: &mut T,
edit_count: usize,
executor: &BackgroundExecutor,
) -> (Vec<(Range<usize>, Arc<str>)>, Operation)
where
T: rand::Rng,
@@ -1857,7 +1899,7 @@ impl Buffer {
let mut edits = self.get_random_edits(rng, edit_count);
log::info!("mutating buffer {:?} with {:?}", self.replica_id, edits);
let op = self.edit(edits.iter().cloned());
let op = self.edit(edits.iter().cloned(), executor);
if let Operation::Edit(edit) = &op {
assert_eq!(edits.len(), edit.new_text.len());
for (edit, new_text) in edits.iter_mut().zip(&edit.new_text) {
@@ -2692,8 +2734,12 @@ impl<'a> RopeBuilder<'a> {
}
}
fn push_str(&mut self, text: &str) {
self.new_visible.push(text);
fn push_str(&mut self, text: &str, cx: &BackgroundExecutor) {
self.new_visible.push(text, cx);
}
fn push_str_small(&mut self, text: &str) {
self.new_visible.push_small(text);
}
fn finish(mut self) -> (Rope, Rope) {