Revert "sum_tree: Replace rayon with futures (#41586) (#41846)

This causes the background executor to hang

Release Notes:

- N/A *or* Added/Fixed/Improved ...
This commit is contained in:
Lukas Wirth 2025-11-03 20:25:15 +01:00 committed by GitHub
parent cb5055aaec
commit 5fc54986c7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
67 changed files with 650 additions and 1287 deletions

14
Cargo.lock generated
View file

@ -12717,12 +12717,6 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5da3b0203fd7ee5720aa0b5e790b591aa5d3f41c3ed2c34a3a393382198af2f7"
[[package]]
name = "pollster"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3"
[[package]]
name = "portable-atomic"
version = "1.11.1"
@ -12771,7 +12765,7 @@ dependencies = [
"log",
"parking_lot",
"pin-project",
"pollster 0.2.5",
"pollster",
"static_assertions",
"thiserror 1.0.69",
]
@ -14323,6 +14317,7 @@ dependencies = [
"gpui",
"log",
"rand 0.9.2",
"rayon",
"sum_tree",
"unicode-segmentation",
"util",
@ -16248,7 +16243,6 @@ checksum = "2b2231b7c3057d5e4ad0156fb3dc807d900806020c5ffa3ee6ff2c8c76fb8520"
name = "streaming_diff"
version = "0.1.0"
dependencies = [
"gpui",
"ordered-float 2.10.1",
"rand 0.9.2",
"rope",
@ -16367,11 +16361,9 @@ version = "0.1.0"
dependencies = [
"arrayvec",
"ctor",
"futures 0.3.31",
"futures-lite 1.13.0",
"log",
"pollster 0.4.0",
"rand 0.9.2",
"rayon",
"zlog",
]

View file

@ -361,12 +361,10 @@ async fn build_buffer_diff(
) -> Result<Entity<BufferDiff>> {
let buffer = cx.update(|cx| buffer.read(cx).snapshot())?;
let executor = cx.background_executor().clone();
let old_text_rope = cx
.background_spawn({
let old_text = old_text.clone();
let executor = executor.clone();
async move { Rope::from_str(old_text.as_str(), &executor) }
async move { Rope::from(old_text.as_str()) }
})
.await;
let base_buffer = cx

View file

@ -3,9 +3,7 @@ use buffer_diff::BufferDiff;
use clock;
use collections::BTreeMap;
use futures::{FutureExt, StreamExt, channel::mpsc};
use gpui::{
App, AppContext, AsyncApp, BackgroundExecutor, Context, Entity, Subscription, Task, WeakEntity,
};
use gpui::{App, AppContext, AsyncApp, Context, Entity, Subscription, Task, WeakEntity};
use language::{Anchor, Buffer, BufferEvent, DiskState, Point, ToPoint};
use project::{Project, ProjectItem, lsp_store::OpenLspBufferHandle};
use std::{cmp, ops::Range, sync::Arc};
@ -323,7 +321,6 @@ impl ActionLog {
let unreviewed_edits = tracked_buffer.unreviewed_edits.clone();
let edits = diff_snapshots(&old_snapshot, &new_snapshot);
let mut has_user_changes = false;
let executor = cx.background_executor().clone();
async move {
if let ChangeAuthor::User = author {
has_user_changes = apply_non_conflicting_edits(
@ -331,7 +328,6 @@ impl ActionLog {
edits,
&mut base_text,
new_snapshot.as_rope(),
&executor,
);
}
@ -386,7 +382,6 @@ impl ActionLog {
let agent_diff_base = tracked_buffer.diff_base.clone();
let git_diff_base = git_diff.read(cx).base_text().as_rope().clone();
let buffer_text = tracked_buffer.snapshot.as_rope().clone();
let executor = cx.background_executor().clone();
anyhow::Ok(cx.background_spawn(async move {
let mut old_unreviewed_edits = old_unreviewed_edits.into_iter().peekable();
let committed_edits = language::line_diff(
@ -421,11 +416,8 @@ impl ActionLog {
),
new_agent_diff_base.max_point(),
));
new_agent_diff_base.replace(
old_byte_start..old_byte_end,
&unreviewed_new,
&executor,
);
new_agent_diff_base
.replace(old_byte_start..old_byte_end, &unreviewed_new);
row_delta +=
unreviewed.new_len() as i32 - unreviewed.old_len() as i32;
}
@ -619,7 +611,6 @@ impl ActionLog {
.snapshot
.text_for_range(new_range)
.collect::<String>(),
cx.background_executor(),
);
delta += edit.new_len() as i32 - edit.old_len() as i32;
false
@ -833,7 +824,6 @@ fn apply_non_conflicting_edits(
edits: Vec<Edit<u32>>,
old_text: &mut Rope,
new_text: &Rope,
executor: &BackgroundExecutor,
) -> bool {
let mut old_edits = patch.edits().iter().cloned().peekable();
let mut new_edits = edits.into_iter().peekable();
@ -887,7 +877,6 @@ fn apply_non_conflicting_edits(
old_text.replace(
old_bytes,
&new_text.chunks_in_range(new_bytes).collect::<String>(),
executor,
);
applied_delta += new_edit.new_len() as i32 - new_edit.old_len() as i32;
has_made_changes = true;
@ -2293,7 +2282,6 @@ mod tests {
old_text.replace(
old_start..old_end,
&new_text.slice_rows(edit.new.clone()).to_string(),
cx.background_executor(),
);
}
pretty_assertions::assert_eq!(old_text.to_string(), new_text.to_string());

View file

@ -305,20 +305,18 @@ impl SearchMatrix {
#[cfg(test)]
mod tests {
use super::*;
use gpui::TestAppContext;
use indoc::indoc;
use language::{BufferId, TextBuffer};
use rand::prelude::*;
use text::ReplicaId;
use util::test::{generate_marked_text, marked_text_ranges};
#[gpui::test]
fn test_empty_query(cx: &mut gpui::TestAppContext) {
#[test]
fn test_empty_query() {
let buffer = TextBuffer::new(
ReplicaId::LOCAL,
BufferId::new(1).unwrap(),
"Hello world\nThis is a test\nFoo bar baz",
cx.background_executor(),
);
let snapshot = buffer.snapshot();
@ -327,13 +325,12 @@ mod tests {
assert_eq!(finish(finder), None);
}
#[gpui::test]
fn test_streaming_exact_match(cx: &mut gpui::TestAppContext) {
#[test]
fn test_streaming_exact_match() {
let buffer = TextBuffer::new(
ReplicaId::LOCAL,
BufferId::new(1).unwrap(),
"Hello world\nThis is a test\nFoo bar baz",
cx.background_executor(),
);
let snapshot = buffer.snapshot();
@ -352,8 +349,8 @@ mod tests {
assert_eq!(finish(finder), Some("This is a test".to_string()));
}
#[gpui::test]
fn test_streaming_fuzzy_match(cx: &mut gpui::TestAppContext) {
#[test]
fn test_streaming_fuzzy_match() {
let buffer = TextBuffer::new(
ReplicaId::LOCAL,
BufferId::new(1).unwrap(),
@ -366,7 +363,6 @@ mod tests {
return x * y;
}
"},
cx.background_executor(),
);
let snapshot = buffer.snapshot();
@ -387,13 +383,12 @@ mod tests {
);
}
#[gpui::test]
fn test_incremental_improvement(cx: &mut gpui::TestAppContext) {
#[test]
fn test_incremental_improvement() {
let buffer = TextBuffer::new(
ReplicaId::LOCAL,
BufferId::new(1).unwrap(),
"Line 1\nLine 2\nLine 3\nLine 4\nLine 5",
cx.background_executor(),
);
let snapshot = buffer.snapshot();
@ -413,8 +408,8 @@ mod tests {
assert_eq!(finish(finder), Some("Line 3\nLine 4".to_string()));
}
#[gpui::test]
fn test_incomplete_lines_buffering(cx: &mut gpui::TestAppContext) {
#[test]
fn test_incomplete_lines_buffering() {
let buffer = TextBuffer::new(
ReplicaId::LOCAL,
BufferId::new(1).unwrap(),
@ -423,7 +418,6 @@ mod tests {
jumps over the lazy dog
Pack my box with five dozen liquor jugs
"},
cx.background_executor(),
);
let snapshot = buffer.snapshot();
@ -441,8 +435,8 @@ mod tests {
);
}
#[gpui::test]
fn test_multiline_fuzzy_match(cx: &mut gpui::TestAppContext) {
#[test]
fn test_multiline_fuzzy_match() {
let buffer = TextBuffer::new(
ReplicaId::LOCAL,
BufferId::new(1).unwrap(),
@ -462,7 +456,6 @@ mod tests {
}
}
"#},
cx.background_executor(),
);
let snapshot = buffer.snapshot();
@ -516,7 +509,7 @@ mod tests {
}
#[gpui::test(iterations = 100)]
fn test_resolve_location_single_line(mut rng: StdRng, cx: &mut TestAppContext) {
fn test_resolve_location_single_line(mut rng: StdRng) {
assert_location_resolution(
concat!(
" Lorem\n",
@ -526,12 +519,11 @@ mod tests {
),
"ipsum",
&mut rng,
cx,
);
}
#[gpui::test(iterations = 100)]
fn test_resolve_location_multiline(mut rng: StdRng, cx: &mut TestAppContext) {
fn test_resolve_location_multiline(mut rng: StdRng) {
assert_location_resolution(
concat!(
" Lorem\n",
@ -541,12 +533,11 @@ mod tests {
),
"ipsum\ndolor sit amet",
&mut rng,
cx,
);
}
#[gpui::test(iterations = 100)]
fn test_resolve_location_function_with_typo(mut rng: StdRng, cx: &mut TestAppContext) {
fn test_resolve_location_function_with_typo(mut rng: StdRng) {
assert_location_resolution(
indoc! {"
«fn foo1(a: usize) -> usize {
@ -559,12 +550,11 @@ mod tests {
"},
"fn foo1(a: usize) -> u32 {\n40\n}",
&mut rng,
cx,
);
}
#[gpui::test(iterations = 100)]
fn test_resolve_location_class_methods(mut rng: StdRng, cx: &mut TestAppContext) {
fn test_resolve_location_class_methods(mut rng: StdRng) {
assert_location_resolution(
indoc! {"
class Something {
@ -585,12 +575,11 @@ mod tests {
six() { return 6666; }
"},
&mut rng,
cx,
);
}
#[gpui::test(iterations = 100)]
fn test_resolve_location_imports_no_match(mut rng: StdRng, cx: &mut TestAppContext) {
fn test_resolve_location_imports_no_match(mut rng: StdRng) {
assert_location_resolution(
indoc! {"
use std::ops::Range;
@ -620,12 +609,11 @@ mod tests {
use std::sync::Arc;
"},
&mut rng,
cx,
);
}
#[gpui::test(iterations = 100)]
fn test_resolve_location_nested_closure(mut rng: StdRng, cx: &mut TestAppContext) {
fn test_resolve_location_nested_closure(mut rng: StdRng) {
assert_location_resolution(
indoc! {"
impl Foo {
@ -653,12 +641,11 @@ mod tests {
" });",
),
&mut rng,
cx,
);
}
#[gpui::test(iterations = 100)]
fn test_resolve_location_tool_invocation(mut rng: StdRng, cx: &mut TestAppContext) {
fn test_resolve_location_tool_invocation(mut rng: StdRng) {
assert_location_resolution(
indoc! {r#"
let tool = cx
@ -686,12 +673,11 @@ mod tests {
" .output;",
),
&mut rng,
cx,
);
}
#[gpui::test]
fn test_line_hint_selection(cx: &mut TestAppContext) {
fn test_line_hint_selection() {
let text = indoc! {r#"
fn first_function() {
return 42;
@ -710,7 +696,6 @@ mod tests {
ReplicaId::LOCAL,
BufferId::new(1).unwrap(),
text.to_string(),
cx.background_executor(),
);
let snapshot = buffer.snapshot();
let mut matcher = StreamingFuzzyMatcher::new(snapshot.clone());
@ -742,19 +727,9 @@ mod tests {
}
#[track_caller]
fn assert_location_resolution(
text_with_expected_range: &str,
query: &str,
rng: &mut StdRng,
cx: &mut TestAppContext,
) {
fn assert_location_resolution(text_with_expected_range: &str, query: &str, rng: &mut StdRng) {
let (text, expected_ranges) = marked_text_ranges(text_with_expected_range, false);
let buffer = TextBuffer::new(
ReplicaId::LOCAL,
BufferId::new(1).unwrap(),
text.clone(),
cx.background_executor(),
);
let buffer = TextBuffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), text.clone());
let snapshot = buffer.snapshot();
let mut matcher = StreamingFuzzyMatcher::new(snapshot);

View file

@ -569,7 +569,6 @@ mod tests {
use prompt_store::ProjectContext;
use serde_json::json;
use settings::SettingsStore;
use text::Rope;
use util::{path, rel_path::rel_path};
#[gpui::test]
@ -742,7 +741,7 @@ mod tests {
// Create the file
fs.save(
path!("/root/src/main.rs").as_ref(),
&Rope::from_str_small("initial content"),
&"initial content".into(),
language::LineEnding::Unix,
)
.await
@ -909,7 +908,7 @@ mod tests {
// Create a simple file with trailing whitespace
fs.save(
path!("/root/src/main.rs").as_ref(),
&Rope::from_str_small("initial content"),
&"initial content".into(),
language::LineEnding::Unix,
)
.await

View file

@ -29,7 +29,6 @@ use project::{
agent_server_store::{AgentServerStore, CLAUDE_CODE_NAME, CODEX_NAME, GEMINI_NAME},
context_server_store::{ContextServerConfiguration, ContextServerStatus, ContextServerStore},
};
use rope::Rope;
use settings::{Settings, SettingsStore, update_settings_file};
use ui::{
Button, ButtonStyle, Chip, CommonAnimationExt, ContextMenu, Disclosure, Divider, DividerColor,
@ -1183,11 +1182,8 @@ async fn open_new_agent_servers_entry_in_settings_editor(
) -> Result<()> {
let settings_editor = workspace
.update_in(cx, |_, window, cx| {
create_and_open_local_file(paths::settings_file(), window, cx, |cx| {
Rope::from_str(
&settings::initial_user_settings_content(),
cx.background_executor(),
)
create_and_open_local_file(paths::settings_file(), window, cx, || {
settings::initial_user_settings_content().as_ref().into()
})
})?
.await?

View file

@ -487,10 +487,9 @@ impl CodegenAlternative {
) {
let start_time = Instant::now();
let snapshot = self.snapshot.clone();
let selected_text = Rope::from_iter(
snapshot.text_for_range(self.range.start..self.range.end),
cx.background_executor(),
);
let selected_text = snapshot
.text_for_range(self.range.start..self.range.end)
.collect::<Rope>();
let selection_start = self.range.start.to_point(&snapshot);

View file

@ -744,13 +744,12 @@ impl TextThread {
telemetry: Option<Arc<Telemetry>>,
cx: &mut Context<Self>,
) -> Self {
let buffer = cx.new(|cx| {
let buffer = cx.new(|_cx| {
let buffer = Buffer::remote(
language::BufferId::new(1).unwrap(),
replica_id,
capability,
"",
cx.background_executor(),
);
buffer.set_language_registry(language_registry.clone());
buffer

View file

@ -1,9 +1,6 @@
use futures::channel::oneshot;
use git2::{DiffLineType as GitDiffLineType, DiffOptions as GitOptions, Patch as GitPatch};
use gpui::{
App, AppContext as _, AsyncApp, BackgroundExecutor, Context, Entity, EventEmitter, Task,
TaskLabel,
};
use gpui::{App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, Task, TaskLabel};
use language::{Language, LanguageRegistry};
use rope::Rope;
use std::{
@ -194,7 +191,7 @@ impl BufferDiffSnapshot {
let base_text_exists;
let base_text_snapshot;
if let Some(text) = &base_text {
let base_text_rope = Rope::from_str(text.as_str(), cx.background_executor());
let base_text_rope = Rope::from(text.as_str());
base_text_pair = Some((text.clone(), base_text_rope.clone()));
let snapshot =
language::Buffer::build_snapshot(base_text_rope, language, language_registry, cx);
@ -314,7 +311,6 @@ impl BufferDiffInner {
hunks: &[DiffHunk],
buffer: &text::BufferSnapshot,
file_exists: bool,
cx: &BackgroundExecutor,
) -> Option<Rope> {
let head_text = self
.base_text_exists
@ -509,7 +505,7 @@ impl BufferDiffInner {
for (old_range, replacement_text) in edits {
new_index_text.append(index_cursor.slice(old_range.start));
index_cursor.seek_forward(old_range.end);
new_index_text.push(&replacement_text, cx);
new_index_text.push(&replacement_text);
}
new_index_text.append(index_cursor.suffix());
Some(new_index_text)
@ -966,7 +962,6 @@ impl BufferDiff {
hunks,
buffer,
file_exists,
cx.background_executor(),
);
cx.emit(BufferDiffEvent::HunksStagedOrUnstaged(
@ -1390,12 +1385,7 @@ mod tests {
"
.unindent();
let mut buffer = Buffer::new(
ReplicaId::LOCAL,
BufferId::new(1).unwrap(),
buffer_text,
cx.background_executor(),
);
let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), buffer_text);
let mut diff = BufferDiffSnapshot::new_sync(buffer.clone(), diff_base.clone(), cx);
assert_hunks(
diff.hunks_intersecting_range(Anchor::MIN..Anchor::MAX, &buffer),
@ -1404,7 +1394,7 @@ mod tests {
&[(1..2, "two\n", "HELLO\n", DiffHunkStatus::modified_none())],
);
buffer.edit([(0..0, "point five\n")], cx.background_executor());
buffer.edit([(0..0, "point five\n")]);
diff = BufferDiffSnapshot::new_sync(buffer.clone(), diff_base.clone(), cx);
assert_hunks(
diff.hunks_intersecting_range(Anchor::MIN..Anchor::MAX, &buffer),
@ -1469,12 +1459,7 @@ mod tests {
"
.unindent();
let buffer = Buffer::new(
ReplicaId::LOCAL,
BufferId::new(1).unwrap(),
buffer_text,
cx.background_executor(),
);
let buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), buffer_text);
let unstaged_diff = BufferDiffSnapshot::new_sync(buffer.clone(), index_text, cx);
let mut uncommitted_diff =
BufferDiffSnapshot::new_sync(buffer.clone(), head_text.clone(), cx);
@ -1543,12 +1528,7 @@ mod tests {
"
.unindent();
let buffer = Buffer::new(
ReplicaId::LOCAL,
BufferId::new(1).unwrap(),
buffer_text,
cx.background_executor(),
);
let buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), buffer_text);
let diff = cx
.update(|cx| {
BufferDiffSnapshot::new_with_base_text(
@ -1811,12 +1791,7 @@ mod tests {
for example in table {
let (buffer_text, ranges) = marked_text_ranges(&example.buffer_marked_text, false);
let buffer = Buffer::new(
ReplicaId::LOCAL,
BufferId::new(1).unwrap(),
buffer_text,
cx.background_executor(),
);
let buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), buffer_text);
let hunk_range =
buffer.anchor_before(ranges[0].start)..buffer.anchor_before(ranges[0].end);
@ -1893,7 +1868,6 @@ mod tests {
ReplicaId::LOCAL,
BufferId::new(1).unwrap(),
buffer_text.clone(),
cx.background_executor(),
);
let unstaged = BufferDiffSnapshot::new_sync(buffer.clone(), index_text, cx);
let uncommitted = BufferDiffSnapshot::new_sync(buffer.clone(), head_text.clone(), cx);
@ -1967,12 +1941,7 @@ mod tests {
"
.unindent();
let mut buffer = Buffer::new(
ReplicaId::LOCAL,
BufferId::new(1).unwrap(),
buffer_text_1,
cx.background_executor(),
);
let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), buffer_text_1);
let empty_diff = cx.update(|cx| BufferDiffSnapshot::empty(&buffer, cx));
let diff_1 = BufferDiffSnapshot::new_sync(buffer.clone(), base_text.clone(), cx);
@ -1992,7 +1961,6 @@ mod tests {
NINE
"
.unindent(),
cx.background_executor(),
);
let diff_2 = BufferDiffSnapshot::new_sync(buffer.clone(), base_text.clone(), cx);
assert_eq!(None, diff_2.inner.compare(&diff_1.inner, &buffer));
@ -2010,7 +1978,6 @@ mod tests {
NINE
"
.unindent(),
cx.background_executor(),
);
let diff_3 = BufferDiffSnapshot::new_sync(buffer.clone(), base_text.clone(), cx);
let range = diff_3.inner.compare(&diff_2.inner, &buffer).unwrap();
@ -2028,7 +1995,6 @@ mod tests {
NINE
"
.unindent(),
cx.background_executor(),
);
let diff_4 = BufferDiffSnapshot::new_sync(buffer.clone(), base_text.clone(), cx);
let range = diff_4.inner.compare(&diff_3.inner, &buffer).unwrap();
@ -2047,7 +2013,6 @@ mod tests {
NINE
"
.unindent(),
cx.background_executor(),
);
let diff_5 = BufferDiffSnapshot::new_sync(buffer.snapshot(), base_text.clone(), cx);
let range = diff_5.inner.compare(&diff_4.inner, &buffer).unwrap();
@ -2066,7 +2031,6 @@ mod tests {
«nine»
"
.unindent(),
cx.background_executor(),
);
let diff_6 = BufferDiffSnapshot::new_sync(buffer.snapshot(), base_text, cx);
let range = diff_6.inner.compare(&diff_5.inner, &buffer).unwrap();
@ -2176,14 +2140,14 @@ mod tests {
let working_copy = gen_working_copy(rng, &head_text);
let working_copy = cx.new(|cx| {
language::Buffer::local_normalized(
Rope::from_str(working_copy.as_str(), cx.background_executor()),
Rope::from(working_copy.as_str()),
text::LineEnding::default(),
cx,
)
});
let working_copy = working_copy.read_with(cx, |working_copy, _| working_copy.snapshot());
let mut index_text = if rng.random() {
Rope::from_str(head_text.as_str(), cx.background_executor())
Rope::from(head_text.as_str())
} else {
working_copy.as_rope().clone()
};

View file

@ -70,7 +70,6 @@ impl ChannelBuffer {
ReplicaId::new(response.replica_id as u16),
capability,
base_text,
cx.background_executor(),
)
})?;
buffer.update(cx, |buffer, cx| buffer.apply_ops(operations, cx))?;

View file

@ -701,12 +701,12 @@ impl Database {
return Ok(());
}
let mut text_buffer = text::Buffer::new_slow(
let mut text_buffer = text::Buffer::new(
clock::ReplicaId::LOCAL,
text::BufferId::new(1).unwrap(),
base_text,
);
text_buffer.apply_ops(operations.into_iter().filter_map(operation_from_wire), None);
text_buffer.apply_ops(operations.into_iter().filter_map(operation_from_wire));
let base_text = text_buffer.text();
let epoch = buffer.epoch + 1;

View file

@ -74,21 +74,11 @@ async fn test_channel_buffers(db: &Arc<Database>) {
ReplicaId::new(0),
text::BufferId::new(1).unwrap(),
"".to_string(),
&db.test_options.as_ref().unwrap().executor,
);
let operations = vec![
buffer_a.edit(
[(0..0, "hello world")],
&db.test_options.as_ref().unwrap().executor,
),
buffer_a.edit(
[(5..5, ", cruel")],
&db.test_options.as_ref().unwrap().executor,
),
buffer_a.edit(
[(0..5, "goodbye")],
&db.test_options.as_ref().unwrap().executor,
),
buffer_a.edit([(0..0, "hello world")]),
buffer_a.edit([(5..5, ", cruel")]),
buffer_a.edit([(0..5, "goodbye")]),
buffer_a.undo().unwrap().1,
];
assert_eq!(buffer_a.text(), "hello, cruel world");
@ -112,19 +102,15 @@ async fn test_channel_buffers(db: &Arc<Database>) {
ReplicaId::new(0),
text::BufferId::new(1).unwrap(),
buffer_response_b.base_text,
&db.test_options.as_ref().unwrap().executor,
);
buffer_b.apply_ops(
buffer_response_b.operations.into_iter().map(|operation| {
let operation = proto::deserialize_operation(operation).unwrap();
if let language::Operation::Buffer(operation) = operation {
operation
} else {
unreachable!()
}
}),
None,
);
buffer_b.apply_ops(buffer_response_b.operations.into_iter().map(|operation| {
let operation = proto::deserialize_operation(operation).unwrap();
if let language::Operation::Buffer(operation) = operation {
operation
} else {
unreachable!()
}
}));
assert_eq!(buffer_b.text(), "hello, cruel world");
@ -261,7 +247,6 @@ async fn test_channel_buffers_last_operations(db: &Database) {
ReplicaId::new(res.replica_id as u16),
text::BufferId::new(1).unwrap(),
"".to_string(),
&db.test_options.as_ref().unwrap().executor,
));
}
@ -270,9 +255,9 @@ async fn test_channel_buffers_last_operations(db: &Database) {
user_id,
db,
vec![
text_buffers[0].edit([(0..0, "a")], &db.test_options.as_ref().unwrap().executor),
text_buffers[0].edit([(0..0, "b")], &db.test_options.as_ref().unwrap().executor),
text_buffers[0].edit([(0..0, "c")], &db.test_options.as_ref().unwrap().executor),
text_buffers[0].edit([(0..0, "a")]),
text_buffers[0].edit([(0..0, "b")]),
text_buffers[0].edit([(0..0, "c")]),
],
)
.await;
@ -282,9 +267,9 @@ async fn test_channel_buffers_last_operations(db: &Database) {
user_id,
db,
vec![
text_buffers[1].edit([(0..0, "d")], &db.test_options.as_ref().unwrap().executor),
text_buffers[1].edit([(1..1, "e")], &db.test_options.as_ref().unwrap().executor),
text_buffers[1].edit([(2..2, "f")], &db.test_options.as_ref().unwrap().executor),
text_buffers[1].edit([(0..0, "d")]),
text_buffers[1].edit([(1..1, "e")]),
text_buffers[1].edit([(2..2, "f")]),
],
)
.await;
@ -301,15 +286,14 @@ async fn test_channel_buffers_last_operations(db: &Database) {
replica_id,
text::BufferId::new(1).unwrap(),
"def".to_string(),
&db.test_options.as_ref().unwrap().executor,
);
update_buffer(
buffers[1].channel_id,
user_id,
db,
vec![
text_buffers[1].edit([(0..0, "g")], &db.test_options.as_ref().unwrap().executor),
text_buffers[1].edit([(0..0, "h")], &db.test_options.as_ref().unwrap().executor),
text_buffers[1].edit([(0..0, "g")]),
text_buffers[1].edit([(0..0, "h")]),
],
)
.await;
@ -318,7 +302,7 @@ async fn test_channel_buffers_last_operations(db: &Database) {
buffers[2].channel_id,
user_id,
db,
vec![text_buffers[2].edit([(0..0, "i")], &db.test_options.as_ref().unwrap().executor)],
vec![text_buffers[2].edit([(0..0, "i")])],
)
.await;

View file

@ -3694,7 +3694,7 @@ async fn test_buffer_reloading(
assert_eq!(buf.line_ending(), LineEnding::Unix);
});
let new_contents = Rope::from_str_small("d\ne\nf");
let new_contents = Rope::from("d\ne\nf");
client_a
.fs()
.save(
@ -4479,7 +4479,7 @@ async fn test_reloading_buffer_manually(
.fs()
.save(
path!("/a/a.rs").as_ref(),
&Rope::from_str_small("let seven = 7;"),
&Rope::from("let seven = 7;"),
LineEnding::Unix,
)
.await

View file

@ -27,7 +27,6 @@ use std::{
rc::Rc,
sync::Arc,
};
use text::Rope;
use util::{
ResultExt, path,
paths::PathStyle,
@ -939,11 +938,7 @@ impl RandomizedTest for ProjectCollaborationTest {
client
.fs()
.save(
&path,
&Rope::from_str_small(content.as_str()),
text::LineEnding::Unix,
)
.save(&path, &content.as_str().into(), text::LineEnding::Unix)
.await
.unwrap();
}

View file

@ -887,7 +887,7 @@ async fn test_random_diagnostics_with_inlays(cx: &mut TestAppContext, mut rng: S
vec![Inlay::edit_prediction(
post_inc(&mut next_inlay_id),
snapshot.buffer_snapshot().anchor_before(position),
Rope::from_iter_small(["Test inlay ", "next_inlay_id"]),
Rope::from_iter(["Test inlay ", "next_inlay_id"]),
)],
cx,
);
@ -2080,7 +2080,7 @@ fn random_lsp_diagnostic(
const ERROR_MARGIN: usize = 10;
let file_content = fs.read_file_sync(path).unwrap();
let file_text = Rope::from_str_small(String::from_utf8_lossy(&file_content).as_ref());
let file_text = Rope::from(String::from_utf8_lossy(&file_content).as_ref());
let start = rng.random_range(0..file_text.len().saturating_add(ERROR_MARGIN));
let end = rng.random_range(start..file_text.len().saturating_add(ERROR_MARGIN));

View file

@ -13,7 +13,7 @@ use gpui::{
};
use indoc::indoc;
use language::{
EditPredictionsMode, File, Language, Rope,
EditPredictionsMode, File, Language,
language_settings::{self, AllLanguageSettings, EditPredictionProvider, all_language_settings},
};
use project::DisableAiSettings;
@ -1056,11 +1056,8 @@ async fn open_disabled_globs_setting_in_editor(
) -> Result<()> {
let settings_editor = workspace
.update_in(cx, |_, window, cx| {
create_and_open_local_file(paths::settings_file(), window, cx, |cx| {
Rope::from_str(
settings::initial_user_settings_content().as_ref(),
cx.background_executor(),
)
create_and_open_local_file(paths::settings_file(), window, cx, || {
settings::initial_user_settings_content().as_ref().into()
})
})?
.await?

View file

@ -1569,7 +1569,6 @@ pub mod tests {
use lsp::LanguageServerId;
use project::Project;
use rand::{Rng, prelude::*};
use rope::Rope;
use settings::{SettingsContent, SettingsStore};
use smol::stream::StreamExt;
use std::{env, sync::Arc};
@ -2075,7 +2074,7 @@ pub mod tests {
vec![Inlay::edit_prediction(
0,
buffer_snapshot.anchor_after(0),
Rope::from_str_small("\n"),
"\n",
)],
cx,
);

View file

@ -700,20 +700,16 @@ impl InlayMap {
.collect::<String>();
let next_inlay = if i % 2 == 0 {
use rope::Rope;
Inlay::mock_hint(
post_inc(next_inlay_id),
snapshot.buffer.anchor_at(position, bias),
Rope::from_str_small(&text),
&text,
)
} else {
use rope::Rope;
Inlay::edit_prediction(
post_inc(next_inlay_id),
snapshot.buffer.anchor_at(position, bias),
Rope::from_str_small(&text),
&text,
)
};
let inlay_id = next_inlay.id;
@ -1305,7 +1301,7 @@ mod tests {
vec![Inlay::mock_hint(
post_inc(&mut next_inlay_id),
buffer.read(cx).snapshot(cx).anchor_after(3),
Rope::from_str_small("|123|"),
"|123|",
)],
);
assert_eq!(inlay_snapshot.text(), "abc|123|defghi");
@ -1382,12 +1378,12 @@ mod tests {
Inlay::mock_hint(
post_inc(&mut next_inlay_id),
buffer.read(cx).snapshot(cx).anchor_before(3),
Rope::from_str_small("|123|"),
"|123|",
),
Inlay::edit_prediction(
post_inc(&mut next_inlay_id),
buffer.read(cx).snapshot(cx).anchor_after(3),
Rope::from_str_small("|456|"),
"|456|",
),
],
);
@ -1597,17 +1593,17 @@ mod tests {
Inlay::mock_hint(
post_inc(&mut next_inlay_id),
buffer.read(cx).snapshot(cx).anchor_before(0),
Rope::from_str_small("|123|\n"),
"|123|\n",
),
Inlay::mock_hint(
post_inc(&mut next_inlay_id),
buffer.read(cx).snapshot(cx).anchor_before(4),
Rope::from_str_small("|456|"),
"|456|",
),
Inlay::edit_prediction(
post_inc(&mut next_inlay_id),
buffer.read(cx).snapshot(cx).anchor_before(7),
Rope::from_str_small("\n|567|\n"),
"\n|567|\n",
),
],
);
@ -1681,14 +1677,9 @@ mod tests {
(offset, inlay.clone())
})
.collect::<Vec<_>>();
let mut expected_text =
Rope::from_str(&buffer_snapshot.text(), cx.background_executor());
let mut expected_text = Rope::from(&buffer_snapshot.text());
for (offset, inlay) in inlays.iter().rev() {
expected_text.replace(
*offset..*offset,
&inlay.text().to_string(),
cx.background_executor(),
);
expected_text.replace(*offset..*offset, &inlay.text().to_string());
}
assert_eq!(inlay_snapshot.text(), expected_text.to_string());
@ -2076,7 +2067,7 @@ mod tests {
let inlay = Inlay {
id: InlayId::Hint(0),
position,
content: InlayContent::Text(text::Rope::from_str(inlay_text, cx.background_executor())),
content: InlayContent::Text(text::Rope::from(inlay_text)),
};
let (inlay_snapshot, _) = inlay_map.splice(&[], vec![inlay]);
@ -2190,10 +2181,7 @@ mod tests {
let inlay = Inlay {
id: InlayId::Hint(0),
position,
content: InlayContent::Text(text::Rope::from_str(
test_case.inlay_text,
cx.background_executor(),
)),
content: InlayContent::Text(text::Rope::from(test_case.inlay_text)),
};
let (inlay_snapshot, _) = inlay_map.splice(&[], vec![inlay]);

View file

@ -1042,7 +1042,7 @@ mod tests {
let (mut tab_map, _) = TabMap::new(fold_snapshot, tab_size);
let tabs_snapshot = tab_map.set_max_expansion_column(32);
let text = text::Rope::from_str(tabs_snapshot.text().as_str(), cx.background_executor());
let text = text::Rope::from(tabs_snapshot.text().as_str());
log::info!(
"TabMap text (tab size: {}): {:?}",
tab_size,

View file

@ -863,7 +863,7 @@ impl WrapSnapshot {
}
}
let text = language::Rope::from_str_small(self.text().as_str());
let text = language::Rope::from(self.text().as_str());
let mut input_buffer_rows = self.tab_snapshot.rows(0);
let mut expected_buffer_rows = Vec::new();
let mut prev_tab_row = 0;
@ -1413,10 +1413,9 @@ mod tests {
}
}
let mut initial_text =
Rope::from_str(initial_snapshot.text().as_str(), cx.background_executor());
let mut initial_text = Rope::from(initial_snapshot.text().as_str());
for (snapshot, patch) in edits {
let snapshot_text = Rope::from_str(snapshot.text().as_str(), cx.background_executor());
let snapshot_text = Rope::from(snapshot.text().as_str());
for edit in &patch {
let old_start = initial_text.point_to_offset(Point::new(edit.new.start, 0));
let old_end = initial_text.point_to_offset(cmp::min(
@ -1432,7 +1431,7 @@ mod tests {
.chunks_in_range(new_start..new_end)
.collect::<String>();
initial_text.replace(old_start..old_end, &new_text, cx.background_executor());
initial_text.replace(old_start..old_end, &new_text);
}
assert_eq!(initial_text.to_string(), snapshot_text.to_string());
}

View file

@ -7858,7 +7858,7 @@ impl Editor {
let inlay = Inlay::edit_prediction(
post_inc(&mut self.next_inlay_id),
range.start,
Rope::from_str_small(new_text.as_str()),
new_text.as_str(),
);
inlay_ids.push(inlay.id);
inlays.push(inlay);

View file

@ -1115,19 +1115,18 @@ mod tests {
let fs = FakeFs::new(cx.executor());
let buffer_initial_text_len = rng.random_range(5..15);
let mut buffer_initial_text = Rope::from_str(
let mut buffer_initial_text = Rope::from(
RandomCharIter::new(&mut rng)
.take(buffer_initial_text_len)
.collect::<String>()
.as_str(),
cx.background_executor(),
);
let mut newline_ixs = (0..buffer_initial_text_len).choose_multiple(&mut rng, 5);
newline_ixs.sort_unstable();
for newline_ix in newline_ixs.into_iter().rev() {
let newline_ix = buffer_initial_text.clip_offset(newline_ix, Bias::Right);
buffer_initial_text.replace(newline_ix..newline_ix, "\n", cx.background_executor());
buffer_initial_text.replace(newline_ix..newline_ix, "\n");
}
log::info!("initial buffer text: {:?}", buffer_initial_text);

View file

@ -59,10 +59,10 @@ impl Inlay {
pub fn hint(id: InlayId, position: Anchor, hint: &InlayHint) -> Self {
let mut text = hint.text();
if hint.padding_right && text.reversed_chars_at(text.len()).next() != Some(' ') {
text.push_small(" ");
text.push(" ");
}
if hint.padding_left && text.chars_at(0).next() != Some(' ') {
text.push_front_small(" ");
text.push_front(" ");
}
Self {
id,
@ -72,11 +72,11 @@ impl Inlay {
}
#[cfg(any(test, feature = "test-support"))]
pub fn mock_hint(id: usize, position: Anchor, text: Rope) -> Self {
pub fn mock_hint(id: usize, position: Anchor, text: impl Into<Rope>) -> Self {
Self {
id: InlayId::Hint(id),
position,
content: InlayContent::Text(text),
content: InlayContent::Text(text.into()),
}
}
@ -88,19 +88,19 @@ impl Inlay {
}
}
pub fn edit_prediction(id: usize, position: Anchor, text: Rope) -> Self {
pub fn edit_prediction<T: Into<Rope>>(id: usize, position: Anchor, text: T) -> Self {
Self {
id: InlayId::EditPrediction(id),
position,
content: InlayContent::Text(text),
content: InlayContent::Text(text.into()),
}
}
pub fn debugger(id: usize, position: Anchor, text: Rope) -> Self {
pub fn debugger<T: Into<Rope>>(id: usize, position: Anchor, text: T) -> Self {
Self {
id: InlayId::DebuggerValue(id),
position,
content: InlayContent::Text(text),
content: InlayContent::Text(text.into()),
}
}
@ -108,7 +108,7 @@ impl Inlay {
static COLOR_TEXT: OnceLock<Rope> = OnceLock::new();
match &self.content {
InlayContent::Text(text) => text,
InlayContent::Color(_) => COLOR_TEXT.get_or_init(|| Rope::from_str_small("")),
InlayContent::Color(_) => COLOR_TEXT.get_or_init(|| Rope::from("")),
}
}

View file

@ -878,7 +878,6 @@ mod tests {
use gpui::{AppContext as _, font, px};
use language::Capability;
use project::{Project, project_settings::DiagnosticSeverity};
use rope::Rope;
use settings::SettingsStore;
use util::post_inc;
@ -1025,22 +1024,22 @@ mod tests {
Inlay::edit_prediction(
post_inc(&mut id),
buffer_snapshot.anchor_before(offset),
Rope::from_str_small("test"),
"test",
),
Inlay::edit_prediction(
post_inc(&mut id),
buffer_snapshot.anchor_after(offset),
Rope::from_str_small("test"),
"test",
),
Inlay::mock_hint(
post_inc(&mut id),
buffer_snapshot.anchor_before(offset),
Rope::from_str_small("test"),
"test",
),
Inlay::mock_hint(
post_inc(&mut id),
buffer_snapshot.anchor_after(offset),
Rope::from_str_small("test"),
"test",
),
]
})

View file

@ -193,7 +193,7 @@ impl Editor {
if let Some(language) = language {
for signature in &mut signature_help.signatures {
let text = Rope::from_str_small(signature.label.as_ref());
let text = Rope::from(signature.label.as_ref());
let highlights = language
.highlight_text(&text, 0..signature.label.len())
.into_iter()

View file

@ -1468,7 +1468,6 @@ impl ExtensionStore {
let extensions_dir = self.installed_dir.clone();
let index_path = self.index_path.clone();
let proxy = self.proxy.clone();
let executor = cx.background_executor().clone();
cx.background_spawn(async move {
let start_time = Instant::now();
let mut index = ExtensionIndex::default();
@ -1502,14 +1501,10 @@ impl ExtensionStore {
}
if let Ok(index_json) = serde_json::to_string_pretty(&index) {
fs.save(
&index_path,
&Rope::from_str(&index_json, &executor),
Default::default(),
)
.await
.context("failed to save extension index")
.log_err();
fs.save(&index_path, &index_json.as_str().into(), Default::default())
.await
.context("failed to save extension index")
.log_err();
}
log::info!("rebuilt extension index in {:?}", start_time.elapsed());
@ -1676,7 +1671,7 @@ impl ExtensionStore {
let manifest_toml = toml::to_string(&loaded_extension.manifest)?;
fs.save(
&tmp_dir.join(EXTENSION_TOML),
&Rope::from_str_small(&manifest_toml),
&Rope::from(manifest_toml),
language::LineEnding::Unix,
)
.await?;

View file

@ -170,10 +170,7 @@ impl CommitView {
ReplicaId::LOCAL,
cx.entity_id().as_non_zero_u64().into(),
LineEnding::default(),
Rope::from_str(
&format_commit(&commit, stash.is_some()),
cx.background_executor(),
),
format_commit(&commit, stash.is_some()).into(),
);
metadata_buffer_id = Some(buffer.remote_id());
Buffer::build(buffer, Some(file.clone()), Capability::ReadWrite)
@ -339,7 +336,7 @@ async fn build_buffer(
) -> Result<Entity<Buffer>> {
let line_ending = LineEnding::detect(&text);
LineEnding::normalize(&mut text);
let text = Rope::from_str(&text, cx.background_executor());
let text = Rope::from(text);
let language = cx.update(|cx| language_registry.language_for_file(&blob, Some(&text), cx))?;
let language = if let Some(language) = language {
language_registry
@ -379,7 +376,7 @@ async fn build_buffer_diff(
let base_buffer = cx
.update(|cx| {
Buffer::build_snapshot(
Rope::from_str(old_text.as_deref().unwrap_or(""), cx.background_executor()),
old_text.as_deref().unwrap_or("").into(),
buffer.language().cloned(),
Some(language_registry.clone()),
cx,

View file

@ -359,7 +359,6 @@ mod tests {
use super::*;
use editor::test::editor_test_context::assert_state_with_diff;
use gpui::TestAppContext;
use language::Rope;
use project::{FakeFs, Fs, Project};
use settings::SettingsStore;
use std::path::PathBuf;
@ -430,7 +429,7 @@ mod tests {
// Modify the new file on disk
fs.save(
path!("/test/new_file.txt").as_ref(),
&Rope::from_str_small(&unindent(
&unindent(
"
new line 1
line 2
@ -438,7 +437,8 @@ mod tests {
line 4
new line 5
",
)),
)
.into(),
Default::default(),
)
.await
@ -465,14 +465,15 @@ mod tests {
// Modify the old file on disk
fs.save(
path!("/test/old_file.txt").as_ref(),
&Rope::from_str_small(&unindent(
&unindent(
"
new line 1
line 2
old line 3
line 4
",
)),
)
.into(),
Default::default(),
)
.await

View file

@ -260,19 +260,6 @@ impl AsyncApp {
}
}
impl sum_tree::BackgroundSpawn for BackgroundExecutor {
type Task<R>
= Task<R>
where
R: Send + Sync;
fn background_spawn<R>(&self, future: impl Future<Output = R> + Send + 'static) -> Self::Task<R>
where
R: Send + Sync + 'static,
{
self.spawn(future)
}
}
/// A cloneable, owned handle to the application context,
/// composed with the window associated with the current task.
#[derive(Clone, Deref, DerefMut)]

View file

@ -393,11 +393,6 @@ impl TestAppContext {
}
}
/// Returns the background executor for this context.
pub fn background_executor(&self) -> &BackgroundExecutor {
&self.background_executor
}
/// Wait until there are no more pending tasks.
pub fn run_until_parked(&mut self) {
self.background_executor.run_until_parked()

View file

@ -342,7 +342,7 @@ impl BackgroundExecutor {
/// for all of them to complete before returning.
pub async fn scoped<'scope, F>(&self, scheduler: F)
where
F: for<'a> FnOnce(&'a mut Scope<'scope>),
F: FnOnce(&mut Scope<'scope>),
{
let mut scope = Scope::new(self.clone());
(scheduler)(&mut scope);

View file

@ -22,7 +22,7 @@ use gpui::{
ScrollWheelEvent, Stateful, StyledText, Subscription, Task, TextStyleRefinement, WeakEntity,
actions, anchored, deferred, div,
};
use language::{Language, LanguageConfig, Rope, ToOffset as _};
use language::{Language, LanguageConfig, ToOffset as _};
use notifications::status_toast::{StatusToast, ToastIcon};
use project::{CompletionDisplayOptions, Project};
use settings::{
@ -2119,7 +2119,7 @@ impl RenderOnce for SyntaxHighlightedText {
let highlights = self
.language
.highlight_text(&Rope::from_str_small(text.as_ref()), 0..text.len());
.highlight_text(&text.as_ref().into(), 0..text.len());
let mut runs = Vec::with_capacity(highlights.len());
let mut offset = 0;

View file

@ -24,8 +24,8 @@ use collections::HashMap;
use fs::MTime;
use futures::channel::oneshot;
use gpui::{
App, AppContext as _, BackgroundExecutor, Context, Entity, EventEmitter, HighlightStyle,
SharedString, StyledText, Task, TaskLabel, TextStyle,
App, AppContext as _, Context, Entity, EventEmitter, HighlightStyle, SharedString, StyledText,
Task, TaskLabel, TextStyle,
};
use lsp::{LanguageServerId, NumberOrString};
@ -832,7 +832,6 @@ impl Buffer {
ReplicaId::LOCAL,
cx.entity_id().as_non_zero_u64().into(),
base_text.into(),
&cx.background_executor(),
),
None,
Capability::ReadWrite,
@ -863,10 +862,9 @@ impl Buffer {
replica_id: ReplicaId,
capability: Capability,
base_text: impl Into<String>,
cx: &BackgroundExecutor,
) -> Self {
Self::build(
TextBuffer::new(replica_id, remote_id, base_text.into(), cx),
TextBuffer::new(replica_id, remote_id, base_text.into()),
None,
capability,
)
@ -879,10 +877,9 @@ impl Buffer {
capability: Capability,
message: proto::BufferState,
file: Option<Arc<dyn File>>,
cx: &BackgroundExecutor,
) -> Result<Self> {
let buffer_id = BufferId::new(message.id).context("Could not deserialize buffer_id")?;
let buffer = TextBuffer::new(replica_id, buffer_id, message.base_text, cx);
let buffer = TextBuffer::new(replica_id, buffer_id, message.base_text);
let mut this = Self::build(buffer, file, capability);
this.text.set_line_ending(proto::deserialize_line_ending(
rpc::proto::LineEnding::from_i32(message.line_ending).context("missing line_ending")?,
@ -1141,14 +1138,13 @@ impl Buffer {
let old_snapshot = self.text.snapshot();
let mut branch_buffer = self.text.branch();
let mut syntax_snapshot = self.syntax_map.lock().snapshot();
let executor = cx.background_executor().clone();
cx.background_spawn(async move {
if !edits.is_empty() {
if let Some(language) = language.clone() {
syntax_snapshot.reparse(&old_snapshot, registry.clone(), language);
}
branch_buffer.edit(edits.iter().cloned(), &executor);
branch_buffer.edit(edits.iter().cloned());
let snapshot = branch_buffer.snapshot();
syntax_snapshot.interpolate(&snapshot);
@ -2365,9 +2361,7 @@ impl Buffer {
let autoindent_request = autoindent_mode
.and_then(|mode| self.language.as_ref().map(|_| (self.snapshot(), mode)));
let edit_operation = self
.text
.edit(edits.iter().cloned(), cx.background_executor());
let edit_operation = self.text.edit(edits.iter().cloned());
let edit_id = edit_operation.timestamp();
if let Some((before_edit, mode)) = autoindent_request {
@ -2598,8 +2592,7 @@ impl Buffer {
for operation in buffer_ops.iter() {
self.send_operation(Operation::Buffer(operation.clone()), false, cx);
}
self.text
.apply_ops(buffer_ops, Some(cx.background_executor()));
self.text.apply_ops(buffer_ops);
self.deferred_ops.insert(deferred_ops);
self.flush_deferred_ops(cx);
self.did_edit(&old_version, was_dirty, cx);

View file

@ -75,7 +75,6 @@ fn test_set_line_ending(cx: &mut TestAppContext) {
Capability::ReadWrite,
base.read(cx).to_proto(cx),
None,
cx.background_executor(),
)
.unwrap()
});
@ -256,18 +255,14 @@ async fn test_first_line_pattern(cx: &mut TestAppContext) {
.is_none()
);
assert!(
cx.read(|cx| languages.language_for_file(
&file("the/script"),
Some(&Rope::from_str("nothing", cx.background_executor())),
cx
))
.is_none()
cx.read(|cx| languages.language_for_file(&file("the/script"), Some(&"nothing".into()), cx))
.is_none()
);
assert_eq!(
cx.read(|cx| languages.language_for_file(
&file("the/script"),
Some(&Rope::from_str("#!/bin/env node", cx.background_executor())),
Some(&"#!/bin/env node".into()),
cx
))
.unwrap()
@ -411,7 +406,6 @@ fn test_edit_events(cx: &mut gpui::App) {
ReplicaId::new(1),
Capability::ReadWrite,
"abcdef",
cx.background_executor(),
)
});
let buffer1_ops = Arc::new(Mutex::new(Vec::new()));
@ -2787,14 +2781,8 @@ fn test_serialization(cx: &mut gpui::App) {
.background_executor()
.block(buffer1.read(cx).serialize_ops(None, cx));
let buffer2 = cx.new(|cx| {
let mut buffer = Buffer::from_proto(
ReplicaId::new(1),
Capability::ReadWrite,
state,
None,
cx.background_executor(),
)
.unwrap();
let mut buffer =
Buffer::from_proto(ReplicaId::new(1), Capability::ReadWrite, state, None).unwrap();
buffer.apply_ops(
ops.into_iter()
.map(|op| proto::deserialize_operation(op).unwrap()),
@ -2818,7 +2806,6 @@ fn test_branch_and_merge(cx: &mut TestAppContext) {
Capability::ReadWrite,
base.read(cx).to_proto(cx),
None,
cx.background_executor(),
)
.unwrap()
});
@ -3133,14 +3120,9 @@ fn test_random_collaboration(cx: &mut App, mut rng: StdRng) {
let ops = cx
.background_executor()
.block(base_buffer.read(cx).serialize_ops(None, cx));
let mut buffer = Buffer::from_proto(
ReplicaId::new(i as u16),
Capability::ReadWrite,
state,
None,
cx.background_executor(),
)
.unwrap();
let mut buffer =
Buffer::from_proto(ReplicaId::new(i as u16), Capability::ReadWrite, state, None)
.unwrap();
buffer.apply_ops(
ops.into_iter()
.map(|op| proto::deserialize_operation(op).unwrap()),
@ -3269,7 +3251,6 @@ fn test_random_collaboration(cx: &mut App, mut rng: StdRng) {
Capability::ReadWrite,
old_buffer_state,
None,
cx.background_executor(),
)
.unwrap();
new_buffer.apply_ops(
@ -3433,7 +3414,7 @@ fn test_contiguous_ranges() {
}
#[gpui::test(iterations = 500)]
fn test_trailing_whitespace_ranges(mut rng: StdRng, cx: &mut TestAppContext) {
fn test_trailing_whitespace_ranges(mut rng: StdRng) {
// Generate a random multi-line string containing
// some lines with trailing whitespace.
let mut text = String::new();
@ -3457,7 +3438,7 @@ fn test_trailing_whitespace_ranges(mut rng: StdRng, cx: &mut TestAppContext) {
_ => {}
}
let rope = Rope::from_str(text.as_str(), cx.background_executor());
let rope = Rope::from(text.as_str());
let actual_ranges = trailing_whitespace_ranges(&rope);
let expected_ranges = TRAILING_WHITESPACE_REGEX
.find_iter(&text)

View file

@ -100,7 +100,6 @@ fn test_syntax_map_layers_for_range(cx: &mut App) {
}
"#
.unindent(),
cx.background_executor(),
);
let mut syntax_map = SyntaxMap::new(&buffer);
@ -148,7 +147,7 @@ fn test_syntax_map_layers_for_range(cx: &mut App) {
// Replace a vec! macro invocation with a plain slice, removing a syntactic layer.
let macro_name_range = range_for_text(&buffer, "vec!");
buffer.edit([(macro_name_range, "&")], cx.background_executor());
buffer.edit([(macro_name_range, "&")]);
syntax_map.interpolate(&buffer);
syntax_map.reparse(language.clone(), &buffer);
@ -200,7 +199,6 @@ fn test_dynamic_language_injection(cx: &mut App) {
```
"#
.unindent(),
cx.background_executor(),
);
let mut syntax_map = SyntaxMap::new(&buffer);
@ -220,10 +218,7 @@ fn test_dynamic_language_injection(cx: &mut App) {
// Replace `rs` with a path to ending in `.rb` in code block.
let macro_name_range = range_for_text(&buffer, "rs");
buffer.edit(
[(macro_name_range, "foo/bar/baz.rb")],
cx.background_executor(),
);
buffer.edit([(macro_name_range, "foo/bar/baz.rb")]);
syntax_map.interpolate(&buffer);
syntax_map.reparse(markdown.clone(), &buffer);
syntax_map.reparse(markdown_inline.clone(), &buffer);
@ -240,7 +235,7 @@ fn test_dynamic_language_injection(cx: &mut App) {
// Replace Ruby with a language that hasn't been loaded yet.
let macro_name_range = range_for_text(&buffer, "foo/bar/baz.rb");
buffer.edit([(macro_name_range, "html")], cx.background_executor());
buffer.edit([(macro_name_range, "html")]);
syntax_map.interpolate(&buffer);
syntax_map.reparse(markdown.clone(), &buffer);
syntax_map.reparse(markdown_inline.clone(), &buffer);
@ -816,12 +811,7 @@ fn test_syntax_map_languages_loading_with_erb(cx: &mut App) {
.unindent();
let registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
let mut buffer = Buffer::new(
ReplicaId::LOCAL,
BufferId::new(1).unwrap(),
text,
cx.background_executor(),
);
let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), text);
let mut syntax_map = SyntaxMap::new(&buffer);
syntax_map.set_language_registry(registry.clone());
@ -869,7 +859,7 @@ fn test_syntax_map_languages_loading_with_erb(cx: &mut App) {
.unindent();
log::info!("editing");
buffer.edit_via_marked_text(&text, cx.background_executor());
buffer.edit_via_marked_text(&text);
syntax_map.interpolate(&buffer);
syntax_map.reparse(language, &buffer);
@ -913,7 +903,7 @@ fn test_random_syntax_map_edits_rust_macros(rng: StdRng, cx: &mut App) {
let language = Arc::new(rust_lang());
registry.add(language.clone());
test_random_edits(text, registry, language, rng, cx);
test_random_edits(text, registry, language, rng);
}
#[gpui::test(iterations = 50)]
@ -942,7 +932,7 @@ fn test_random_syntax_map_edits_with_erb(rng: StdRng, cx: &mut App) {
registry.add(Arc::new(ruby_lang()));
registry.add(Arc::new(html_lang()));
test_random_edits(text, registry, language, rng, cx);
test_random_edits(text, registry, language, rng);
}
#[gpui::test(iterations = 50)]
@ -975,7 +965,7 @@ fn test_random_syntax_map_edits_with_heex(rng: StdRng, cx: &mut App) {
registry.add(Arc::new(heex_lang()));
registry.add(Arc::new(html_lang()));
test_random_edits(text, registry, language, rng, cx);
test_random_edits(text, registry, language, rng);
}
fn test_random_edits(
@ -983,18 +973,12 @@ fn test_random_edits(
registry: Arc<LanguageRegistry>,
language: Arc<Language>,
mut rng: StdRng,
cx: &mut App,
) {
let operations = env::var("OPERATIONS")
.map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
.unwrap_or(10);
let mut buffer = Buffer::new(
ReplicaId::LOCAL,
BufferId::new(1).unwrap(),
text,
cx.background_executor(),
);
let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), text);
let mut syntax_map = SyntaxMap::new(&buffer);
syntax_map.set_language_registry(registry.clone());
@ -1009,7 +993,7 @@ fn test_random_edits(
let prev_buffer = buffer.snapshot();
let prev_syntax_map = syntax_map.snapshot();
buffer.randomly_edit(&mut rng, 3, cx.background_executor());
buffer.randomly_edit(&mut rng, 3);
log::info!("text:\n{}", buffer.text());
syntax_map.interpolate(&buffer);
@ -1175,12 +1159,7 @@ fn test_edit_sequence(language_name: &str, steps: &[&str], cx: &mut App) -> (Buf
.now_or_never()
.unwrap()
.unwrap();
let mut buffer = Buffer::new(
ReplicaId::LOCAL,
BufferId::new(1).unwrap(),
"",
cx.background_executor(),
);
let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "");
let mut mutated_syntax_map = SyntaxMap::new(&buffer);
mutated_syntax_map.set_language_registry(registry.clone());
@ -1189,7 +1168,7 @@ fn test_edit_sequence(language_name: &str, steps: &[&str], cx: &mut App) -> (Buf
for (i, marked_string) in steps.iter().enumerate() {
let marked_string = marked_string.unindent();
log::info!("incremental parse {i}: {marked_string:?}");
buffer.edit_via_marked_text(&marked_string, cx.background_executor());
buffer.edit_via_marked_text(&marked_string);
// Reparse the syntax map
mutated_syntax_map.interpolate(&buffer);

View file

@ -11,7 +11,7 @@ use futures::{Future, FutureExt, future::join_all};
use gpui::{App, AppContext, AsyncApp, Task};
use language::{
BinaryStatus, CodeLabel, DynLspInstaller, HighlightId, Language, LanguageName, LspAdapter,
LspAdapterDelegate, Rope, Toolchain,
LspAdapterDelegate, Toolchain,
};
use lsp::{
CodeActionKind, LanguageServerBinary, LanguageServerBinaryOptions, LanguageServerName,
@ -403,10 +403,7 @@ fn labels_from_extension(
let runs = if label.code.is_empty() {
Vec::new()
} else {
language.highlight_text(
&Rope::from_str_small(label.code.as_str()),
0..label.code.len(),
)
language.highlight_text(&label.code.as_str().into(), 0..label.code.len())
};
build_code_label(&label, &runs, language)
})

View file

@ -189,7 +189,7 @@ impl super::LspAdapter for CLspAdapter {
Some(lsp::CompletionItemKind::FIELD) if completion.detail.is_some() => {
let detail = completion.detail.as_ref().unwrap();
let text = format!("{} {}", detail, label);
let source = Rope::from_str_small(format!("struct S {{ {} }}", text).as_str());
let source = Rope::from(format!("struct S {{ {} }}", text).as_str());
let runs = language.highlight_text(&source, 11..11 + text.len());
let filter_range = completion
.filter_text
@ -206,8 +206,7 @@ impl super::LspAdapter for CLspAdapter {
{
let detail = completion.detail.as_ref().unwrap();
let text = format!("{} {}", detail, label);
let runs =
language.highlight_text(&Rope::from_str_small(text.as_str()), 0..text.len());
let runs = language.highlight_text(&Rope::from(text.as_str()), 0..text.len());
let filter_range = completion
.filter_text
.as_deref()
@ -223,8 +222,7 @@ impl super::LspAdapter for CLspAdapter {
{
let detail = completion.detail.as_ref().unwrap();
let text = format!("{} {}", detail, label);
let runs =
language.highlight_text(&Rope::from_str_small(text.as_str()), 0..text.len());
let runs = language.highlight_text(&Rope::from(text.as_str()), 0..text.len());
let filter_range = completion
.filter_text
.as_deref()
@ -328,7 +326,7 @@ impl super::LspAdapter for CLspAdapter {
Some(CodeLabel::new(
text[display_range.clone()].to_string(),
filter_range,
language.highlight_text(&Rope::from_str_small(text.as_str()), display_range),
language.highlight_text(&text.as_str().into(), display_range),
))
}

View file

@ -221,7 +221,7 @@ impl LspAdapter for GoLspAdapter {
match completion.kind.zip(completion.detail.as_ref()) {
Some((lsp::CompletionItemKind::MODULE, detail)) => {
let text = format!("{label} {detail}");
let source = Rope::from_str_small(format!("import {text}").as_str());
let source = Rope::from(format!("import {text}").as_str());
let runs = language.highlight_text(&source, 7..7 + text[name_offset..].len());
let filter_range = completion
.filter_text
@ -238,9 +238,8 @@ impl LspAdapter for GoLspAdapter {
detail,
)) => {
let text = format!("{label} {detail}");
let source = Rope::from_str_small(
format!("var {} {}", &text[name_offset..], detail).as_str(),
);
let source =
Rope::from(format!("var {} {}", &text[name_offset..], detail).as_str());
let runs = adjust_runs(
name_offset,
language.highlight_text(&source, 4..4 + text[name_offset..].len()),
@ -257,8 +256,7 @@ impl LspAdapter for GoLspAdapter {
}
Some((lsp::CompletionItemKind::STRUCT, _)) => {
let text = format!("{label} struct {{}}");
let source =
Rope::from_str_small(format!("type {}", &text[name_offset..]).as_str());
let source = Rope::from(format!("type {}", &text[name_offset..]).as_str());
let runs = adjust_runs(
name_offset,
language.highlight_text(&source, 5..5 + text[name_offset..].len()),
@ -275,8 +273,7 @@ impl LspAdapter for GoLspAdapter {
}
Some((lsp::CompletionItemKind::INTERFACE, _)) => {
let text = format!("{label} interface {{}}");
let source =
Rope::from_str_small(format!("type {}", &text[name_offset..]).as_str());
let source = Rope::from(format!("type {}", &text[name_offset..]).as_str());
let runs = adjust_runs(
name_offset,
language.highlight_text(&source, 5..5 + text[name_offset..].len()),
@ -293,9 +290,8 @@ impl LspAdapter for GoLspAdapter {
}
Some((lsp::CompletionItemKind::FIELD, detail)) => {
let text = format!("{label} {detail}");
let source = Rope::from_str_small(
format!("type T struct {{ {} }}", &text[name_offset..]).as_str(),
);
let source =
Rope::from(format!("type T struct {{ {} }}", &text[name_offset..]).as_str());
let runs = adjust_runs(
name_offset,
language.highlight_text(&source, 16..16 + text[name_offset..].len()),
@ -313,9 +309,7 @@ impl LspAdapter for GoLspAdapter {
Some((lsp::CompletionItemKind::FUNCTION | lsp::CompletionItemKind::METHOD, detail)) => {
if let Some(signature) = detail.strip_prefix("func") {
let text = format!("{label}{signature}");
let source = Rope::from_str_small(
format!("func {} {{}}", &text[name_offset..]).as_str(),
);
let source = Rope::from(format!("func {} {{}}", &text[name_offset..]).as_str());
let runs = adjust_runs(
name_offset,
language.highlight_text(&source, 5..5 + text[name_offset..].len()),
@ -391,7 +385,7 @@ impl LspAdapter for GoLspAdapter {
Some(CodeLabel::new(
text[display_range.clone()].to_string(),
filter_range,
language.highlight_text(&Rope::from_str_small(text.as_str()), display_range),
language.highlight_text(&text.as_str().into(), display_range),
))
}

View file

@ -19,7 +19,6 @@ use pet_core::python_environment::{PythonEnvironment, PythonEnvironmentKind};
use pet_virtualenv::is_virtualenv_dir;
use project::Fs;
use project::lsp_store::language_server_settings;
use rope::Rope;
use serde::{Deserialize, Serialize};
use serde_json::{Value, json};
use smol::lock::OnceCell;
@ -467,7 +466,7 @@ impl LspAdapter for PyrightLspAdapter {
Some(language::CodeLabel::new(
text[display_range.clone()].to_string(),
filter_range,
language.highlight_text(&Rope::from_str_small(text.as_str()), display_range),
language.highlight_text(&text.as_str().into(), display_range),
))
}
@ -1512,7 +1511,7 @@ impl LspAdapter for PyLspAdapter {
Some(language::CodeLabel::new(
text[display_range.clone()].to_string(),
filter_range,
language.highlight_text(&Rope::from_str_small(text.as_str()), display_range),
language.highlight_text(&text.as_str().into(), display_range),
))
}
@ -1801,7 +1800,7 @@ impl LspAdapter for BasedPyrightLspAdapter {
Some(language::CodeLabel::new(
text[display_range.clone()].to_string(),
filter_range,
language.highlight_text(&Rope::from_str_small(text.as_str()), display_range),
language.highlight_text(&text.as_str().into(), display_range),
))
}

View file

@ -252,7 +252,7 @@ impl LspAdapter for RustLspAdapter {
let name = &completion.label;
let text = format!("{name}: {signature}");
let prefix = "struct S { ";
let source = Rope::from_iter_small([prefix, &text, " }"]);
let source = Rope::from_iter([prefix, &text, " }"]);
let runs =
language.highlight_text(&source, prefix.len()..prefix.len() + text.len());
mk_label(text, &|| 0..completion.label.len(), runs)
@ -264,7 +264,7 @@ impl LspAdapter for RustLspAdapter {
let name = &completion.label;
let text = format!("{name}: {signature}",);
let prefix = "let ";
let source = Rope::from_iter_small([prefix, &text, " = ();"]);
let source = Rope::from_iter([prefix, &text, " = ();"]);
let runs =
language.highlight_text(&source, prefix.len()..prefix.len() + text.len());
mk_label(text, &|| 0..completion.label.len(), runs)
@ -302,7 +302,7 @@ impl LspAdapter for RustLspAdapter {
.filter(|it| it.contains(&label))
.and_then(|it| Some((it, FULL_SIGNATURE_REGEX.find(it)?)))
{
let source = Rope::from_str_small(function_signature);
let source = Rope::from(function_signature);
let runs = language.highlight_text(&source, 0..function_signature.len());
mk_label(
function_signature.to_owned(),
@ -311,7 +311,7 @@ impl LspAdapter for RustLspAdapter {
)
} else if let Some((prefix, suffix)) = fn_prefixed {
let text = format!("{label}{suffix}");
let source = Rope::from_iter_small([prefix, " ", &text, " {}"]);
let source = Rope::from_iter([prefix, " ", &text, " {}"]);
let run_start = prefix.len() + 1;
let runs = language.highlight_text(&source, run_start..run_start + text.len());
mk_label(text, &|| 0..label.len(), runs)
@ -322,7 +322,7 @@ impl LspAdapter for RustLspAdapter {
{
let text = completion.label.clone();
let len = text.len();
let source = Rope::from_str_small(text.as_str());
let source = Rope::from(text.as_str());
let runs = language.highlight_text(&source, 0..len);
mk_label(text, &|| 0..completion.label.len(), runs)
} else if detail_left.is_none() {
@ -399,10 +399,7 @@ impl LspAdapter for RustLspAdapter {
Some(CodeLabel::new(
format!("{prefix}{name}"),
filter_range,
language.highlight_text(
&Rope::from_iter_small([prefix, name, suffix]),
display_range,
),
language.highlight_text(&Rope::from_iter([prefix, name, suffix]), display_range),
))
}

View file

@ -1558,9 +1558,7 @@ impl MarkdownElementBuilder {
if let Some(Some(language)) = self.code_block_stack.last() {
let mut offset = 0;
for (range, highlight_id) in
language.highlight_text(&Rope::from_str_small(text), 0..text.len())
{
for (range, highlight_id) in language.highlight_text(&Rope::from(text), 0..text.len()) {
if range.start > offset {
self.pending_line
.runs

View file

@ -779,7 +779,7 @@ impl<'a> MarkdownParser<'a> {
let highlights = if let Some(language) = &language {
if let Some(registry) = &self.language_registry {
let rope = language::Rope::from_str_small(code.as_str());
let rope: language::Rope = code.as_str().into();
registry
.language_for_name_or_extension(language)
.await

View file

@ -1,6 +1,6 @@
use super::*;
use buffer_diff::{DiffHunkStatus, DiffHunkStatusKind};
use gpui::{App, BackgroundExecutor, TestAppContext};
use gpui::{App, TestAppContext};
use indoc::indoc;
use language::{Buffer, Rope};
use parking_lot::RwLock;
@ -79,14 +79,9 @@ fn test_remote(cx: &mut App) {
let ops = cx
.background_executor()
.block(host_buffer.read(cx).serialize_ops(None, cx));
let mut buffer = Buffer::from_proto(
ReplicaId::REMOTE_SERVER,
Capability::ReadWrite,
state,
None,
cx.background_executor(),
)
.unwrap();
let mut buffer =
Buffer::from_proto(ReplicaId::REMOTE_SERVER, Capability::ReadWrite, state, None)
.unwrap();
buffer.apply_ops(
ops.into_iter()
.map(|op| language::proto::deserialize_operation(op).unwrap()),
@ -1229,7 +1224,7 @@ fn test_basic_diff_hunks(cx: &mut TestAppContext) {
assert_chunks_in_ranges(&snapshot);
assert_consistent_line_numbers(&snapshot);
assert_position_translation(&snapshot);
assert_line_indents(&snapshot, cx.background_executor());
assert_line_indents(&snapshot);
multibuffer.update(cx, |multibuffer, cx| {
multibuffer.collapse_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
@ -1253,7 +1248,7 @@ fn test_basic_diff_hunks(cx: &mut TestAppContext) {
assert_chunks_in_ranges(&snapshot);
assert_consistent_line_numbers(&snapshot);
assert_position_translation(&snapshot);
assert_line_indents(&snapshot, cx.background_executor());
assert_line_indents(&snapshot);
// Expand the first diff hunk
multibuffer.update(cx, |multibuffer, cx| {
@ -1305,7 +1300,7 @@ fn test_basic_diff_hunks(cx: &mut TestAppContext) {
assert_chunks_in_ranges(&snapshot);
assert_consistent_line_numbers(&snapshot);
assert_position_translation(&snapshot);
assert_line_indents(&snapshot, cx.background_executor());
assert_line_indents(&snapshot);
// Edit the buffer before the first hunk
buffer.update(cx, |buffer, cx| {
@ -1347,7 +1342,7 @@ fn test_basic_diff_hunks(cx: &mut TestAppContext) {
assert_chunks_in_ranges(&snapshot);
assert_consistent_line_numbers(&snapshot);
assert_position_translation(&snapshot);
assert_line_indents(&snapshot, cx.background_executor());
assert_line_indents(&snapshot);
// Recalculate the diff, changing the first diff hunk.
diff.update(cx, |diff, cx| {
@ -2072,7 +2067,7 @@ fn test_diff_hunks_with_multiple_excerpts(cx: &mut TestAppContext) {
}
assert_position_translation(&snapshot);
assert_line_indents(&snapshot, cx.background_executor());
assert_line_indents(&snapshot);
assert_eq!(
snapshot
@ -2123,7 +2118,7 @@ fn test_diff_hunks_with_multiple_excerpts(cx: &mut TestAppContext) {
),
);
assert_line_indents(&snapshot, cx.background_executor());
assert_line_indents(&snapshot);
}
/// A naive implementation of a multi-buffer that does not maintain
@ -2893,7 +2888,7 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) {
);
}
let text_rope = Rope::from_str(expected_text.as_str(), cx.background_executor());
let text_rope = Rope::from(expected_text.as_str());
for _ in 0..10 {
let end_ix = text_rope.clip_offset(rng.random_range(0..=text_rope.len()), Bias::Right);
let start_ix = text_rope.clip_offset(rng.random_range(0..=end_ix), Bias::Left);
@ -3517,7 +3512,7 @@ fn assert_consistent_line_numbers(snapshot: &MultiBufferSnapshot) {
#[track_caller]
fn assert_position_translation(snapshot: &MultiBufferSnapshot) {
let text = Rope::from_str_small(&snapshot.text());
let text = Rope::from(snapshot.text());
let mut left_anchors = Vec::new();
let mut right_anchors = Vec::new();
@ -3641,10 +3636,10 @@ fn assert_position_translation(snapshot: &MultiBufferSnapshot) {
}
}
fn assert_line_indents(snapshot: &MultiBufferSnapshot, executor: &BackgroundExecutor) {
fn assert_line_indents(snapshot: &MultiBufferSnapshot) {
let max_row = snapshot.max_point().row;
let buffer_id = snapshot.excerpts().next().unwrap().1.remote_id();
let text = text::Buffer::new(ReplicaId::LOCAL, buffer_id, snapshot.text(), executor);
let text = text::Buffer::new(ReplicaId::LOCAL, buffer_id, snapshot.text());
let mut line_indents = text
.line_indents_in_row_range(0..max_row + 1)
.collect::<Vec<_>>();

View file

@ -180,13 +180,7 @@ impl RemoteBufferStore {
buffer_file = Some(Arc::new(File::from_proto(file, worktree, cx)?)
as Arc<dyn language::File>);
}
Buffer::from_proto(
replica_id,
capability,
state,
buffer_file,
cx.background_executor(),
)
Buffer::from_proto(replica_id, capability, state, buffer_file)
});
match buffer_result {
@ -634,10 +628,9 @@ impl LocalBufferStore {
Ok(loaded) => {
let reservation = cx.reserve_entity::<Buffer>()?;
let buffer_id = BufferId::from(reservation.entity_id().as_non_zero_u64());
let executor = cx.background_executor().clone();
let text_buffer = cx
.background_spawn(async move {
text::Buffer::new(ReplicaId::LOCAL, buffer_id, loaded.text, &executor)
text::Buffer::new(ReplicaId::LOCAL, buffer_id, loaded.text)
})
.await;
cx.insert_entity(reservation, |_| {
@ -646,12 +639,7 @@ impl LocalBufferStore {
}
Err(error) if is_not_found_error(&error) => cx.new(|cx| {
let buffer_id = BufferId::from(cx.entity_id().as_non_zero_u64());
let text_buffer = text::Buffer::new(
ReplicaId::LOCAL,
buffer_id,
"",
cx.background_executor(),
);
let text_buffer = text::Buffer::new(ReplicaId::LOCAL, buffer_id, "");
Buffer::build(
text_buffer,
Some(Arc::new(File {

View file

@ -276,8 +276,8 @@ mod tests {
use util::{path, rel_path::rel_path};
use worktree::WorktreeSettings;
#[gpui::test]
fn test_parse_conflicts_in_buffer(cx: &mut TestAppContext) {
#[test]
fn test_parse_conflicts_in_buffer() {
// Create a buffer with conflict markers
let test_content = r#"
This is some text before the conflict.
@ -299,12 +299,7 @@ mod tests {
.unindent();
let buffer_id = BufferId::new(1).unwrap();
let buffer = Buffer::new(
ReplicaId::LOCAL,
buffer_id,
test_content,
cx.background_executor(),
);
let buffer = Buffer::new(ReplicaId::LOCAL, buffer_id, test_content);
let snapshot = buffer.snapshot();
let conflict_snapshot = ConflictSet::parse(&snapshot);
@ -360,8 +355,8 @@ mod tests {
assert_eq!(conflicts_in_range.len(), 0);
}
#[gpui::test]
fn test_nested_conflict_markers(cx: &mut TestAppContext) {
#[test]
fn test_nested_conflict_markers() {
// Create a buffer with nested conflict markers
let test_content = r#"
This is some text before the conflict.
@ -379,12 +374,7 @@ mod tests {
.unindent();
let buffer_id = BufferId::new(1).unwrap();
let buffer = Buffer::new(
ReplicaId::LOCAL,
buffer_id,
test_content,
cx.background_executor(),
);
let buffer = Buffer::new(ReplicaId::LOCAL, buffer_id, test_content);
let snapshot = buffer.snapshot();
let conflict_snapshot = ConflictSet::parse(&snapshot);
@ -406,8 +396,8 @@ mod tests {
assert_eq!(their_text, "This is their version in a nested conflict\n");
}
#[gpui::test]
fn test_conflict_markers_at_eof(cx: &mut TestAppContext) {
#[test]
fn test_conflict_markers_at_eof() {
let test_content = r#"
<<<<<<< ours
=======
@ -415,20 +405,15 @@ mod tests {
>>>>>>> "#
.unindent();
let buffer_id = BufferId::new(1).unwrap();
let buffer = Buffer::new(
ReplicaId::LOCAL,
buffer_id,
test_content,
cx.background_executor(),
);
let buffer = Buffer::new(ReplicaId::LOCAL, buffer_id, test_content);
let snapshot = buffer.snapshot();
let conflict_snapshot = ConflictSet::parse(&snapshot);
assert_eq!(conflict_snapshot.conflicts.len(), 1);
}
#[gpui::test]
fn test_conflicts_in_range(cx: &mut TestAppContext) {
#[test]
fn test_conflicts_in_range() {
// Create a buffer with conflict markers
let test_content = r#"
one
@ -462,12 +447,7 @@ mod tests {
.unindent();
let buffer_id = BufferId::new(1).unwrap();
let buffer = Buffer::new(
ReplicaId::LOCAL,
buffer_id,
test_content.clone(),
cx.background_executor(),
);
let buffer = Buffer::new(ReplicaId::LOCAL, buffer_id, test_content.clone());
let snapshot = buffer.snapshot();
let conflict_snapshot = ConflictSet::parse(&snapshot);

View file

@ -13,9 +13,7 @@ use futures::{
future::{self, Shared},
stream::FuturesUnordered,
};
use gpui::{
AppContext as _, AsyncApp, BackgroundExecutor, Context, Entity, EventEmitter, Task, WeakEntity,
};
use gpui::{AppContext as _, AsyncApp, Context, Entity, EventEmitter, Task, WeakEntity};
use language::{
Buffer, LanguageRegistry, LocalFile,
language_settings::{Formatter, LanguageSettings},
@ -560,137 +558,99 @@ impl PrettierStore {
let plugins_to_install = new_plugins.clone();
let fs = Arc::clone(&self.fs);
let new_installation_task = cx
.spawn(async move |prettier_store, cx| {
cx.background_executor()
.timer(Duration::from_millis(30))
.await;
.spawn(async move |prettier_store, cx| {
cx.background_executor().timer(Duration::from_millis(30)).await;
let location_data = prettier_store.update(cx, |prettier_store, cx| {
worktree
.and_then(|worktree_id| {
prettier_store
.worktree_store
.read(cx)
.worktree_for_id(worktree_id, cx)
.map(|worktree| worktree.read(cx).abs_path())
})
.map(|locate_from| {
let installed_prettiers =
prettier_store.prettier_instances.keys().cloned().collect();
(locate_from, installed_prettiers)
})
worktree.and_then(|worktree_id| {
prettier_store.worktree_store
.read(cx)
.worktree_for_id(worktree_id, cx)
.map(|worktree| worktree.read(cx).abs_path())
}).map(|locate_from| {
let installed_prettiers = prettier_store.prettier_instances.keys().cloned().collect();
(locate_from, installed_prettiers)
})
})?;
let locate_prettier_installation = match location_data {
Some((locate_from, installed_prettiers)) => {
Prettier::locate_prettier_installation(
fs.as_ref(),
&installed_prettiers,
locate_from.as_ref(),
)
.await
.context("locate prettier installation")
.map_err(Arc::new)?
}
Some((locate_from, installed_prettiers)) => Prettier::locate_prettier_installation(
fs.as_ref(),
&installed_prettiers,
locate_from.as_ref(),
)
.await
.context("locate prettier installation").map_err(Arc::new)?,
None => ControlFlow::Continue(None),
};
match locate_prettier_installation {
match locate_prettier_installation
{
ControlFlow::Break(()) => return Ok(()),
ControlFlow::Continue(prettier_path) => {
if prettier_path.is_some() {
new_plugins.clear();
}
let mut needs_install =
should_write_prettier_server_file(fs.as_ref()).await;
let mut needs_install = should_write_prettier_server_file(fs.as_ref()).await;
if let Some(previous_installation_task) = previous_installation_task
&& let Err(e) = previous_installation_task.await
{
log::error!("Failed to install default prettier: {e:#}");
prettier_store.update(cx, |prettier_store, _| {
if let PrettierInstallation::NotInstalled {
attempts,
not_installed_plugins,
..
} = &mut prettier_store.default_prettier.prettier
{
*attempts += 1;
new_plugins.extend(not_installed_plugins.iter().cloned());
installation_attempt = *attempts;
needs_install = true;
};
})?;
};
&& let Err(e) = previous_installation_task.await {
log::error!("Failed to install default prettier: {e:#}");
prettier_store.update(cx, |prettier_store, _| {
if let PrettierInstallation::NotInstalled { attempts, not_installed_plugins, .. } = &mut prettier_store.default_prettier.prettier {
*attempts += 1;
new_plugins.extend(not_installed_plugins.iter().cloned());
installation_attempt = *attempts;
needs_install = true;
};
})?;
};
if installation_attempt > prettier::FAIL_THRESHOLD {
prettier_store.update(cx, |prettier_store, _| {
if let PrettierInstallation::NotInstalled {
installation_task,
..
} = &mut prettier_store.default_prettier.prettier
{
if let PrettierInstallation::NotInstalled { installation_task, .. } = &mut prettier_store.default_prettier.prettier {
*installation_task = None;
};
})?;
log::warn!(
"Default prettier installation had failed {installation_attempt} \
times, not attempting again",
"Default prettier installation had failed {installation_attempt} times, not attempting again",
);
return Ok(());
}
prettier_store.update(cx, |prettier_store, _| {
new_plugins.retain(|plugin| {
!prettier_store
.default_prettier
.installed_plugins
.contains(plugin)
!prettier_store.default_prettier.installed_plugins.contains(plugin)
});
if let PrettierInstallation::NotInstalled {
not_installed_plugins,
..
} = &mut prettier_store.default_prettier.prettier
{
if let PrettierInstallation::NotInstalled { not_installed_plugins, .. } = &mut prettier_store.default_prettier.prettier {
not_installed_plugins.retain(|plugin| {
!prettier_store
.default_prettier
.installed_plugins
.contains(plugin)
!prettier_store.default_prettier.installed_plugins.contains(plugin)
});
not_installed_plugins.extend(new_plugins.iter().cloned());
}
needs_install |= !new_plugins.is_empty();
})?;
if needs_install {
log::info!(
"Initializing default prettier with plugins {new_plugins:?}"
);
log::info!("Initializing default prettier with plugins {new_plugins:?}");
let installed_plugins = new_plugins.clone();
let executor = cx.background_executor().clone();
cx.background_spawn(async move {
install_prettier_packages(fs.as_ref(), new_plugins, node).await?;
// Save the server file last, so the reinstall need could be determined by the absence of the file.
save_prettier_server_file(fs.as_ref(), &executor).await?;
save_prettier_server_file(fs.as_ref()).await?;
anyhow::Ok(())
})
.await
.context("prettier & plugins install")
.map_err(Arc::new)?;
log::info!(
"Initialized default prettier with plugins: {installed_plugins:?}"
);
.await
.context("prettier & plugins install")
.map_err(Arc::new)?;
log::info!("Initialized default prettier with plugins: {installed_plugins:?}");
prettier_store.update(cx, |prettier_store, _| {
prettier_store.default_prettier.prettier =
PrettierInstallation::Installed(PrettierInstance {
attempt: 0,
prettier: None,
});
prettier_store
.default_prettier
prettier_store.default_prettier
.installed_plugins
.extend(installed_plugins);
})?;
} else {
prettier_store.update(cx, |prettier_store, _| {
if let PrettierInstallation::NotInstalled { .. } =
&mut prettier_store.default_prettier.prettier
{
if let PrettierInstallation::NotInstalled { .. } = &mut prettier_store.default_prettier.prettier {
prettier_store.default_prettier.prettier =
PrettierInstallation::Installed(PrettierInstance {
attempt: 0,
@ -976,14 +936,11 @@ async fn install_prettier_packages(
anyhow::Ok(())
}
async fn save_prettier_server_file(
fs: &dyn Fs,
executor: &BackgroundExecutor,
) -> anyhow::Result<()> {
async fn save_prettier_server_file(fs: &dyn Fs) -> anyhow::Result<()> {
let prettier_wrapper_path = default_prettier_dir().join(prettier::PRETTIER_SERVER_FILE);
fs.save(
&prettier_wrapper_path,
&text::Rope::from_str(prettier::PRETTIER_SERVER_JS, executor),
&text::Rope::from(prettier::PRETTIER_SERVER_JS),
text::LineEnding::Unix,
)
.await

View file

@ -714,10 +714,8 @@ pub enum ResolveState {
impl InlayHint {
pub fn text(&self) -> Rope {
match &self.label {
InlayHintLabel::String(s) => Rope::from_str_small(s),
InlayHintLabel::LabelParts(parts) => {
Rope::from_iter_small(parts.iter().map(|part| &*part.value))
}
InlayHintLabel::String(s) => Rope::from(s),
InlayHintLabel::LabelParts(parts) => parts.iter().map(|part| &*part.value).collect(),
}
}
}
@ -5392,12 +5390,7 @@ impl Project {
worktree
.update(cx, |worktree, cx| {
let line_ending = text::LineEnding::detect(&new_text);
worktree.write_file(
rel_path.clone(),
Rope::from_str(&new_text, cx.background_executor()),
line_ending,
cx,
)
worktree.write_file(rel_path.clone(), new_text.into(), line_ending, cx)
})?
.await
.context("Failed to write settings file")?;

View file

@ -1461,21 +1461,21 @@ async fn test_reporting_fs_changes_to_language_servers(cx: &mut gpui::TestAppCon
.unwrap();
fs.save(
path!("/the-root/Cargo.lock").as_ref(),
&Rope::default(),
&"".into(),
Default::default(),
)
.await
.unwrap();
fs.save(
path!("/the-stdlib/LICENSE").as_ref(),
&Rope::default(),
&"".into(),
Default::default(),
)
.await
.unwrap();
fs.save(
path!("/the/stdlib/src/string.rs").as_ref(),
&Rope::default(),
&"".into(),
Default::default(),
)
.await
@ -4064,7 +4064,7 @@ async fn test_file_changes_multiple_times_on_disk(cx: &mut gpui::TestAppContext)
// to be detected by the worktree, so that the buffer starts reloading.
fs.save(
path!("/dir/file1").as_ref(),
&Rope::from_str("the first contents", cx.background_executor()),
&"the first contents".into(),
Default::default(),
)
.await
@ -4075,7 +4075,7 @@ async fn test_file_changes_multiple_times_on_disk(cx: &mut gpui::TestAppContext)
// previous file change may still be in progress.
fs.save(
path!("/dir/file1").as_ref(),
&Rope::from_str("the second contents", cx.background_executor()),
&"the second contents".into(),
Default::default(),
)
.await
@ -4119,7 +4119,7 @@ async fn test_edit_buffer_while_it_reloads(cx: &mut gpui::TestAppContext) {
// to be detected by the worktree, so that the buffer starts reloading.
fs.save(
path!("/dir/file1").as_ref(),
&Rope::from_str("the first contents", cx.background_executor()),
&"the first contents".into(),
Default::default(),
)
.await
@ -4797,7 +4797,7 @@ async fn test_buffer_file_changes_on_disk(cx: &mut gpui::TestAppContext) {
marked_text_offsets("oneˇ\nthree ˇFOURˇ five\nsixtyˇ seven\n");
fs.save(
path!("/dir/the-file").as_ref(),
&Rope::from_str(new_contents.as_str(), cx.background_executor()),
&new_contents.as_str().into(),
LineEnding::Unix,
)
.await
@ -4829,7 +4829,7 @@ async fn test_buffer_file_changes_on_disk(cx: &mut gpui::TestAppContext) {
// Change the file on disk again, adding blank lines to the beginning.
fs.save(
path!("/dir/the-file").as_ref(),
&Rope::from_str("\n\n\nAAAA\naaa\nBB\nbbbbb\n", cx.background_executor()),
&"\n\n\nAAAA\naaa\nBB\nbbbbb\n".into(),
LineEnding::Unix,
)
.await
@ -4881,7 +4881,7 @@ async fn test_buffer_line_endings(cx: &mut gpui::TestAppContext) {
// state updates correctly.
fs.save(
path!("/dir/file1").as_ref(),
&Rope::from_str("aaa\nb\nc\n", cx.background_executor()),
&"aaa\nb\nc\n".into(),
LineEnding::Windows,
)
.await

View file

@ -13,7 +13,7 @@ use fs::{FakeFs, Fs};
use gpui::{AppContext as _, Entity, SemanticVersion, SharedString, TestAppContext};
use http_client::{BlockedHttpClient, FakeHttpClient};
use language::{
Buffer, FakeLspAdapter, LanguageConfig, LanguageMatcher, LanguageRegistry, LineEnding, Rope,
Buffer, FakeLspAdapter, LanguageConfig, LanguageMatcher, LanguageRegistry, LineEnding,
language_settings::{AllLanguageSettings, language_settings},
};
use lsp::{CompletionContext, CompletionResponse, CompletionTriggerKind, LanguageServerName};
@ -120,7 +120,7 @@ async fn test_basic_remote_editing(cx: &mut TestAppContext, server_cx: &mut Test
// sees the new file.
fs.save(
path!("/code/project1/src/main.rs").as_ref(),
&Rope::from_str_small("fn main() {}"),
&"fn main() {}".into(),
Default::default(),
)
.await
@ -766,7 +766,7 @@ async fn test_remote_reload(cx: &mut TestAppContext, server_cx: &mut TestAppCont
fs.save(
&PathBuf::from(path!("/code/project1/src/lib.rs")),
&Rope::from_str_small("bangles"),
&("bangles".to_string().into()),
LineEnding::Unix,
)
.await
@ -781,7 +781,7 @@ async fn test_remote_reload(cx: &mut TestAppContext, server_cx: &mut TestAppCont
fs.save(
&PathBuf::from(path!("/code/project1/src/lib.rs")),
&Rope::from_str_small("bloop"),
&("bloop".to_string().into()),
LineEnding::Unix,
)
.await

View file

@ -1,10 +1,9 @@
use futures::FutureExt;
use gpui::{
AnyElement, AnyView, App, BackgroundExecutor, ElementId, FontStyle, FontWeight, HighlightStyle,
InteractiveText, IntoElement, SharedString, StrikethroughStyle, StyledText, UnderlineStyle,
Window,
AnyElement, AnyView, App, ElementId, FontStyle, FontWeight, HighlightStyle, InteractiveText,
IntoElement, SharedString, StrikethroughStyle, StyledText, UnderlineStyle, Window,
};
use language::{HighlightId, Language, LanguageRegistry, Rope};
use language::{HighlightId, Language, LanguageRegistry};
use std::{ops::Range, sync::Arc};
use theme::ActiveTheme;
use ui::LinkPreview;
@ -57,7 +56,6 @@ impl RichText {
block: String,
mentions: &[Mention],
language_registry: &Arc<LanguageRegistry>,
executor: &BackgroundExecutor,
) -> Self {
let mut text = String::new();
let mut highlights = Vec::new();
@ -72,7 +70,6 @@ impl RichText {
&mut highlights,
&mut link_ranges,
&mut link_urls,
executor,
);
text.truncate(text.trim_end().len());
@ -187,7 +184,6 @@ pub fn render_markdown_mut(
highlights: &mut Vec<(Range<usize>, Highlight)>,
link_ranges: &mut Vec<Range<usize>>,
link_urls: &mut Vec<String>,
executor: &BackgroundExecutor,
) {
use pulldown_cmark::{CodeBlockKind, Event, Options, Parser, Tag, TagEnd};
@ -206,7 +202,7 @@ pub fn render_markdown_mut(
match event {
Event::Text(t) => {
if let Some(language) = &current_language {
render_code(text, highlights, t.as_ref(), language, executor);
render_code(text, highlights, t.as_ref(), language);
} else {
while let Some(mention) = mentions.first() {
if !source_range.contains_inclusive(&mention.range) {
@ -377,14 +373,11 @@ pub fn render_code(
highlights: &mut Vec<(Range<usize>, Highlight)>,
content: &str,
language: &Arc<Language>,
executor: &BackgroundExecutor,
) {
let prev_len = text.len();
text.push_str(content);
let mut offset = 0;
for (range, highlight_id) in
language.highlight_text(&Rope::from_str(content, executor), 0..content.len())
{
for (range, highlight_id) in language.highlight_text(&content.into(), 0..content.len()) {
if range.start > offset {
highlights.push((prev_len + offset..prev_len + range.start, Highlight::Code));
}

View file

@ -14,10 +14,10 @@ path = "src/rope.rs"
[dependencies]
arrayvec = "0.7.1"
log.workspace = true
rayon.workspace = true
sum_tree.workspace = true
unicode-segmentation.workspace = true
util.workspace = true
gpui.workspace = true
[dev-dependencies]
ctor.workspace = true

View file

@ -3,7 +3,6 @@ use std::ops::Range;
use criterion::{
BatchSize, BenchmarkId, Criterion, Throughput, black_box, criterion_group, criterion_main,
};
use gpui::{AsyncApp, TestAppContext};
use rand::prelude::*;
use rand::rngs::StdRng;
use rope::{Point, Rope};
@ -27,10 +26,10 @@ fn generate_random_text(rng: &mut StdRng, len: usize) -> String {
str
}
fn generate_random_rope(rng: &mut StdRng, text_len: usize, cx: &AsyncApp) -> Rope {
fn generate_random_rope(rng: &mut StdRng, text_len: usize) -> Rope {
let text = generate_random_text(rng, text_len);
let mut rope = Rope::new();
rope.push(&text, cx.background_executor());
rope.push(&text);
rope
}
@ -83,13 +82,11 @@ fn rope_benchmarks(c: &mut Criterion) {
group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {
let mut rng = StdRng::seed_from_u64(SEED);
let text = generate_random_text(&mut rng, *size);
let cx = TestAppContext::single();
let cx = cx.to_async();
b.iter(|| {
let mut rope = Rope::new();
for _ in 0..10 {
rope.push(&text, cx.background_executor());
rope.push(&text);
}
});
});
@ -102,10 +99,8 @@ fn rope_benchmarks(c: &mut Criterion) {
group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {
let mut rng = StdRng::seed_from_u64(SEED);
let mut random_ropes = Vec::new();
let cx = TestAppContext::single();
let cx = cx.to_async();
for _ in 0..5 {
let rope = generate_random_rope(&mut rng, *size, &cx);
let rope = generate_random_rope(&mut rng, *size);
random_ropes.push(rope);
}
@ -124,9 +119,7 @@ fn rope_benchmarks(c: &mut Criterion) {
group.throughput(Throughput::Bytes(*size as u64));
group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {
let mut rng = StdRng::seed_from_u64(SEED);
let cx = TestAppContext::single();
let cx = cx.to_async();
let rope = generate_random_rope(&mut rng, *size, &cx);
let rope = generate_random_rope(&mut rng, *size);
b.iter_batched(
|| generate_random_rope_ranges(&mut rng, &rope),
@ -146,9 +139,7 @@ fn rope_benchmarks(c: &mut Criterion) {
group.throughput(Throughput::Bytes(*size as u64));
group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {
let mut rng = StdRng::seed_from_u64(SEED);
let cx = TestAppContext::single();
let cx = cx.to_async();
let rope = generate_random_rope(&mut rng, *size, &cx);
let rope = generate_random_rope(&mut rng, *size);
b.iter_batched(
|| generate_random_rope_ranges(&mut rng, &rope),
@ -169,9 +160,7 @@ fn rope_benchmarks(c: &mut Criterion) {
group.throughput(Throughput::Bytes(*size as u64));
group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {
let mut rng = StdRng::seed_from_u64(SEED);
let cx = TestAppContext::single();
let cx = cx.to_async();
let rope = generate_random_rope(&mut rng, *size, &cx);
let rope = generate_random_rope(&mut rng, *size);
b.iter(|| {
let chars = rope.chars().count();
@ -186,9 +175,7 @@ fn rope_benchmarks(c: &mut Criterion) {
group.throughput(Throughput::Bytes(*size as u64));
group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {
let mut rng = StdRng::seed_from_u64(SEED);
let cx = TestAppContext::single();
let cx = cx.to_async();
let rope = generate_random_rope(&mut rng, *size, &cx);
let rope = generate_random_rope(&mut rng, *size);
b.iter_batched(
|| generate_random_rope_points(&mut rng, &rope),
@ -209,9 +196,7 @@ fn rope_benchmarks(c: &mut Criterion) {
group.throughput(Throughput::Bytes(*size as u64));
group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {
let mut rng = StdRng::seed_from_u64(SEED);
let cx = TestAppContext::single();
let cx = cx.to_async();
let rope = generate_random_rope(&mut rng, *size, &cx);
let rope = generate_random_rope(&mut rng, *size);
b.iter_batched(
|| generate_random_rope_points(&mut rng, &rope),
@ -231,9 +216,7 @@ fn rope_benchmarks(c: &mut Criterion) {
group.throughput(Throughput::Bytes(*size as u64));
group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {
let mut rng = StdRng::seed_from_u64(SEED);
let cx = TestAppContext::single();
let cx = cx.to_async();
let rope = generate_random_rope(&mut rng, *size, &cx);
let rope = generate_random_rope(&mut rng, *size);
b.iter_batched(
|| {

View file

@ -5,7 +5,7 @@ mod point_utf16;
mod unclipped;
use arrayvec::ArrayVec;
use gpui::BackgroundExecutor;
use rayon::iter::{IntoParallelIterator, ParallelIterator as _};
use std::{
cmp, fmt, io, mem,
ops::{self, AddAssign, Range},
@ -31,41 +31,6 @@ impl Rope {
Self::default()
}
/// Create a new rope from a string without trying to parallelize the construction for large strings.
pub fn from_str_small(text: &str) -> Self {
let mut rope = Self::new();
rope.push_small(text);
rope
}
/// Create a new rope from a string.
pub fn from_str(text: &str, executor: &BackgroundExecutor) -> Self {
let mut rope = Self::new();
rope.push(text, executor);
rope
}
/// Create a new rope from a string without trying to parallelize the construction for large strings.
pub fn from_iter_small<'a, T: IntoIterator<Item = &'a str>>(iter: T) -> Self {
let mut rope = Rope::new();
for chunk in iter {
rope.push_small(chunk);
}
rope
}
/// Create a new rope from a string.
pub fn from_iter<'a, T: IntoIterator<Item = &'a str>>(
iter: T,
executor: &BackgroundExecutor,
) -> Self {
let mut rope = Rope::new();
for chunk in iter {
rope.push(chunk, executor);
}
rope
}
/// Checks that `index`-th byte is the first byte in a UTF-8 code point
/// sequence or the end of the string.
///
@ -180,12 +145,12 @@ impl Rope {
self.check_invariants();
}
pub fn replace(&mut self, range: Range<usize>, text: &str, executor: &BackgroundExecutor) {
pub fn replace(&mut self, range: Range<usize>, text: &str) {
let mut new_rope = Rope::new();
let mut cursor = self.cursor(0);
new_rope.append(cursor.slice(range.start));
cursor.seek_forward(range.end);
new_rope.push(text, executor);
new_rope.push(text);
new_rope.append(cursor.suffix());
*self = new_rope;
}
@ -203,12 +168,28 @@ impl Rope {
self.slice(start..end)
}
pub fn push(&mut self, mut text: &str, executor: &BackgroundExecutor) {
self.fill_last_chunk(&mut text);
pub fn push(&mut self, mut text: &str) {
self.chunks.update_last(
|last_chunk| {
let split_ix = if last_chunk.text.len() + text.len() <= chunk::MAX_BASE {
text.len()
} else {
let mut split_ix = cmp::min(
chunk::MIN_BASE.saturating_sub(last_chunk.text.len()),
text.len(),
);
while !text.is_char_boundary(split_ix) {
split_ix += 1;
}
split_ix
};
if text.is_empty() {
return;
}
let (suffix, remainder) = text.split_at(split_ix);
last_chunk.push_str(suffix);
text = remainder;
},
(),
);
#[cfg(all(test, not(rust_analyzer)))]
const NUM_CHUNKS: usize = 16;
@ -219,8 +200,7 @@ impl Rope {
// but given the chunk boundary can land within a character
// we need to accommodate for the worst case where every chunk gets cut short by up to 4 bytes
if text.len() > NUM_CHUNKS * chunk::MAX_BASE - NUM_CHUNKS * 4 {
let future = self.push_large(text, executor.clone());
return executor.block(future);
return self.push_large(text);
}
// 16 is enough as otherwise we will hit the branch above
let mut new_chunks = ArrayVec::<_, NUM_CHUNKS>::new();
@ -240,57 +220,8 @@ impl Rope {
self.check_invariants();
}
/// Pushes a string into the rope. Unlike [`push`], this method does not parallelize the construction on large strings.
pub fn push_small(&mut self, mut text: &str) {
self.fill_last_chunk(&mut text);
if text.is_empty() {
return;
}
// 16 is enough as otherwise we will hit the branch above
let mut new_chunks = Vec::new();
while !text.is_empty() {
let mut split_ix = cmp::min(chunk::MAX_BASE, text.len());
while !text.is_char_boundary(split_ix) {
split_ix -= 1;
}
let (chunk, remainder) = text.split_at(split_ix);
new_chunks.push(chunk);
text = remainder;
}
self.chunks
.extend(new_chunks.into_iter().map(Chunk::new), ());
self.check_invariants();
}
fn fill_last_chunk(&mut self, text: &mut &str) {
self.chunks.update_last(
|last_chunk| {
let split_ix = if last_chunk.text.len() + text.len() <= chunk::MAX_BASE {
text.len()
} else {
let mut split_ix = cmp::min(
chunk::MIN_BASE.saturating_sub(last_chunk.text.len()),
text.len(),
);
while !text.is_char_boundary(split_ix) {
split_ix += 1;
}
split_ix
};
let (suffix, remainder) = text.split_at(split_ix);
last_chunk.push_str(suffix);
*text = remainder;
},
(),
);
}
/// A copy of `push` specialized for working with large quantities of text.
async fn push_large(&mut self, mut text: &str, executor: BackgroundExecutor) {
fn push_large(&mut self, mut text: &str) {
// To avoid frequent reallocs when loading large swaths of file contents,
// we estimate worst-case `new_chunks` capacity;
// Chunk is a fixed-capacity buffer. If a character falls on
@ -323,14 +254,8 @@ impl Rope {
const PARALLEL_THRESHOLD: usize = 4 * (2 * sum_tree::TREE_BASE);
if new_chunks.len() >= PARALLEL_THRESHOLD {
// SAFETY: transmuting to 'static is sound here. We block on the future making use of this
// and we know that the result of this computation is not stashing the static reference
// away.
let new_chunks =
unsafe { std::mem::transmute::<Vec<&str>, Vec<&'static str>>(new_chunks) };
self.chunks
.async_extend(new_chunks.into_iter().map(Chunk::new), executor)
.await;
.par_extend(new_chunks.into_par_iter().map(Chunk::new), ());
} else {
self.chunks
.extend(new_chunks.into_iter().map(Chunk::new), ());
@ -367,13 +292,8 @@ impl Rope {
}
}
pub fn push_front(&mut self, text: &str, cx: &BackgroundExecutor) {
let suffix = mem::replace(self, Rope::from_str(text, cx));
self.append(suffix);
}
pub fn push_front_small(&mut self, text: &str) {
let suffix = mem::replace(self, Rope::from_str_small(text));
pub fn push_front(&mut self, text: &str) {
let suffix = mem::replace(self, Rope::from(text));
self.append(suffix);
}
@ -657,19 +577,37 @@ impl Rope {
}
}
// impl From<String> for Rope {
// #[inline(always)]
// fn from(text: String) -> Self {
// Rope::from(text.as_str())
// }
// }
impl<'a> From<&'a str> for Rope {
fn from(text: &'a str) -> Self {
let mut rope = Self::new();
rope.push(text);
rope
}
}
// impl From<&String> for Rope {
// #[inline(always)]
// fn from(text: &String) -> Self {
// Rope::from(text.as_str())
// }
// }
impl<'a> FromIterator<&'a str> for Rope {
fn from_iter<T: IntoIterator<Item = &'a str>>(iter: T) -> Self {
let mut rope = Rope::new();
for chunk in iter {
rope.push(chunk);
}
rope
}
}
impl From<String> for Rope {
#[inline(always)]
fn from(text: String) -> Self {
Rope::from(text.as_str())
}
}
impl From<&String> for Rope {
#[inline(always)]
fn from(text: &String) -> Self {
Rope::from(text.as_str())
}
}
impl fmt::Display for Rope {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@ -1701,7 +1639,6 @@ where
mod tests {
use super::*;
use Bias::{Left, Right};
use gpui::TestAppContext;
use rand::prelude::*;
use std::{cmp::Ordering, env, io::Read};
use util::RandomCharIter;
@ -1711,17 +1648,17 @@ mod tests {
zlog::init_test();
}
#[gpui::test]
async fn test_all_4_byte_chars(cx: &mut TestAppContext) {
#[test]
fn test_all_4_byte_chars() {
let mut rope = Rope::new();
let text = "🏀".repeat(256);
rope.push(&text, cx.background_executor());
rope.push(&text);
assert_eq!(rope.text(), text);
}
#[gpui::test]
fn test_clip(cx: &mut TestAppContext) {
let rope = Rope::from_str("🧘", cx.background_executor());
#[test]
fn test_clip() {
let rope = Rope::from("🧘");
assert_eq!(rope.clip_offset(1, Bias::Left), 0);
assert_eq!(rope.clip_offset(1, Bias::Right), 4);
@ -1767,9 +1704,9 @@ mod tests {
);
}
#[gpui::test]
fn test_prev_next_line(cx: &mut TestAppContext) {
let rope = Rope::from_str("abc\ndef\nghi\njkl", cx.background_executor());
#[test]
fn test_prev_next_line() {
let rope = Rope::from("abc\ndef\nghi\njkl");
let mut chunks = rope.chunks();
assert_eq!(chunks.peek().unwrap().chars().next().unwrap(), 'a');
@ -1811,16 +1748,16 @@ mod tests {
assert_eq!(chunks.peek(), None);
}
#[gpui::test]
fn test_lines(cx: &mut TestAppContext) {
let rope = Rope::from_str("abc\ndefg\nhi", cx.background_executor());
#[test]
fn test_lines() {
let rope = Rope::from("abc\ndefg\nhi");
let mut lines = rope.chunks().lines();
assert_eq!(lines.next(), Some("abc"));
assert_eq!(lines.next(), Some("defg"));
assert_eq!(lines.next(), Some("hi"));
assert_eq!(lines.next(), None);
let rope = Rope::from_str("abc\ndefg\nhi\n", cx.background_executor());
let rope = Rope::from("abc\ndefg\nhi\n");
let mut lines = rope.chunks().lines();
assert_eq!(lines.next(), Some("abc"));
assert_eq!(lines.next(), Some("defg"));
@ -1828,14 +1765,14 @@ mod tests {
assert_eq!(lines.next(), Some(""));
assert_eq!(lines.next(), None);
let rope = Rope::from_str("abc\ndefg\nhi", cx.background_executor());
let rope = Rope::from("abc\ndefg\nhi");
let mut lines = rope.reversed_chunks_in_range(0..rope.len()).lines();
assert_eq!(lines.next(), Some("hi"));
assert_eq!(lines.next(), Some("defg"));
assert_eq!(lines.next(), Some("abc"));
assert_eq!(lines.next(), None);
let rope = Rope::from_str("abc\ndefg\nhi\n", cx.background_executor());
let rope = Rope::from("abc\ndefg\nhi\n");
let mut lines = rope.reversed_chunks_in_range(0..rope.len()).lines();
assert_eq!(lines.next(), Some(""));
assert_eq!(lines.next(), Some("hi"));
@ -1843,14 +1780,14 @@ mod tests {
assert_eq!(lines.next(), Some("abc"));
assert_eq!(lines.next(), None);
let rope = Rope::from_str("abc\nlonger line test\nhi", cx.background_executor());
let rope = Rope::from("abc\nlonger line test\nhi");
let mut lines = rope.chunks().lines();
assert_eq!(lines.next(), Some("abc"));
assert_eq!(lines.next(), Some("longer line test"));
assert_eq!(lines.next(), Some("hi"));
assert_eq!(lines.next(), None);
let rope = Rope::from_str("abc\nlonger line test\nhi", cx.background_executor());
let rope = Rope::from("abc\nlonger line test\nhi");
let mut lines = rope.reversed_chunks_in_range(0..rope.len()).lines();
assert_eq!(lines.next(), Some("hi"));
assert_eq!(lines.next(), Some("longer line test"));
@ -1859,7 +1796,7 @@ mod tests {
}
#[gpui::test(iterations = 100)]
async fn test_random_rope(cx: &mut TestAppContext, mut rng: StdRng) {
fn test_random_rope(mut rng: StdRng) {
let operations = env::var("OPERATIONS")
.map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
.unwrap_or(10);
@ -1875,7 +1812,7 @@ mod tests {
let mut new_actual = Rope::new();
let mut cursor = actual.cursor(0);
new_actual.append(cursor.slice(start_ix));
new_actual.push(&new_text, cx.background_executor());
new_actual.push(&new_text);
cursor.seek_forward(end_ix);
new_actual.append(cursor.suffix());
actual = new_actual;
@ -2175,10 +2112,10 @@ mod tests {
}
}
#[gpui::test]
fn test_chunks_equals_str(cx: &mut TestAppContext) {
#[test]
fn test_chunks_equals_str() {
let text = "This is a multi-chunk\n& multi-line test string!";
let rope = Rope::from_str(text, cx.background_executor());
let rope = Rope::from(text);
for start in 0..text.len() {
for end in start..text.len() {
let range = start..end;
@ -2221,37 +2158,34 @@ mod tests {
}
}
let rope = Rope::from_str("", cx.background_executor());
let rope = Rope::from("");
assert!(rope.chunks_in_range(0..0).equals_str(""));
assert!(rope.reversed_chunks_in_range(0..0).equals_str(""));
assert!(!rope.chunks_in_range(0..0).equals_str("foo"));
assert!(!rope.reversed_chunks_in_range(0..0).equals_str("foo"));
}
#[gpui::test]
fn test_is_char_boundary(cx: &mut TestAppContext) {
#[test]
fn test_is_char_boundary() {
let fixture = "";
let rope = Rope::from_str("", cx.background_executor());
let rope = Rope::from("");
for b in 0..=fixture.len() {
assert_eq!(rope.is_char_boundary(b), fixture.is_char_boundary(b));
}
let fixture = "";
let rope = Rope::from_str("", cx.background_executor());
let rope = Rope::from("");
for b in 0..=fixture.len() {
assert_eq!(rope.is_char_boundary(b), fixture.is_char_boundary(b));
}
let fixture = "🔴🟠🟡🟢🔵🟣⚫️⚪️🟤\n🏳️‍⚧️🏁🏳️‍🌈🏴‍☠️⛳️📬📭🏴🏳️🚩";
let rope = Rope::from_str(
"🔴🟠🟡🟢🔵🟣⚫️⚪️🟤\n🏳️‍⚧️🏁🏳️‍🌈🏴‍☠️⛳️📬📭🏴🏳️🚩",
cx.background_executor(),
);
let rope = Rope::from("🔴🟠🟡🟢🔵🟣⚫️⚪️🟤\n🏳️‍⚧️🏁🏳️‍🌈🏴‍☠️⛳️📬📭🏴🏳️🚩");
for b in 0..=fixture.len() {
assert_eq!(rope.is_char_boundary(b), fixture.is_char_boundary(b));
}
}
#[gpui::test]
fn test_floor_char_boundary(cx: &mut TestAppContext) {
#[test]
fn test_floor_char_boundary() {
// polyfill of str::floor_char_boundary
fn floor_char_boundary(str: &str, index: usize) -> usize {
if index >= str.len() {
@ -2267,7 +2201,7 @@ mod tests {
}
let fixture = "";
let rope = Rope::from_str("", cx.background_executor());
let rope = Rope::from("");
for b in 0..=fixture.len() {
assert_eq!(
rope.floor_char_boundary(b),
@ -2276,7 +2210,7 @@ mod tests {
}
let fixture = "";
let rope = Rope::from_str("", cx.background_executor());
let rope = Rope::from("");
for b in 0..=fixture.len() {
assert_eq!(
rope.floor_char_boundary(b),
@ -2285,10 +2219,7 @@ mod tests {
}
let fixture = "🔴🟠🟡🟢🔵🟣⚫️⚪️🟤\n🏳️‍⚧️🏁🏳️‍🌈🏴‍☠️⛳️📬📭🏴🏳️🚩";
let rope = Rope::from_str(
"🔴🟠🟡🟢🔵🟣⚫️⚪️🟤\n🏳️‍⚧️🏁🏳️‍🌈🏴‍☠️⛳️📬📭🏴🏳️🚩",
cx.background_executor(),
);
let rope = Rope::from("🔴🟠🟡🟢🔵🟣⚫️⚪️🟤\n🏳️‍⚧️🏁🏳️‍🌈🏴‍☠️⛳️📬📭🏴🏳️🚩");
for b in 0..=fixture.len() {
assert_eq!(
rope.floor_char_boundary(b),
@ -2297,8 +2228,8 @@ mod tests {
}
}
#[gpui::test]
fn test_ceil_char_boundary(cx: &mut TestAppContext) {
#[test]
fn test_ceil_char_boundary() {
// polyfill of str::ceil_char_boundary
fn ceil_char_boundary(str: &str, index: usize) -> usize {
if index > str.len() {
@ -2313,22 +2244,19 @@ mod tests {
}
let fixture = "";
let rope = Rope::from_str("", cx.background_executor());
let rope = Rope::from("");
for b in 0..=fixture.len() {
assert_eq!(rope.ceil_char_boundary(b), ceil_char_boundary(&fixture, b));
}
let fixture = "";
let rope = Rope::from_str("", cx.background_executor());
let rope = Rope::from("");
for b in 0..=fixture.len() {
assert_eq!(rope.ceil_char_boundary(b), ceil_char_boundary(&fixture, b));
}
let fixture = "🔴🟠🟡🟢🔵🟣⚫️⚪️🟤\n🏳️‍⚧️🏁🏳️‍🌈🏴‍☠️⛳️📬📭🏴🏳️🚩";
let rope = Rope::from_str(
"🔴🟠🟡🟢🔵🟣⚫️⚪️🟤\n🏳️‍⚧️🏁🏳️‍🌈🏴‍☠️⛳️📬📭🏴🏳️🚩",
cx.background_executor(),
);
let rope = Rope::from("🔴🟠🟡🟢🔵🟣⚫️⚪️🟤\n🏳️‍⚧️🏁🏳️‍🌈🏴‍☠️⛳️📬📭🏴🏳️🚩");
for b in 0..=fixture.len() {
assert_eq!(rope.ceil_char_boundary(b), ceil_char_boundary(&fixture, b));
}

View file

@ -554,7 +554,7 @@ impl RulesLibrary {
let prompt_id = PromptId::new();
let save = self.store.update(cx, |store, cx| {
store.save(prompt_id, None, false, Default::default(), cx)
store.save(prompt_id, None, false, "".into(), cx)
});
self.picker
.update(cx, |picker, cx| picker.refresh(window, cx));
@ -888,13 +888,7 @@ impl RulesLibrary {
let new_id = PromptId::new();
let body = rule.body_editor.read(cx).text(cx);
let save = self.store.update(cx, |store, cx| {
store.save(
new_id,
Some(title.into()),
false,
Rope::from_str(&body, cx.background_executor()),
cx,
)
store.save(new_id, Some(title.into()), false, body.into(), cx)
});
self.picker
.update(cx, |picker, cx| picker.refresh(window, cx));

View file

@ -14,7 +14,6 @@ path = "src/streaming_diff.rs"
[dependencies]
ordered-float.workspace = true
rope.workspace = true
gpui.workspace = true
[dev-dependencies]
rand.workspace = true

View file

@ -503,12 +503,11 @@ fn is_line_end(point: Point, text: &Rope) -> bool {
#[cfg(test)]
mod tests {
use super::*;
use gpui::BackgroundExecutor;
use rand::prelude::*;
use std::env;
#[gpui::test]
fn test_delete_first_of_two_lines(cx: &mut gpui::TestAppContext) {
#[test]
fn test_delete_first_of_two_lines() {
let old_text = "aaaa\nbbbb";
let char_ops = vec![
CharOperation::Delete { bytes: 5 },
@ -524,18 +523,18 @@ mod tests {
apply_line_operations(old_text, &new_text, &expected_line_ops)
);
let line_ops = char_ops_to_line_ops(old_text, &char_ops, cx.background_executor());
let line_ops = char_ops_to_line_ops(old_text, &char_ops);
assert_eq!(line_ops, expected_line_ops);
}
#[gpui::test]
fn test_delete_second_of_two_lines(cx: &mut gpui::TestAppContext) {
#[test]
fn test_delete_second_of_two_lines() {
let old_text = "aaaa\nbbbb";
let char_ops = vec![
CharOperation::Keep { bytes: 5 },
CharOperation::Delete { bytes: 4 },
];
let line_ops = char_ops_to_line_ops(old_text, &char_ops, cx.background_executor());
let line_ops = char_ops_to_line_ops(old_text, &char_ops);
assert_eq!(
line_ops,
vec![
@ -551,8 +550,8 @@ mod tests {
);
}
#[gpui::test]
fn test_add_new_line(cx: &mut gpui::TestAppContext) {
#[test]
fn test_add_new_line() {
let old_text = "aaaa\nbbbb";
let char_ops = vec![
CharOperation::Keep { bytes: 9 },
@ -560,7 +559,7 @@ mod tests {
text: "\ncccc".into(),
},
];
let line_ops = char_ops_to_line_ops(old_text, &char_ops, cx.background_executor());
let line_ops = char_ops_to_line_ops(old_text, &char_ops);
assert_eq!(
line_ops,
vec![
@ -575,15 +574,15 @@ mod tests {
);
}
#[gpui::test]
fn test_delete_line_in_middle(cx: &mut gpui::TestAppContext) {
#[test]
fn test_delete_line_in_middle() {
let old_text = "aaaa\nbbbb\ncccc";
let char_ops = vec![
CharOperation::Keep { bytes: 5 },
CharOperation::Delete { bytes: 5 },
CharOperation::Keep { bytes: 4 },
];
let line_ops = char_ops_to_line_ops(old_text, &char_ops, cx.background_executor());
let line_ops = char_ops_to_line_ops(old_text, &char_ops);
assert_eq!(
line_ops,
vec![
@ -599,8 +598,8 @@ mod tests {
);
}
#[gpui::test]
fn test_replace_line(cx: &mut gpui::TestAppContext) {
#[test]
fn test_replace_line() {
let old_text = "aaaa\nbbbb\ncccc";
let char_ops = vec![
CharOperation::Keep { bytes: 5 },
@ -610,7 +609,7 @@ mod tests {
},
CharOperation::Keep { bytes: 5 },
];
let line_ops = char_ops_to_line_ops(old_text, &char_ops, cx.background_executor());
let line_ops = char_ops_to_line_ops(old_text, &char_ops);
assert_eq!(
line_ops,
vec![
@ -627,8 +626,8 @@ mod tests {
);
}
#[gpui::test]
fn test_multiple_edits_on_different_lines(cx: &mut gpui::TestAppContext) {
#[test]
fn test_multiple_edits_on_different_lines() {
let old_text = "aaaa\nbbbb\ncccc\ndddd";
let char_ops = vec![
CharOperation::Insert { text: "A".into() },
@ -639,7 +638,7 @@ mod tests {
text: "\nEEEE".into(),
},
];
let line_ops = char_ops_to_line_ops(old_text, &char_ops, cx.background_executor());
let line_ops = char_ops_to_line_ops(old_text, &char_ops);
assert_eq!(
line_ops,
vec![
@ -657,15 +656,15 @@ mod tests {
);
}
#[gpui::test]
fn test_edit_at_end_of_line(cx: &mut gpui::TestAppContext) {
#[test]
fn test_edit_at_end_of_line() {
let old_text = "aaaa\nbbbb\ncccc";
let char_ops = vec![
CharOperation::Keep { bytes: 4 },
CharOperation::Insert { text: "A".into() },
CharOperation::Keep { bytes: 10 },
];
let line_ops = char_ops_to_line_ops(old_text, &char_ops, cx.background_executor());
let line_ops = char_ops_to_line_ops(old_text, &char_ops);
assert_eq!(
line_ops,
vec![
@ -681,8 +680,8 @@ mod tests {
);
}
#[gpui::test]
fn test_insert_newline_character(cx: &mut gpui::TestAppContext) {
#[test]
fn test_insert_newline_character() {
let old_text = "aaaabbbb";
let char_ops = vec![
CharOperation::Keep { bytes: 4 },
@ -690,7 +689,7 @@ mod tests {
CharOperation::Keep { bytes: 4 },
];
let new_text = apply_char_operations(old_text, &char_ops);
let line_ops = char_ops_to_line_ops(old_text, &char_ops, cx.background_executor());
let line_ops = char_ops_to_line_ops(old_text, &char_ops);
assert_eq!(
line_ops,
vec![
@ -704,14 +703,14 @@ mod tests {
);
}
#[gpui::test]
fn test_insert_newline_at_beginning(cx: &mut gpui::TestAppContext) {
#[test]
fn test_insert_newline_at_beginning() {
let old_text = "aaaa\nbbbb";
let char_ops = vec![
CharOperation::Insert { text: "\n".into() },
CharOperation::Keep { bytes: 9 },
];
let line_ops = char_ops_to_line_ops(old_text, &char_ops, cx.background_executor());
let line_ops = char_ops_to_line_ops(old_text, &char_ops);
assert_eq!(
line_ops,
vec![
@ -726,15 +725,15 @@ mod tests {
);
}
#[gpui::test]
fn test_delete_newline(cx: &mut gpui::TestAppContext) {
#[test]
fn test_delete_newline() {
let old_text = "aaaa\nbbbb";
let char_ops = vec![
CharOperation::Keep { bytes: 4 },
CharOperation::Delete { bytes: 1 },
CharOperation::Keep { bytes: 4 },
];
let line_ops = char_ops_to_line_ops(old_text, &char_ops, cx.background_executor());
let line_ops = char_ops_to_line_ops(old_text, &char_ops);
assert_eq!(
line_ops,
vec![
@ -750,8 +749,8 @@ mod tests {
);
}
#[gpui::test]
fn test_insert_multiple_newlines(cx: &mut gpui::TestAppContext) {
#[test]
fn test_insert_multiple_newlines() {
let old_text = "aaaa\nbbbb";
let char_ops = vec![
CharOperation::Keep { bytes: 5 },
@ -760,7 +759,7 @@ mod tests {
},
CharOperation::Keep { bytes: 4 },
];
let line_ops = char_ops_to_line_ops(old_text, &char_ops, cx.background_executor());
let line_ops = char_ops_to_line_ops(old_text, &char_ops);
assert_eq!(
line_ops,
vec![
@ -776,15 +775,15 @@ mod tests {
);
}
#[gpui::test]
fn test_delete_multiple_newlines(cx: &mut gpui::TestAppContext) {
#[test]
fn test_delete_multiple_newlines() {
let old_text = "aaaa\n\n\nbbbb";
let char_ops = vec![
CharOperation::Keep { bytes: 5 },
CharOperation::Delete { bytes: 2 },
CharOperation::Keep { bytes: 4 },
];
let line_ops = char_ops_to_line_ops(old_text, &char_ops, cx.background_executor());
let line_ops = char_ops_to_line_ops(old_text, &char_ops);
assert_eq!(
line_ops,
vec![
@ -800,8 +799,8 @@ mod tests {
);
}
#[gpui::test]
fn test_complex_scenario(cx: &mut gpui::TestAppContext) {
#[test]
fn test_complex_scenario() {
let old_text = "line1\nline2\nline3\nline4";
let char_ops = vec![
CharOperation::Keep { bytes: 6 },
@ -815,7 +814,7 @@ mod tests {
},
CharOperation::Keep { bytes: 6 },
];
let line_ops = char_ops_to_line_ops(old_text, &char_ops, cx.background_executor());
let line_ops = char_ops_to_line_ops(old_text, &char_ops);
assert_eq!(
line_ops,
vec![
@ -835,8 +834,8 @@ mod tests {
);
}
#[gpui::test]
fn test_cleaning_up_common_suffix(cx: &mut gpui::TestAppContext) {
#[test]
fn test_cleaning_up_common_suffix() {
let old_text = concat!(
" for y in 0..size.y() {\n",
" let a = 10;\n",
@ -884,7 +883,7 @@ mod tests {
},
CharOperation::Keep { bytes: 1 },
];
let line_ops = char_ops_to_line_ops(old_text, &char_ops, cx.background_executor());
let line_ops = char_ops_to_line_ops(old_text, &char_ops);
assert_eq!(
line_ops,
vec![
@ -902,8 +901,8 @@ mod tests {
);
}
#[gpui::test]
fn test_random_diffs(cx: &mut gpui::TestAppContext) {
#[test]
fn test_random_diffs() {
random_test(|mut rng| {
let old_text_len = env::var("OLD_TEXT_LEN")
.map(|i| i.parse().expect("invalid `OLD_TEXT_LEN` variable"))
@ -923,19 +922,15 @@ mod tests {
assert_eq!(patched, new);
// Test char_ops_to_line_ops
let line_ops = char_ops_to_line_ops(&old, &char_operations, cx.background_executor());
let line_ops = char_ops_to_line_ops(&old, &char_operations);
println!("line operations: {:?}", line_ops);
let patched = apply_line_operations(&old, &new, &line_ops);
assert_eq!(patched, new);
});
}
fn char_ops_to_line_ops(
old_text: &str,
char_ops: &[CharOperation],
executor: &BackgroundExecutor,
) -> Vec<LineOperation> {
let old_rope = Rope::from_str(old_text, executor);
fn char_ops_to_line_ops(old_text: &str, char_ops: &[CharOperation]) -> Vec<LineOperation> {
let old_rope = Rope::from(old_text);
let mut diff = LineDiff::default();
for op in char_ops {
diff.push_char_operation(op, &old_rope);

View file

@ -15,12 +15,10 @@ doctest = false
[dependencies]
arrayvec = "0.7.1"
rayon.workspace = true
log.workspace = true
futures.workspace = true
futures-lite.workspace = true
[dev-dependencies]
ctor.workspace = true
rand.workspace = true
zlog.workspace = true
pollster = "0.4.0"

View file

@ -3,30 +3,17 @@ mod tree_map;
use arrayvec::ArrayVec;
pub use cursor::{Cursor, FilterCursor, Iter};
use futures::{StreamExt, stream};
use futures_lite::future::yield_now;
use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator as _};
use std::marker::PhantomData;
use std::mem;
use std::{cmp::Ordering, fmt, iter::FromIterator, sync::Arc};
pub use tree_map::{MapSeekTarget, TreeMap, TreeSet};
#[cfg(all(test, not(rust_analyzer)))]
#[cfg(test)]
pub const TREE_BASE: usize = 2;
#[cfg(not(all(test, not(rust_analyzer))))]
#[cfg(not(test))]
pub const TREE_BASE: usize = 6;
pub trait BackgroundSpawn {
type Task<R>: Future<Output = R> + Send + Sync
where
R: Send + Sync;
fn background_spawn<R>(
&self,
future: impl Future<Output = R> + Send + Sync + 'static,
) -> Self::Task<R>
where
R: Send + Sync + 'static;
}
/// An item that can be stored in a [`SumTree`]
///
/// Must be summarized by a type that implements [`Summary`]
@ -311,85 +298,62 @@ impl<T: Item> SumTree<T> {
}
}
pub async fn from_iter_async<I, S>(iter: I, spawn: S) -> Self
pub fn from_par_iter<I, Iter>(iter: I, cx: <T::Summary as Summary>::Context<'_>) -> Self
where
T: 'static + Send + Sync,
for<'a> T::Summary: Summary<Context<'a> = ()> + Send + Sync,
S: BackgroundSpawn,
I: IntoIterator<Item = T, IntoIter: ExactSizeIterator>,
I: IntoParallelIterator<Iter = Iter>,
Iter: IndexedParallelIterator<Item = T>,
T: Send + Sync,
T::Summary: Send + Sync,
for<'a> <T::Summary as Summary>::Context<'a>: Sync,
{
let iter = iter.into_iter();
let num_leaves = iter.len().div_ceil(2 * TREE_BASE);
if num_leaves == 0 {
return Self::new(());
}
let mut nodes = stream::iter(iter)
.chunks(num_leaves.div_ceil(4))
.map(|chunk| async move {
let mut chunk = chunk.into_iter();
let mut leaves = vec![];
loop {
let items: ArrayVec<T, { 2 * TREE_BASE }> =
chunk.by_ref().take(2 * TREE_BASE).collect();
if items.is_empty() {
break;
}
let item_summaries: ArrayVec<T::Summary, { 2 * TREE_BASE }> =
items.iter().map(|item| item.summary(())).collect();
let mut summary = item_summaries[0].clone();
for item_summary in &item_summaries[1..] {
<T::Summary as Summary>::add_summary(&mut summary, item_summary, ());
}
leaves.push(SumTree(Arc::new(Node::Leaf {
summary,
items,
item_summaries,
})));
yield_now().await;
let mut nodes = iter
.into_par_iter()
.chunks(2 * TREE_BASE)
.map(|items| {
let items: ArrayVec<T, { 2 * TREE_BASE }> = items.into_iter().collect();
let item_summaries: ArrayVec<T::Summary, { 2 * TREE_BASE }> =
items.iter().map(|item| item.summary(cx)).collect();
let mut summary = item_summaries[0].clone();
for item_summary in &item_summaries[1..] {
<T::Summary as Summary>::add_summary(&mut summary, item_summary, cx);
}
leaves
SumTree(Arc::new(Node::Leaf {
summary,
items,
item_summaries,
}))
})
.map(|future| spawn.background_spawn(future))
.buffered(4)
.flat_map(|it| stream::iter(it.into_iter()))
.collect::<Vec<_>>()
.await;
.collect::<Vec<_>>();
let mut height = 0;
while nodes.len() > 1 {
height += 1;
let current_nodes = mem::take(&mut nodes);
nodes = stream::iter(current_nodes)
nodes = nodes
.into_par_iter()
.chunks(2 * TREE_BASE)
.map(|chunk| {
spawn.background_spawn(async move {
let child_trees: ArrayVec<SumTree<T>, { 2 * TREE_BASE }> =
chunk.into_iter().collect();
let child_summaries: ArrayVec<T::Summary, { 2 * TREE_BASE }> = child_trees
.iter()
.map(|child_tree| child_tree.summary().clone())
.collect();
let mut summary = child_summaries[0].clone();
for child_summary in &child_summaries[1..] {
<T::Summary as Summary>::add_summary(&mut summary, child_summary, ());
}
SumTree(Arc::new(Node::Internal {
height,
summary,
child_summaries,
child_trees,
}))
})
.map(|child_nodes| {
let child_trees: ArrayVec<SumTree<T>, { 2 * TREE_BASE }> =
child_nodes.into_iter().collect();
let child_summaries: ArrayVec<T::Summary, { 2 * TREE_BASE }> = child_trees
.iter()
.map(|child_tree| child_tree.summary().clone())
.collect();
let mut summary = child_summaries[0].clone();
for child_summary in &child_summaries[1..] {
<T::Summary as Summary>::add_summary(&mut summary, child_summary, cx);
}
SumTree(Arc::new(Node::Internal {
height,
summary,
child_summaries,
child_trees,
}))
})
.buffered(4)
.collect::<Vec<_>>()
.await;
.collect::<Vec<_>>();
}
if nodes.is_empty() {
Self::new(())
Self::new(cx)
} else {
debug_assert_eq!(nodes.len(), 1);
nodes.pop().unwrap()
@ -633,15 +597,15 @@ impl<T: Item> SumTree<T> {
self.append(Self::from_iter(iter, cx), cx);
}
pub async fn async_extend<S, I>(&mut self, iter: I, spawn: S)
pub fn par_extend<I, Iter>(&mut self, iter: I, cx: <T::Summary as Summary>::Context<'_>)
where
S: BackgroundSpawn,
I: IntoIterator<Item = T, IntoIter: ExactSizeIterator>,
T: 'static + Send + Sync,
for<'b> T::Summary: Summary<Context<'b> = ()> + Send + Sync,
I: IntoParallelIterator<Iter = Iter>,
Iter: IndexedParallelIterator<Item = T>,
T: Send + Sync,
T::Summary: Send + Sync,
for<'a> <T::Summary as Summary>::Context<'a>: Sync,
{
let other = Self::from_iter_async(iter, spawn);
self.append(other.await, ());
self.append(Self::from_par_iter(iter, cx), cx);
}
pub fn push(&mut self, item: T, cx: <T::Summary as Summary>::Context<'_>) {
@ -1106,23 +1070,6 @@ mod tests {
#[test]
fn test_random() {
struct NoSpawn;
impl BackgroundSpawn for NoSpawn {
type Task<R>
= std::pin::Pin<Box<dyn Future<Output = R> + Sync + Send>>
where
R: Send + Sync;
fn background_spawn<R>(
&self,
future: impl Future<Output = R> + Send + Sync + 'static,
) -> Self::Task<R>
where
R: Send + Sync + 'static,
{
Box::pin(future)
}
}
let mut starting_seed = 0;
if let Ok(value) = std::env::var("SEED") {
starting_seed = value.parse().expect("invalid SEED variable");
@ -1140,7 +1087,7 @@ mod tests {
let rng = &mut rng;
let mut tree = SumTree::<u8>::default();
let count = rng.random_range(0..128);
let count = rng.random_range(0..10);
if rng.random() {
tree.extend(rng.sample_iter(StandardUniform).take(count), ());
} else {
@ -1148,13 +1095,13 @@ mod tests {
.sample_iter(StandardUniform)
.take(count)
.collect::<Vec<_>>();
pollster::block_on(tree.async_extend(items, NoSpawn));
tree.par_extend(items, ());
}
for _ in 0..num_operations {
let splice_end = rng.random_range(0..tree.extent::<Count>(()).0 + 1);
let splice_start = rng.random_range(0..splice_end + 1);
let count = rng.random_range(0..128);
let count = rng.random_range(0..10);
let tree_end = tree.extent::<Count>(());
let new_items = rng
.sample_iter(StandardUniform)
@ -1170,7 +1117,7 @@ mod tests {
if rng.random() {
new_tree.extend(new_items, ());
} else {
pollster::block_on(new_tree.async_extend(new_items, NoSpawn));
new_tree.par_extend(new_items, ());
}
cursor.seek(&Count(splice_end), Bias::Right);
new_tree.append(cursor.slice(&tree_end, Bias::Right), ());

View file

@ -28,7 +28,6 @@ rope.workspace = true
smallvec.workspace = true
sum_tree.workspace = true
util.workspace = true
gpui.workspace = true
[dev-dependencies]
collections = { workspace = true, features = ["test-support"] }

View file

@ -14,29 +14,24 @@ fn init_logger() {
zlog::init_test();
}
#[gpui::test]
fn test_edit(cx: &mut gpui::TestAppContext) {
let mut buffer = Buffer::new(
ReplicaId::LOCAL,
BufferId::new(1).unwrap(),
"abc",
cx.background_executor(),
);
#[test]
fn test_edit() {
let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "abc");
assert_eq!(buffer.text(), "abc");
buffer.edit([(3..3, "def")], cx.background_executor());
buffer.edit([(3..3, "def")]);
assert_eq!(buffer.text(), "abcdef");
buffer.edit([(0..0, "ghi")], cx.background_executor());
buffer.edit([(0..0, "ghi")]);
assert_eq!(buffer.text(), "ghiabcdef");
buffer.edit([(5..5, "jkl")], cx.background_executor());
buffer.edit([(5..5, "jkl")]);
assert_eq!(buffer.text(), "ghiabjklcdef");
buffer.edit([(6..7, "")], cx.background_executor());
buffer.edit([(6..7, "")]);
assert_eq!(buffer.text(), "ghiabjlcdef");
buffer.edit([(4..9, "mno")], cx.background_executor());
buffer.edit([(4..9, "mno")]);
assert_eq!(buffer.text(), "ghiamnoef");
}
#[gpui::test(iterations = 100)]
fn test_random_edits(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
fn test_random_edits(mut rng: StdRng) {
let operations = env::var("OPERATIONS")
.map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
.unwrap_or(10);
@ -49,7 +44,6 @@ fn test_random_edits(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
ReplicaId::LOCAL,
BufferId::new(1).unwrap(),
reference_string.clone(),
cx.background_executor(),
);
LineEnding::normalize(&mut reference_string);
@ -62,7 +56,7 @@ fn test_random_edits(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
);
for _i in 0..operations {
let (edits, _) = buffer.randomly_edit(&mut rng, 5, cx.background_executor());
let (edits, _) = buffer.randomly_edit(&mut rng, 5);
for (old_range, new_text) in edits.iter().rev() {
reference_string.replace_range(old_range.clone(), new_text);
}
@ -112,11 +106,7 @@ fn test_random_edits(cx: &mut gpui::TestAppContext, 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,
cx.background_executor(),
);
text.replace(edit.new.start..edit.new.start + edit.old.len(), &new_text);
}
assert_eq!(text.to_string(), buffer.text());
@ -171,18 +161,14 @@ fn test_random_edits(cx: &mut gpui::TestAppContext, 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,
cx.background_executor(),
);
text.replace(edit.new.start..edit.new.start + edit.old.len(), &new_text);
}
assert_eq!(text.to_string(), buffer.text());
}
}
#[gpui::test]
fn test_line_endings(cx: &mut gpui::TestAppContext) {
#[test]
fn test_line_endings() {
assert_eq!(LineEnding::detect(&"🍐✅\n".repeat(1000)), LineEnding::Unix);
assert_eq!(LineEnding::detect(&"abcd\n".repeat(1000)), LineEnding::Unix);
assert_eq!(
@ -198,34 +184,25 @@ fn test_line_endings(cx: &mut gpui::TestAppContext) {
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")],
cx.background_executor(),
);
buffer.edit([(0..0, "zero\r\n")], cx.background_executor());
buffer.edit([(buffer.len()..buffer.len(), "\r\nfour")]);
buffer.edit([(0..0, "zero\r\n")]);
assert_eq!(buffer.text(), "zero\none\ntwo\nthree\nfour");
assert_eq!(buffer.line_ending(), LineEnding::Windows);
buffer.check_invariants();
}
#[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());
#[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")]);
assert_eq!(buffer.line_len(0), 4);
assert_eq!(buffer.line_len(1), 3);
@ -235,15 +212,10 @@ fn test_line_len(cx: &mut gpui::TestAppContext) {
assert_eq!(buffer.line_len(5), 0);
}
#[gpui::test]
fn test_common_prefix_at_position(cx: &mut gpui::TestAppContext) {
#[test]
fn test_common_prefix_at_position() {
let text = "a = str; b = δα";
let buffer = Buffer::new(
ReplicaId::LOCAL,
BufferId::new(1).unwrap(),
text,
cx.background_executor(),
);
let buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), text);
let offset1 = offset_after(text, "str");
let offset2 = offset_after(text, "δα");
@ -289,13 +261,12 @@ fn test_common_prefix_at_position(cx: &mut gpui::TestAppContext) {
}
}
#[gpui::test]
fn test_text_summary_for_range(cx: &mut gpui::TestAppContext) {
#[test]
fn test_text_summary_for_range() {
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),
@ -383,18 +354,13 @@ fn test_text_summary_for_range(cx: &mut gpui::TestAppContext) {
);
}
#[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());
#[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")]);
let chars = buffer.chars_at(Point::new(0, 0));
assert_eq!(chars.collect::<String>(), "abcd\nefgh\nijkl\nmno\nPQrs");
@ -412,53 +378,43 @@ fn test_chars_at(cx: &mut gpui::TestAppContext) {
assert_eq!(chars.collect::<String>(), "PQrs");
// Regression test:
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 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 chars = buffer.chars_at(Point::new(6, 0));
assert_eq!(chars.collect::<String>(), " \"xray_wasm\",\n]\n");
}
#[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());
#[test]
fn test_anchors() {
let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "");
buffer.edit([(0..0, "abc")]);
let left_anchor = buffer.anchor_before(2);
let right_anchor = buffer.anchor_after(2);
buffer.edit([(1..1, "def\n")], cx.background_executor());
buffer.edit([(1..1, "def\n")]);
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, "")], cx.background_executor());
buffer.edit([(2..3, "")]);
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")], cx.background_executor());
buffer.edit([(5..5, "ghi\n")]);
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, "")], cx.background_executor());
buffer.edit([(7..9, "")]);
assert_eq!(buffer.text(), "adf\nbghc");
assert_eq!(left_anchor.to_offset(&buffer), 5);
assert_eq!(right_anchor.to_offset(&buffer), 7);
@ -548,18 +504,13 @@ fn test_anchors(cx: &mut gpui::TestAppContext) {
);
}
#[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(),
);
#[test]
fn test_anchors_at_start_and_end() {
let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "");
let before_start_anchor = buffer.anchor_before(0);
let after_end_anchor = buffer.anchor_after(0);
buffer.edit([(0..0, "abc")], cx.background_executor());
buffer.edit([(0..0, "abc")]);
assert_eq!(buffer.text(), "abc");
assert_eq!(before_start_anchor.to_offset(&buffer), 0);
assert_eq!(after_end_anchor.to_offset(&buffer), 3);
@ -567,8 +518,8 @@ fn test_anchors_at_start_and_end(cx: &mut gpui::TestAppContext) {
let after_start_anchor = buffer.anchor_after(0);
let before_end_anchor = buffer.anchor_before(3);
buffer.edit([(3..3, "def")], cx.background_executor());
buffer.edit([(0..0, "ghi")], cx.background_executor());
buffer.edit([(3..3, "def")]);
buffer.edit([(0..0, "ghi")]);
assert_eq!(buffer.text(), "ghiabcdef");
assert_eq!(before_start_anchor.to_offset(&buffer), 0);
assert_eq!(after_start_anchor.to_offset(&buffer), 3);
@ -576,20 +527,15 @@ fn test_anchors_at_start_and_end(cx: &mut gpui::TestAppContext) {
assert_eq!(after_end_anchor.to_offset(&buffer), 9);
}
#[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(),
);
#[test]
fn test_undo_redo() {
let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "1234");
// 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")], cx.background_executor());
buffer.edit([(3..4, "yzef")], cx.background_executor());
buffer.edit([(3..5, "cd")], cx.background_executor());
buffer.edit([(1..1, "abx")]);
buffer.edit([(3..4, "yzef")]);
buffer.edit([(3..5, "cd")]);
assert_eq!(buffer.text(), "1abcdef234");
let entries = buffer.history.undo_stack.clone();
@ -617,31 +563,26 @@ fn test_undo_redo(cx: &mut gpui::TestAppContext) {
assert_eq!(buffer.text(), "1234");
}
#[gpui::test]
fn test_history(cx: &mut gpui::TestAppContext) {
#[test]
fn test_history() {
let mut now = Instant::now();
let mut buffer = Buffer::new(
ReplicaId::LOCAL,
BufferId::new(1).unwrap(),
"123456",
cx.background_executor(),
);
let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "123456");
buffer.set_group_interval(Duration::from_millis(300));
let transaction_1 = buffer.start_transaction_at(now).unwrap();
buffer.edit([(2..4, "cd")], cx.background_executor());
buffer.edit([(2..4, "cd")]);
buffer.end_transaction_at(now);
assert_eq!(buffer.text(), "12cd56");
buffer.start_transaction_at(now);
buffer.edit([(4..5, "e")], cx.background_executor());
buffer.edit([(4..5, "e")]);
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")], cx.background_executor());
buffer.edit([(1..1, "b")], cx.background_executor());
buffer.edit([(0..1, "a")]);
buffer.edit([(1..1, "b")]);
buffer.end_transaction_at(now).unwrap();
assert_eq!(buffer.text(), "ab2cde6");
@ -668,7 +609,7 @@ fn test_history(cx: &mut gpui::TestAppContext) {
// Redo stack gets cleared after performing an edit.
buffer.start_transaction_at(now);
buffer.edit([(0..0, "X")], cx.background_executor());
buffer.edit([(0..0, "X")]);
buffer.end_transaction_at(now);
assert_eq!(buffer.text(), "X12cde6");
buffer.redo();
@ -689,31 +630,26 @@ fn test_history(cx: &mut gpui::TestAppContext) {
assert_eq!(buffer.text(), "X12cde6");
}
#[gpui::test]
fn test_finalize_last_transaction(cx: &mut gpui::TestAppContext) {
#[test]
fn test_finalize_last_transaction() {
let now = Instant::now();
let mut buffer = Buffer::new(
ReplicaId::LOCAL,
BufferId::new(1).unwrap(),
"123456",
cx.background_executor(),
);
let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "123456");
buffer.history.group_interval = Duration::from_millis(1);
buffer.start_transaction_at(now);
buffer.edit([(2..4, "cd")], cx.background_executor());
buffer.edit([(2..4, "cd")]);
buffer.end_transaction_at(now);
assert_eq!(buffer.text(), "12cd56");
buffer.finalize_last_transaction();
buffer.start_transaction_at(now);
buffer.edit([(4..5, "e")], cx.background_executor());
buffer.edit([(4..5, "e")]);
buffer.end_transaction_at(now).unwrap();
assert_eq!(buffer.text(), "12cde6");
buffer.start_transaction_at(now);
buffer.edit([(0..1, "a")], cx.background_executor());
buffer.edit([(1..1, "b")], cx.background_executor());
buffer.edit([(0..1, "a")]);
buffer.edit([(1..1, "b")]);
buffer.end_transaction_at(now).unwrap();
assert_eq!(buffer.text(), "ab2cde6");
@ -730,19 +666,14 @@ fn test_finalize_last_transaction(cx: &mut gpui::TestAppContext) {
assert_eq!(buffer.text(), "ab2cde6");
}
#[gpui::test]
fn test_edited_ranges_for_transaction(cx: &mut gpui::TestAppContext) {
#[test]
fn test_edited_ranges_for_transaction() {
let now = Instant::now();
let mut buffer = Buffer::new(
ReplicaId::LOCAL,
BufferId::new(1).unwrap(),
"1234567",
cx.background_executor(),
);
let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "1234567");
buffer.start_transaction_at(now);
buffer.edit([(2..4, "cd")], cx.background_executor());
buffer.edit([(6..6, "efg")], cx.background_executor());
buffer.edit([(2..4, "cd")]);
buffer.edit([(6..6, "efg")]);
buffer.end_transaction_at(now);
assert_eq!(buffer.text(), "12cd56efg7");
@ -754,7 +685,7 @@ fn test_edited_ranges_for_transaction(cx: &mut gpui::TestAppContext) {
[2..4, 6..9]
);
buffer.edit([(5..5, "hijk")], cx.background_executor());
buffer.edit([(5..5, "hijk")]);
assert_eq!(buffer.text(), "12cd5hijk6efg7");
assert_eq!(
buffer
@ -763,7 +694,7 @@ fn test_edited_ranges_for_transaction(cx: &mut gpui::TestAppContext) {
[2..4, 10..13]
);
buffer.edit([(4..4, "l")], cx.background_executor());
buffer.edit([(4..4, "l")]);
assert_eq!(buffer.text(), "12cdl5hijk6efg7");
assert_eq!(
buffer
@ -773,42 +704,27 @@ fn test_edited_ranges_for_transaction(cx: &mut gpui::TestAppContext) {
);
}
#[gpui::test]
fn test_concurrent_edits(cx: &mut gpui::TestAppContext) {
#[test]
fn test_concurrent_edits() {
let text = "abcdef";
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 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 buf1_op = buffer1.edit([(1..2, "12")], cx.background_executor());
let buf1_op = buffer1.edit([(1..2, "12")]);
assert_eq!(buffer1.text(), "a12cdef");
let buf2_op = buffer2.edit([(3..4, "34")], cx.background_executor());
let buf2_op = buffer2.edit([(3..4, "34")]);
assert_eq!(buffer2.text(), "abc34ef");
let buf3_op = buffer3.edit([(5..6, "56")], cx.background_executor());
let buf3_op = buffer3.edit([(5..6, "56")]);
assert_eq!(buffer3.text(), "abcde56");
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()));
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);
assert_eq!(buffer1.text(), "a12c34e56");
assert_eq!(buffer2.text(), "a12c34e56");
@ -816,7 +732,7 @@ fn test_concurrent_edits(cx: &mut gpui::TestAppContext) {
}
#[gpui::test(iterations = 100)]
fn test_random_concurrent_edits(mut rng: StdRng, cx: &mut gpui::TestAppContext) {
fn test_random_concurrent_edits(mut rng: StdRng) {
let peers = env::var("PEERS")
.map(|i| i.parse().expect("invalid `PEERS` variable"))
.unwrap_or(5);
@ -837,7 +753,6 @@ fn test_random_concurrent_edits(mut rng: StdRng, cx: &mut gpui::TestAppContext)
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);
@ -854,9 +769,7 @@ fn test_random_concurrent_edits(mut rng: StdRng, cx: &mut gpui::TestAppContext)
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, cx.background_executor())
.1;
let op = buffer.randomly_edit(&mut rng, 5).1;
network.broadcast(buffer.replica_id, vec![op]);
log::info!("buffer {:?} text: {:?}", buffer.replica_id, buffer.text());
mutation_count -= 1;
@ -874,7 +787,7 @@ fn test_random_concurrent_edits(mut rng: StdRng, cx: &mut gpui::TestAppContext)
replica_id,
ops.len()
);
buffer.apply_ops(ops, Some(cx.background_executor()));
buffer.apply_ops(ops);
}
}
_ => {}

View file

@ -15,7 +15,6 @@ 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;
@ -710,41 +709,11 @@ impl FromIterator<char> for LineIndent {
}
impl 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 {
pub fn new(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(&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),
)
Self::new_normalized(replica_id, remote_id, line_ending, Rope::from(&*base_text))
}
pub fn new_normalized(
@ -839,7 +808,7 @@ impl Buffer {
self.history.group_interval
}
pub fn edit<R, I, S, T>(&mut self, edits: R, cx: &BackgroundExecutor) -> Operation
pub fn edit<R, I, S, T>(&mut self, edits: R) -> Operation
where
R: IntoIterator<IntoIter = I>,
I: ExactSizeIterator<Item = (Range<S>, T)>,
@ -852,7 +821,7 @@ impl Buffer {
self.start_transaction();
let timestamp = self.lamport_clock.tick();
let operation = Operation::Edit(self.apply_local_edit(edits, timestamp, cx));
let operation = Operation::Edit(self.apply_local_edit(edits, timestamp));
self.history.push(operation.clone());
self.history.push_undo(operation.timestamp());
@ -865,7 +834,6 @@ 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 {
@ -954,7 +922,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(), executor);
new_ropes.push_str(new_text.as_ref());
new_fragments.push(fragment, &None);
insertion_offset += new_text.len();
}
@ -1033,26 +1001,22 @@ impl Buffer {
self.snapshot.line_ending = line_ending;
}
pub fn apply_ops<I: IntoIterator<Item = Operation>>(
&mut self,
ops: I,
executor: Option<&BackgroundExecutor>,
) {
pub fn apply_ops<I: IntoIterator<Item = Operation>>(&mut self, ops: I) {
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, executor);
self.apply_op(op);
} else {
self.deferred_replicas.insert(op.replica_id());
deferred_ops.push(op);
}
}
self.deferred_ops.insert(deferred_ops);
self.flush_deferred_ops(executor);
self.flush_deferred_ops();
}
fn apply_op(&mut self, op: Operation, executor: Option<&BackgroundExecutor>) {
fn apply_op(&mut self, op: Operation) {
match op {
Operation::Edit(edit) => {
if !self.version.observed(edit.timestamp) {
@ -1061,7 +1025,6 @@ impl Buffer {
&edit.ranges,
&edit.new_text,
edit.timestamp,
executor,
);
self.snapshot.version.observe(edit.timestamp);
self.lamport_clock.observe(edit.timestamp);
@ -1092,7 +1055,6 @@ impl Buffer {
ranges: &[Range<FullOffset>],
new_text: &[Arc<str>],
timestamp: clock::Lamport,
executor: Option<&BackgroundExecutor>,
) {
if ranges.is_empty() {
return;
@ -1208,10 +1170,7 @@ impl Buffer {
});
insertion_slices.push(InsertionSlice::from_fragment(timestamp, &fragment));
new_insertions.push(InsertionFragment::insert_new(&fragment));
match executor {
Some(executor) => new_ropes.push_str(new_text, executor),
None => new_ropes.push_str_small(new_text),
}
new_ropes.push_str(new_text);
new_fragments.push(fragment, &None);
insertion_offset += new_text.len();
}
@ -1389,12 +1348,12 @@ impl Buffer {
self.subscriptions.publish_mut(&edits);
}
fn flush_deferred_ops(&mut self, executor: Option<&BackgroundExecutor>) {
fn flush_deferred_ops(&mut self) {
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, executor);
self.apply_op(op);
} else {
self.deferred_replicas.insert(op.replica_id());
deferred_ops.push(op);
@ -1752,9 +1711,9 @@ impl Buffer {
#[cfg(any(test, feature = "test-support"))]
impl Buffer {
#[track_caller]
pub fn edit_via_marked_text(&mut self, marked_string: &str, cx: &BackgroundExecutor) {
pub fn edit_via_marked_text(&mut self, marked_string: &str) {
let edits = self.edits_for_marked_text(marked_string);
self.edit(edits, cx);
self.edit(edits);
}
#[track_caller]
@ -1891,7 +1850,6 @@ impl Buffer {
&mut self,
rng: &mut T,
edit_count: usize,
executor: &BackgroundExecutor,
) -> (Vec<(Range<usize>, Arc<str>)>, Operation)
where
T: rand::Rng,
@ -1899,7 +1857,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(), executor);
let op = self.edit(edits.iter().cloned());
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) {
@ -2748,12 +2706,8 @@ impl<'a> RopeBuilder<'a> {
}
}
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 push_str(&mut self, text: &str) {
self.new_visible.push(text);
}
fn finish(mut self) -> (Rope, Rope) {

View file

@ -3096,7 +3096,6 @@ mod test {
use indoc::indoc;
use language::Point;
use multi_buffer::MultiBufferRow;
use text::Rope;
#[gpui::test]
async fn test_start_end_of_paragraph(cx: &mut gpui::TestAppContext) {
@ -3823,7 +3822,7 @@ mod test {
cx.update_editor(|editor, _window, cx| {
let range = editor.selections.newest_anchor().range();
let inlay_text = " field: int,\n field2: string\n field3: float";
let inlay = Inlay::edit_prediction(1, range.start, Rope::from_str_small(inlay_text));
let inlay = Inlay::edit_prediction(1, range.start, inlay_text);
editor.splice_inlays(&[], vec![inlay], cx);
});
@ -3855,7 +3854,7 @@ mod test {
let end_of_line =
snapshot.anchor_after(Point::new(0, snapshot.line_len(MultiBufferRow(0))));
let inlay_text = " hint";
let inlay = Inlay::edit_prediction(1, end_of_line, Rope::from_str_small(inlay_text));
let inlay = Inlay::edit_prediction(1, end_of_line, inlay_text);
editor.splice_inlays(&[], vec![inlay], cx);
});
cx.simulate_keystrokes("$");
@ -3894,7 +3893,7 @@ mod test {
// The empty line is at line 3 (0-indexed)
let line_start = snapshot.anchor_after(Point::new(3, 0));
let inlay_text = ": Vec<u32>";
let inlay = Inlay::edit_prediction(1, line_start, Rope::from_str_small(inlay_text));
let inlay = Inlay::edit_prediction(1, line_start, inlay_text);
editor.splice_inlays(&[], vec![inlay], cx);
});
@ -3938,8 +3937,7 @@ mod test {
let snapshot = editor.buffer().read(cx).snapshot(cx);
let empty_line_start = snapshot.anchor_after(Point::new(2, 0));
let inlay_text = ": i32";
let inlay =
Inlay::edit_prediction(2, empty_line_start, Rope::from_str_small(inlay_text));
let inlay = Inlay::edit_prediction(2, empty_line_start, inlay_text);
editor.splice_inlays(&[], vec![inlay], cx);
});

View file

@ -7580,13 +7580,13 @@ pub fn create_and_open_local_file(
path: &'static Path,
window: &mut Window,
cx: &mut Context<Workspace>,
default_content: impl 'static + Send + FnOnce(&mut AsyncApp) -> Rope,
default_content: impl 'static + Send + FnOnce() -> Rope,
) -> Task<Result<Box<dyn ItemHandle>>> {
cx.spawn_in(window, async move |workspace, cx| {
let fs = workspace.read_with(cx, |workspace, _| workspace.app_state().fs.clone())?;
if !fs.is_file(path).await {
fs.create_file(path, Default::default()).await?;
fs.save(path, &default_content(cx), Default::default())
fs.save(path, &default_content(), Default::default())
.await?;
}

View file

@ -20,7 +20,6 @@ use std::{
path::{Path, PathBuf},
sync::Arc,
};
use text::Rope;
use util::{
ResultExt, path,
rel_path::{RelPath, rel_path},
@ -647,13 +646,9 @@ async fn test_dirs_no_longer_ignored(cx: &mut TestAppContext) {
// Update the gitignore so that node_modules is no longer ignored,
// but a subdirectory is ignored
fs.save(
"/root/.gitignore".as_ref(),
&Rope::from_str("e", cx.background_executor()),
Default::default(),
)
.await
.unwrap();
fs.save("/root/.gitignore".as_ref(), &"e".into(), Default::default())
.await
.unwrap();
cx.executor().run_until_parked();
// All of the directories that are no longer ignored are now loaded.
@ -721,7 +716,7 @@ async fn test_write_file(cx: &mut TestAppContext) {
.update(cx, |tree, cx| {
tree.write_file(
rel_path("tracked-dir/file.txt").into(),
Rope::from_str("hello", cx.background_executor()),
"hello".into(),
Default::default(),
cx,
)
@ -732,7 +727,7 @@ async fn test_write_file(cx: &mut TestAppContext) {
.update(cx, |tree, cx| {
tree.write_file(
rel_path("ignored-dir/file.txt").into(),
Rope::from_str("world", cx.background_executor()),
"world".into(),
Default::default(),
cx,
)
@ -1470,7 +1465,7 @@ async fn test_random_worktree_operations_during_initial_scan(
let fs = FakeFs::new(cx.background_executor.clone()) as Arc<dyn Fs>;
fs.as_fake().insert_tree(root_dir, json!({})).await;
for _ in 0..initial_entries {
randomly_mutate_fs(&fs, root_dir, 1.0, &mut rng, cx.background_executor()).await;
randomly_mutate_fs(&fs, root_dir, 1.0, &mut rng).await;
}
log::info!("generated initial tree");
@ -1560,7 +1555,7 @@ async fn test_random_worktree_changes(cx: &mut TestAppContext, mut rng: StdRng)
let fs = FakeFs::new(cx.background_executor.clone()) as Arc<dyn Fs>;
fs.as_fake().insert_tree(root_dir, json!({})).await;
for _ in 0..initial_entries {
randomly_mutate_fs(&fs, root_dir, 1.0, &mut rng, cx.background_executor()).await;
randomly_mutate_fs(&fs, root_dir, 1.0, &mut rng).await;
}
log::info!("generated initial tree");
@ -1603,7 +1598,7 @@ async fn test_random_worktree_changes(cx: &mut TestAppContext, mut rng: StdRng)
.await
.log_err();
} else {
randomly_mutate_fs(&fs, root_dir, 1.0, &mut rng, cx.background_executor()).await;
randomly_mutate_fs(&fs, root_dir, 1.0, &mut rng).await;
}
let buffered_event_count = fs.as_fake().buffered_event_count();
@ -1612,7 +1607,7 @@ async fn test_random_worktree_changes(cx: &mut TestAppContext, mut rng: StdRng)
log::info!("flushing {} events", len);
fs.as_fake().flush_events(len);
} else {
randomly_mutate_fs(&fs, root_dir, 0.6, &mut rng, cx.background_executor()).await;
randomly_mutate_fs(&fs, root_dir, 0.6, &mut rng).await;
mutations_len -= 1;
}
@ -1764,12 +1759,8 @@ fn randomly_mutate_worktree(
})
} else {
log::info!("overwriting file {:?} ({})", &entry.path, entry.id.0);
let task = worktree.write_file(
entry.path.clone(),
Rope::default(),
Default::default(),
cx,
);
let task =
worktree.write_file(entry.path.clone(), "".into(), Default::default(), cx);
cx.background_spawn(async move {
task.await?;
Ok(())
@ -1784,7 +1775,6 @@ async fn randomly_mutate_fs(
root_path: &Path,
insertion_probability: f64,
rng: &mut impl Rng,
executor: &BackgroundExecutor,
) {
log::info!("mutating fs");
let mut files = Vec::new();
@ -1859,7 +1849,7 @@ async fn randomly_mutate_fs(
);
fs.save(
&ignore_path,
&Rope::from_str(ignore_contents.as_str(), executor),
&ignore_contents.as_str().into(),
Default::default(),
)
.await

View file

@ -28,10 +28,10 @@ use git_ui::commit_view::CommitViewToolbar;
use git_ui::git_panel::GitPanel;
use git_ui::project_diff::ProjectDiffToolbar;
use gpui::{
Action, App, AppContext as _, AsyncApp, Context, DismissEvent, Element, Entity, Focusable,
KeyBinding, ParentElement, PathPromptOptions, PromptLevel, ReadGlobal, SharedString, Styled,
Task, TitlebarOptions, UpdateGlobal, Window, WindowKind, WindowOptions, actions, image_cache,
point, px, retain_all,
Action, App, AppContext as _, Context, DismissEvent, Element, Entity, Focusable, KeyBinding,
ParentElement, PathPromptOptions, PromptLevel, ReadGlobal, SharedString, Styled, Task,
TitlebarOptions, UpdateGlobal, Window, WindowKind, WindowOptions, actions, image_cache, point,
px, retain_all,
};
use image_viewer::ImageInfo;
use language::Capability;
@ -201,12 +201,7 @@ pub fn init(cx: &mut App) {
with_active_or_new_workspace(cx, |_, window, cx| {
open_settings_file(
paths::keymap_file(),
|cx| {
Rope::from_str(
settings::initial_keymap_content().as_ref(),
cx.background_executor(),
)
},
|| settings::initial_keymap_content().as_ref().into(),
window,
cx,
);
@ -216,12 +211,7 @@ pub fn init(cx: &mut App) {
with_active_or_new_workspace(cx, |_, window, cx| {
open_settings_file(
paths::settings_file(),
|cx| {
Rope::from_str(
settings::initial_user_settings_content().as_ref(),
cx.background_executor(),
)
},
|| settings::initial_user_settings_content().as_ref().into(),
window,
cx,
);
@ -236,12 +226,7 @@ pub fn init(cx: &mut App) {
with_active_or_new_workspace(cx, |_, window, cx| {
open_settings_file(
paths::tasks_file(),
|cx| {
Rope::from_str(
settings::initial_tasks_content().as_ref(),
cx.background_executor(),
)
},
|| settings::initial_tasks_content().as_ref().into(),
window,
cx,
);
@ -251,12 +236,7 @@ pub fn init(cx: &mut App) {
with_active_or_new_workspace(cx, |_, window, cx| {
open_settings_file(
paths::debug_scenarios_file(),
|cx| {
Rope::from_str(
settings::initial_debug_tasks_content().as_ref(),
cx.background_executor(),
)
},
|| settings::initial_debug_tasks_content().as_ref().into(),
window,
cx,
);
@ -1959,7 +1939,7 @@ fn open_bundled_file(
fn open_settings_file(
abs_path: &'static Path,
default_content: impl FnOnce(&mut AsyncApp) -> Rope + Send + 'static,
default_content: impl FnOnce() -> Rope + Send + 'static,
window: &mut Window,
cx: &mut Context<Workspace>,
) {
@ -4375,7 +4355,7 @@ mod tests {
.fs
.save(
"/settings.json".as_ref(),
&Rope::from_str_small(r#"{"base_keymap": "Atom"}"#),
&r#"{"base_keymap": "Atom"}"#.into(),
Default::default(),
)
.await
@ -4385,7 +4365,7 @@ mod tests {
.fs
.save(
"/keymap.json".as_ref(),
&Rope::from_str_small(r#"[{"bindings": {"backspace": "test_only::ActionA"}}]"#),
&r#"[{"bindings": {"backspace": "test_only::ActionA"}}]"#.into(),
Default::default(),
)
.await
@ -4433,7 +4413,7 @@ mod tests {
.fs
.save(
"/keymap.json".as_ref(),
&Rope::from_str_small(r#"[{"bindings": {"backspace": "test_only::ActionB"}}]"#),
&r#"[{"bindings": {"backspace": "test_only::ActionB"}}]"#.into(),
Default::default(),
)
.await
@ -4453,7 +4433,7 @@ mod tests {
.fs
.save(
"/settings.json".as_ref(),
&Rope::from_str_small(r#"{"base_keymap": "JetBrains"}"#),
&r#"{"base_keymap": "JetBrains"}"#.into(),
Default::default(),
)
.await
@ -4493,7 +4473,7 @@ mod tests {
.fs
.save(
"/settings.json".as_ref(),
&Rope::from_str_small(r#"{"base_keymap": "Atom"}"#),
&r#"{"base_keymap": "Atom"}"#.into(),
Default::default(),
)
.await
@ -4502,7 +4482,7 @@ mod tests {
.fs
.save(
"/keymap.json".as_ref(),
&Rope::from_str_small(r#"[{"bindings": {"backspace": "test_only::ActionA"}}]"#),
&r#"[{"bindings": {"backspace": "test_only::ActionA"}}]"#.into(),
Default::default(),
)
.await
@ -4545,7 +4525,7 @@ mod tests {
.fs
.save(
"/keymap.json".as_ref(),
&Rope::from_str_small(r#"[{"bindings": {"backspace": null}}]"#),
&r#"[{"bindings": {"backspace": null}}]"#.into(),
Default::default(),
)
.await
@ -4565,7 +4545,7 @@ mod tests {
.fs
.save(
"/settings.json".as_ref(),
&Rope::from_str_small(r#"{"base_keymap": "JetBrains"}"#),
&r#"{"base_keymap": "JetBrains"}"#.into(),
Default::default(),
)
.await

View file

@ -861,7 +861,7 @@ mod tests {
.fs
.save(
Path::new(file1_path),
&Rope::from_str("content1", cx.background_executor()),
&Rope::from("content1"),
LineEnding::Unix,
)
.await
@ -875,7 +875,7 @@ mod tests {
.fs
.save(
Path::new(file2_path),
&Rope::from_str("content2", cx.background_executor()),
&Rope::from("content2"),
LineEnding::Unix,
)
.await

View file

@ -1836,13 +1836,12 @@ mod tests {
let fs = project::FakeFs::new(cx.executor());
let project = Project::test(fs.clone(), [], cx).await;
let buffer = cx.new(|cx| {
let buffer = cx.new(|_cx| {
Buffer::remote(
language::BufferId::new(1).unwrap(),
ReplicaId::new(1),
language::Capability::ReadWrite,
"fn main() {\n println!(\"Hello\");\n}",
cx.background_executor(),
)
});