History

sum_tree: Replace rayon with futures (#41586)

Release Notes: - N/A *or* Added/Fixed/Improved ... Co-authored by: Kate <kate@zed.dev>

Lukas Wirth committed 1 year ago

Commit f2ce06c1 parent 7c29c6d67 files · +1271 −640Browse files

Unified · Split

67 files
  1. Cargo.lock +11 −3
  2. crates/acp_thread/src/diff.rs +3 −1
  3. crates/action_log/src/action_log.rs +15 −3
  4. crates/agent/src/edit_agent/streaming_fuzzy_matcher.rs +47 −22
  5. crates/agent/src/tools/edit_file_tool.rs +3 −2
  6. crates/agent_ui/src/agent_configuration.rs +6 −2
  7. crates/agent_ui/src/buffer_codegen.rs +4 −3
  8. crates/assistant_text_thread/src/text_thread.rs +2 −1
  9. crates/buffer_diff/src/buffer_diff.rs +47 −11
  10. crates/channel/src/channel_buffer.rs +1 −0
  11. crates/collab/src/db/queries/buffers.rs +2 −2
  12. crates/collab/src/db/tests/buffer_tests.rs +36 −20
  13. crates/collab/src/tests/integration_tests.rs +2 −2
  14. crates/collab/src/tests/random_project_collaboration_tests.rs +6 −1
  15. crates/diagnostics/src/diagnostics_tests.rs +2 −2
  16. crates/edit_prediction_button/src/edit_prediction_button.rs +6 −3
  17. crates/editor/src/display_map.rs +2 −1
  18. crates/editor/src/display_map/inlay_map.rs +24 −12
  19. crates/editor/src/display_map/tab_map.rs +1 −1
  20. crates/editor/src/display_map/wrap_map.rs +5 −4
  21. crates/editor/src/editor.rs +1 −1
  22. crates/editor/src/git/blame.rs +3 −2
  23. crates/editor/src/inlays.rs +9 −9
  24. crates/editor/src/movement.rs +5 −4
  25. crates/editor/src/signature_help.rs +1 −1
  26. crates/extension_host/src/extension_host.rs +10 −5
  27. crates/git_ui/src/commit_view.rs +6 −3
  28. crates/git_ui/src/file_diff_view.rs +5 −6
  29. crates/gpui/src/app/async_context.rs +13 −0
  30. crates/gpui/src/app/test_context.rs +5 −0
  31. crates/gpui/src/executor.rs +1 −1
  32. crates/keymap_editor/src/keymap_editor.rs +2 −2
  33. crates/language/src/buffer.rs +14 −7
  34. crates/language/src/buffer_tests.rs +29 −10
  35. crates/language/src/syntax_map/syntax_map_tests.rs +33 −12
  36. crates/language_extension/src/extension_lsp_adapter.rs +5 −2
  37. crates/languages/src/c.rs +6 −4
  38. crates/languages/src/go.rs +15 −9
  39. crates/languages/src/python.rs +4 −3
  40. crates/languages/src/rust.rs +9 −6
  41. crates/markdown/src/markdown.rs +3 −1
  42. crates/markdown_preview/src/markdown_parser.rs +1 −1
  43. crates/multi_buffer/src/multi_buffer_tests.rs +19 −14
  44. crates/project/src/buffer_store.rs +15 −3
  45. crates/project/src/git_store/conflict_set.rs +32 −12
  46. crates/project/src/prettier_store.rs +91 −48
  47. crates/project/src/project.rs +10 −3
  48. crates/project/src/project_tests.rs +9 −9
  49. crates/remote_server/src/remote_editing_tests.rs +4 −4
  50. crates/rich_text/src/rich_text.rs +12 −5
  51. crates/rope/Cargo.toml +1 −1
  52. crates/rope/benches/rope_benchmark.rs +27 −10
  53. crates/rope/src/rope.rs +178 −98
  54. crates/rules_library/src/rules_library.rs +8 −2
  55. crates/streaming_diff/Cargo.toml +1 −0
  56. crates/streaming_diff/src/streaming_diff.rs +52 −47
  57. crates/sum_tree/Cargo.toml +3 −1
  58. crates/sum_tree/src/sum_tree.rs +85 −46
  59. crates/text/Cargo.toml +1 −0
  60. crates/text/src/tests.rs +184 −97
  61. crates/text/src/text.rs +63 −17
  62. crates/vim/src/motion.rs +6 −4
  63. crates/workspace/src/workspace.rs +2 −2
  64. crates/worktree/src/worktree_tests.rs +22 −12
  65. crates/zed/src/zed.rs +37 −17
  66. crates/zed/src/zed/open_listener.rs +2 −2
  67. crates/zeta/src/zeta.rs +2 −1
Cargo.lock+11−3
@@ -12711,6 +12711,12 @@ version = "0.2.5" · 12710 unmodified lines
1271112711 source = "registry+https://github.com/rust-lang/crates.io-index"
1271212712 checksum = "5da3b0203fd7ee5720aa0b5e790b591aa5d3f41c3ed2c34a3a393382198af2f7"
1271312713
12714+[[package]]
12715+name = "pollster"
12716+version = "0.4.0"
12717+source = "registry+https://github.com/rust-lang/crates.io-index"
12718+checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3"
12719+
1271412720 [[package]]
1271512721 name = "portable-atomic"
1271612722 version = "1.11.1"
@@ -12759,7 +12765,7 @@ dependencies = [ · 42 unmodified lines
1275912765 "log",
1276012766 "parking_lot",
1276112767 "pin-project",
12762− "pollster",
12768+ "pollster 0.2.5",
1276312769 "static_assertions",
1276412770 "thiserror 1.0.69",
1276512771 ]
@@ -14311,7 +14317,6 @@ dependencies = [ · 1545 unmodified lines
1431114317 "gpui",
1431214318 "log",
1431314319 "rand 0.9.2",
14314− "rayon",
1431514320 "sum_tree",
1431614321 "unicode-segmentation",
1431714322 "util",
@@ -16237,6 +16242,7 @@ checksum = "2b2231b7c3057d5e4ad0156fb3dc807d900806020c5ffa3ee6ff2c8c76fb8520" · 1919 unmodified lines
1623716242 name = "streaming_diff"
1623816243 version = "0.1.0"
1623916244 dependencies = [
16245+ "gpui",
1624016246 "ordered-float 2.10.1",
1624116247 "rand 0.9.2",
1624216248 "rope",
@@ -16355,9 +16361,11 @@ version = "0.1.0" · 112 unmodified lines
1635516361 dependencies = [
1635616362 "arrayvec",
1635716363 "ctor",
16364+ "futures 0.3.31",
16365+ "itertools 0.14.0",
1635816366 "log",
16367+ "pollster 0.4.0",
1635916368 "rand 0.9.2",
16360− "rayon",
1636116369 "zlog",
1636216370 ]
1636316371
crates/acp_thread/src/diff.rs+3−1
@@ -361,10 +361,12 @@ async fn build_buffer_diff( · 360 unmodified lines
361361 ) -> Result<Entity<BufferDiff>> {
362362 let buffer = cx.update(|cx| buffer.read(cx).snapshot())?;
363363
364+ let executor = cx.background_executor().clone();
364365 let old_text_rope = cx
365366 .background_spawn({
366367 let old_text = old_text.clone();
367− async move { Rope::from(old_text.as_str()) }
368+ let executor = executor.clone();
369+ async move { Rope::from_str(old_text.as_str(), &executor) }
368370 })
369371 .await;
370372 let base_buffer = cx
crates/action_log/src/action_log.rs+15−3
@@ -3,7 +3,9 @@ use buffer_diff::BufferDiff; · 2 unmodified lines
33 use clock;
44 use collections::BTreeMap;
55 use futures::{FutureExt, StreamExt, channel::mpsc};
6−use gpui::{App, AppContext, AsyncApp, Context, Entity, Subscription, Task, WeakEntity};
6+use gpui::{
7+ App, AppContext, AsyncApp, BackgroundExecutor, Context, Entity, Subscription, Task, WeakEntity,
8+};
79 use language::{Anchor, Buffer, BufferEvent, DiskState, Point, ToPoint};
810 use project::{Project, ProjectItem, lsp_store::OpenLspBufferHandle};
911 use std::{cmp, ops::Range, sync::Arc};
@@ -321,6 +323,7 @@ impl ActionLog { · 311 unmodified lines
321323 let unreviewed_edits = tracked_buffer.unreviewed_edits.clone();
322324 let edits = diff_snapshots(&old_snapshot, &new_snapshot);
323325 let mut has_user_changes = false;
326+ let executor = cx.background_executor().clone();
324327 async move {
325328 if let ChangeAuthor::User = author {
326329 has_user_changes = apply_non_conflicting_edits(
@@ -328,6 +331,7 @@ impl ActionLog { · 1 unmodified line
328331 edits,
329332 &mut base_text,
330333 new_snapshot.as_rope(),
334+ &executor,
331335 );
332336 }
333337
@@ -382,6 +386,7 @@ impl ActionLog { · 48 unmodified lines
382386 let agent_diff_base = tracked_buffer.diff_base.clone();
383387 let git_diff_base = git_diff.read(cx).base_text().as_rope().clone();
384388 let buffer_text = tracked_buffer.snapshot.as_rope().clone();
389+ let executor = cx.background_executor().clone();
385390 anyhow::Ok(cx.background_spawn(async move {
386391 let mut old_unreviewed_edits = old_unreviewed_edits.into_iter().peekable();
387392 let committed_edits = language::line_diff(
@@ -416,8 +421,11 @@ impl ActionLog { · 28 unmodified lines
416421 ),
417422 new_agent_diff_base.max_point(),
418423 ));
419− new_agent_diff_base
420− .replace(old_byte_start..old_byte_end, &unreviewed_new);
424+ new_agent_diff_base.replace(
425+ old_byte_start..old_byte_end,
426+ &unreviewed_new,
427+ &executor,
428+ );
421429 row_delta +=
422430 unreviewed.new_len() as i32 - unreviewed.old_len() as i32;
423431 }
@@ -611,6 +619,7 @@ impl ActionLog { · 187 unmodified lines
611619 .snapshot
612620 .text_for_range(new_range)
613621 .collect::<String>(),
622+ cx.background_executor(),
614623 );
615624 delta += edit.new_len() as i32 - edit.old_len() as i32;
616625 false
@@ -824,6 +833,7 @@ fn apply_non_conflicting_edits( · 207 unmodified lines
824833 edits: Vec<Edit<u32>>,
825834 old_text: &mut Rope,
826835 new_text: &Rope,
836+ executor: &BackgroundExecutor,
827837 ) -> bool {
828838 let mut old_edits = patch.edits().iter().cloned().peekable();
829839 let mut new_edits = edits.into_iter().peekable();
@@ -877,6 +887,7 @@ fn apply_non_conflicting_edits( · 47 unmodified lines
877887 old_text.replace(
878888 old_bytes,
879889 &new_text.chunks_in_range(new_bytes).collect::<String>(),
890+ executor,
880891 );
881892 applied_delta += new_edit.new_len() as i32 - new_edit.old_len() as i32;
882893 has_made_changes = true;
@@ -2282,6 +2293,7 @@ mod tests { · 1399 unmodified lines
22822293 old_text.replace(
22832294 old_start..old_end,
22842295 &new_text.slice_rows(edit.new.clone()).to_string(),
2296+ cx.background_executor(),
22852297 );
22862298 }
22872299 pretty_assertions::assert_eq!(old_text.to_string(), new_text.to_string());
crates/agent/src/edit_agent/streaming_fuzzy_matcher.rs+47−22
@@ -305,18 +305,20 @@ impl SearchMatrix { · 304 unmodified lines
305305 #[cfg(test)]
306306 mod tests {
307307 use super::*;
308+ use gpui::TestAppContext;
308309 use indoc::indoc;
309310 use language::{BufferId, TextBuffer};
310311 use rand::prelude::*;
311312 use text::ReplicaId;
312313 use util::test::{generate_marked_text, marked_text_ranges};
313314
314− #[test]
315− fn test_empty_query() {
315+ #[gpui::test]
316+ fn test_empty_query(cx: &mut gpui::TestAppContext) {
316317 let buffer = TextBuffer::new(
317318 ReplicaId::LOCAL,
318319 BufferId::new(1).unwrap(),
319320 "Hello world\nThis is a test\nFoo bar baz",
321+ cx.background_executor(),
320322 );
321323 let snapshot = buffer.snapshot();
322324
@@ -325,12 +327,13 @@ mod tests { · 2 unmodified lines
325327 assert_eq!(finish(finder), None);
326328 }
327329
328− #[test]
329− fn test_streaming_exact_match() {
330+ #[gpui::test]
331+ fn test_streaming_exact_match(cx: &mut gpui::TestAppContext) {
330332 let buffer = TextBuffer::new(
331333 ReplicaId::LOCAL,
332334 BufferId::new(1).unwrap(),
333335 "Hello world\nThis is a test\nFoo bar baz",
336+ cx.background_executor(),
334337 );
335338 let snapshot = buffer.snapshot();
336339
@@ -349,8 +352,8 @@ mod tests { · 12 unmodified lines
349352 assert_eq!(finish(finder), Some("This is a test".to_string()));
350353 }
351354
352− #[test]
353− fn test_streaming_fuzzy_match() {
355+ #[gpui::test]
356+ fn test_streaming_fuzzy_match(cx: &mut gpui::TestAppContext) {
354357 let buffer = TextBuffer::new(
355358 ReplicaId::LOCAL,
356359 BufferId::new(1).unwrap(),
@@ -363,6 +366,7 @@ mod tests { · 6 unmodified lines
363366 return x * y;
364367 }
365368 "},
369+ cx.background_executor(),
366370 );
367371 let snapshot = buffer.snapshot();
368372
@@ -383,12 +387,13 @@ mod tests { · 14 unmodified lines
383387 );
384388 }
385389
386− #[test]
387− fn test_incremental_improvement() {
390+ #[gpui::test]
391+ fn test_incremental_improvement(cx: &mut gpui::TestAppContext) {
388392 let buffer = TextBuffer::new(
389393 ReplicaId::LOCAL,
390394 BufferId::new(1).unwrap(),
391395 "Line 1\nLine 2\nLine 3\nLine 4\nLine 5",
396+ cx.background_executor(),
392397 );
393398 let snapshot = buffer.snapshot();
394399
@@ -408,8 +413,8 @@ mod tests { · 13 unmodified lines
408413 assert_eq!(finish(finder), Some("Line 3\nLine 4".to_string()));
409414 }
410415
411− #[test]
412− fn test_incomplete_lines_buffering() {
416+ #[gpui::test]
417+ fn test_incomplete_lines_buffering(cx: &mut gpui::TestAppContext) {
413418 let buffer = TextBuffer::new(
414419 ReplicaId::LOCAL,
415420 BufferId::new(1).unwrap(),
@@ -418,6 +423,7 @@ mod tests { · 2 unmodified lines
418423 jumps over the lazy dog
419424 Pack my box with five dozen liquor jugs
420425 "},
426+ cx.background_executor(),
421427 );
422428 let snapshot = buffer.snapshot();
423429
@@ -435,8 +441,8 @@ mod tests { · 11 unmodified lines
435441 );
436442 }
437443
438− #[test]
439− fn test_multiline_fuzzy_match() {
444+ #[gpui::test]
445+ fn test_multiline_fuzzy_match(cx: &mut gpui::TestAppContext) {
440446 let buffer = TextBuffer::new(
441447 ReplicaId::LOCAL,
442448 BufferId::new(1).unwrap(),
@@ -456,6 +462,7 @@ mod tests { · 13 unmodified lines
456462 }
457463 }
458464 "#},
465+ cx.background_executor(),
459466 );
460467 let snapshot = buffer.snapshot();
461468
@@ -509,7 +516,7 @@ mod tests { · 47 unmodified lines
509516 }
510517
511518 #[gpui::test(iterations = 100)]
512− fn test_resolve_location_single_line(mut rng: StdRng) {
519+ fn test_resolve_location_single_line(mut rng: StdRng, cx: &mut TestAppContext) {
513520 assert_location_resolution(
514521 concat!(
515522 " Lorem\n",
@@ -519,11 +526,12 @@ mod tests { · 3 unmodified lines
519526 ),
520527 "ipsum",
521528 &mut rng,
529+ cx,
522530 );
523531 }
524532
525533 #[gpui::test(iterations = 100)]
526− fn test_resolve_location_multiline(mut rng: StdRng) {
534+ fn test_resolve_location_multiline(mut rng: StdRng, cx: &mut TestAppContext) {
527535 assert_location_resolution(
528536 concat!(
529537 " Lorem\n",
@@ -533,11 +541,12 @@ mod tests { · 3 unmodified lines
533541 ),
534542 "ipsum\ndolor sit amet",
535543 &mut rng,
544+ cx,
536545 );
537546 }
538547
539548 #[gpui::test(iterations = 100)]
540− fn test_resolve_location_function_with_typo(mut rng: StdRng) {
549+ fn test_resolve_location_function_with_typo(mut rng: StdRng, cx: &mut TestAppContext) {
541550 assert_location_resolution(
542551 indoc! {"
543552 «fn foo1(a: usize) -> usize {
@@ -550,11 +559,12 @@ mod tests { · 6 unmodified lines
550559 "},
551560 "fn foo1(a: usize) -> u32 {\n40\n}",
552561 &mut rng,
562+ cx,
553563 );
554564 }
555565
556566 #[gpui::test(iterations = 100)]
557− fn test_resolve_location_class_methods(mut rng: StdRng) {
567+ fn test_resolve_location_class_methods(mut rng: StdRng, cx: &mut TestAppContext) {
558568 assert_location_resolution(
559569 indoc! {"
560570 class Something {
@@ -575,11 +585,12 @@ mod tests { · 14 unmodified lines
575585 six() { return 6666; }
576586 "},
577587 &mut rng,
588+ cx,
578589 );
579590 }
580591
581592 #[gpui::test(iterations = 100)]
582− fn test_resolve_location_imports_no_match(mut rng: StdRng) {
593+ fn test_resolve_location_imports_no_match(mut rng: StdRng, cx: &mut TestAppContext) {
583594 assert_location_resolution(
584595 indoc! {"
585596 use std::ops::Range;
@@ -609,11 +620,12 @@ mod tests { · 23 unmodified lines
609620 use std::sync::Arc;
610621 "},
611622 &mut rng,
623+ cx,
612624 );
613625 }
614626
615627 #[gpui::test(iterations = 100)]
616− fn test_resolve_location_nested_closure(mut rng: StdRng) {
628+ fn test_resolve_location_nested_closure(mut rng: StdRng, cx: &mut TestAppContext) {
617629 assert_location_resolution(
618630 indoc! {"
619631 impl Foo {
@@ -641,11 +653,12 @@ mod tests { · 21 unmodified lines
641653 " });",
642654 ),
643655 &mut rng,
656+ cx,
644657 );
645658 }
646659
647660 #[gpui::test(iterations = 100)]
648− fn test_resolve_location_tool_invocation(mut rng: StdRng) {
661+ fn test_resolve_location_tool_invocation(mut rng: StdRng, cx: &mut TestAppContext) {
649662 assert_location_resolution(
650663 indoc! {r#"
651664 let tool = cx
@@ -673,11 +686,12 @@ mod tests { · 21 unmodified lines
673686 " .output;",
674687 ),
675688 &mut rng,
689+ cx,
676690 );
677691 }
678692
679693 #[gpui::test]
680− fn test_line_hint_selection() {
694+ fn test_line_hint_selection(cx: &mut TestAppContext) {
681695 let text = indoc! {r#"
682696 fn first_function() {
683697 return 42;
@@ -696,6 +710,7 @@ mod tests { · 12 unmodified lines
696710 ReplicaId::LOCAL,
697711 BufferId::new(1).unwrap(),
698712 text.to_string(),
713+ cx.background_executor(),
699714 );
700715 let snapshot = buffer.snapshot();
701716 let mut matcher = StreamingFuzzyMatcher::new(snapshot.clone());
@@ -727,9 +742,19 @@ mod tests { · 25 unmodified lines
727742 }
728743
729744 #[track_caller]
730− fn assert_location_resolution(text_with_expected_range: &str, query: &str, rng: &mut StdRng) {
745+ fn assert_location_resolution(
746+ text_with_expected_range: &str,
747+ query: &str,
748+ rng: &mut StdRng,
749+ cx: &mut TestAppContext,
750+ ) {
731751 let (text, expected_ranges) = marked_text_ranges(text_with_expected_range, false);
732− let buffer = TextBuffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), text.clone());
752+ let buffer = TextBuffer::new(
753+ ReplicaId::LOCAL,
754+ BufferId::new(1).unwrap(),
755+ text.clone(),
756+ cx.background_executor(),
757+ );
733758 let snapshot = buffer.snapshot();
734759
735760 let mut matcher = StreamingFuzzyMatcher::new(snapshot);
crates/agent/src/tools/edit_file_tool.rs+3−2
@@ -569,6 +569,7 @@ mod tests { · 568 unmodified lines
569569 use prompt_store::ProjectContext;
570570 use serde_json::json;
571571 use settings::SettingsStore;
572+ use text::Rope;
572573 use util::{path, rel_path::rel_path};
573574
574575 #[gpui::test]
@@ -741,7 +742,7 @@ mod tests { · 166 unmodified lines
741742 // Create the file
742743 fs.save(
743744 path!("/root/src/main.rs").as_ref(),
744− &"initial content".into(),
745+ &Rope::from_str_small("initial content"),
745746 language::LineEnding::Unix,
746747 )
747748 .await
@@ -908,7 +909,7 @@ mod tests { · 160 unmodified lines
908909 // Create a simple file with trailing whitespace
909910 fs.save(
910911 path!("/root/src/main.rs").as_ref(),
911− &"initial content".into(),
912+ &Rope::from_str_small("initial content"),
912913 language::LineEnding::Unix,
913914 )
914915 .await
crates/agent_ui/src/agent_configuration.rs+6−2
@@ -28,6 +28,7 @@ use project::{ · 27 unmodified lines
2828 agent_server_store::{AgentServerStore, CLAUDE_CODE_NAME, CODEX_NAME, GEMINI_NAME},
2929 context_server_store::{ContextServerConfiguration, ContextServerStatus, ContextServerStore},
3030 };
31+use rope::Rope;
3132 use settings::{SettingsStore, update_settings_file};
3233 use ui::{
3334 Chip, CommonAnimationExt, ContextMenu, Disclosure, Divider, DividerColor, ElevationIndex,
@@ -1114,8 +1115,11 @@ async fn open_new_agent_servers_entry_in_settings_editor( · 1080 unmodified lines
11141115 ) -> Result<()> {
11151116 let settings_editor = workspace
11161117 .update_in(cx, |_, window, cx| {
1117− create_and_open_local_file(paths::settings_file(), window, cx, || {
1118− settings::initial_user_settings_content().as_ref().into()
1118+ create_and_open_local_file(paths::settings_file(), window, cx, |cx| {
1119+ Rope::from_str(
1120+ &settings::initial_user_settings_content(),
1121+ cx.background_executor(),
1122+ )
11191123 })
11201124 })?
11211125 .await?
crates/agent_ui/src/buffer_codegen.rs+4−3
@@ -487,9 +487,10 @@ impl CodegenAlternative { · 486 unmodified lines
487487 ) {
488488 let start_time = Instant::now();
489489 let snapshot = self.snapshot.clone();
490− let selected_text = snapshot
491− .text_for_range(self.range.start..self.range.end)
492− .collect::<Rope>();
490+ let selected_text = Rope::from_iter(
491+ snapshot.text_for_range(self.range.start..self.range.end),
492+ cx.background_executor(),
493+ );
493494
494495 let selection_start = self.range.start.to_point(&snapshot);
495496
crates/assistant_text_thread/src/text_thread.rs+2−1
@@ -744,12 +744,13 @@ impl TextThread { · 743 unmodified lines
744744 telemetry: Option<Arc<Telemetry>>,
745745 cx: &mut Context<Self>,
746746 ) -> Self {
747− let buffer = cx.new(|_cx| {
747+ let buffer = cx.new(|cx| {
748748 let buffer = Buffer::remote(
749749 language::BufferId::new(1).unwrap(),
750750 replica_id,
751751 capability,
752752 "",
753+ cx.background_executor(),
753754 );
754755 buffer.set_language_registry(language_registry.clone());
755756 buffer
crates/buffer_diff/src/buffer_diff.rs+47−11
@@ -1,6 +1,9 @@
11 use futures::channel::oneshot;
22 use git2::{DiffLineType as GitDiffLineType, DiffOptions as GitOptions, Patch as GitPatch};
3−use gpui::{App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, Task, TaskLabel};
3+use gpui::{
4+ App, AppContext as _, AsyncApp, BackgroundExecutor, Context, Entity, EventEmitter, Task,
5+ TaskLabel,
6+};
47 use language::{Language, LanguageRegistry};
58 use rope::Rope;
69 use std::{
@@ -191,7 +194,7 @@ impl BufferDiffSnapshot { · 184 unmodified lines
191194 let base_text_exists;
192195 let base_text_snapshot;
193196 if let Some(text) = &base_text {
194− let base_text_rope = Rope::from(text.as_str());
197+ let base_text_rope = Rope::from_str(text.as_str(), cx.background_executor());
195198 base_text_pair = Some((text.clone(), base_text_rope.clone()));
196199 let snapshot =
197200 language::Buffer::build_snapshot(base_text_rope, language, language_registry, cx);
@@ -311,6 +314,7 @@ impl BufferDiffInner { · 113 unmodified lines
311314 hunks: &[DiffHunk],
312315 buffer: &text::BufferSnapshot,
313316 file_exists: bool,
317+ cx: &BackgroundExecutor,
314318 ) -> Option<Rope> {
315319 let head_text = self
316320 .base_text_exists
@@ -505,7 +509,7 @@ impl BufferDiffInner { · 188 unmodified lines
505509 for (old_range, replacement_text) in edits {
506510 new_index_text.append(index_cursor.slice(old_range.start));
507511 index_cursor.seek_forward(old_range.end);
508− new_index_text.push(&replacement_text);
512+ new_index_text.push(&replacement_text, cx);
509513 }
510514 new_index_text.append(index_cursor.suffix());
511515 Some(new_index_text)
@@ -962,6 +966,7 @@ impl BufferDiff { · 450 unmodified lines
962966 hunks,
963967 buffer,
964968 file_exists,
969+ cx.background_executor(),
965970 );
966971
967972 cx.emit(BufferDiffEvent::HunksStagedOrUnstaged(
@@ -1385,7 +1390,12 @@ mod tests { · 417 unmodified lines
13851390 "
13861391 .unindent();
13871392
1388− let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), buffer_text);
1393+ let mut buffer = Buffer::new(
1394+ ReplicaId::LOCAL,
1395+ BufferId::new(1).unwrap(),
1396+ buffer_text,
1397+ cx.background_executor(),
1398+ );
13891399 let mut diff = BufferDiffSnapshot::new_sync(buffer.clone(), diff_base.clone(), cx);
13901400 assert_hunks(
13911401 diff.hunks_intersecting_range(Anchor::MIN..Anchor::MAX, &buffer),
@@ -1394,7 +1404,7 @@ mod tests { · 2 unmodified lines
13941404 &[(1..2, "two\n", "HELLO\n", DiffHunkStatus::modified_none())],
13951405 );
13961406
1397− buffer.edit([(0..0, "point five\n")]);
1407+ buffer.edit([(0..0, "point five\n")], cx.background_executor());
13981408 diff = BufferDiffSnapshot::new_sync(buffer.clone(), diff_base.clone(), cx);
13991409 assert_hunks(
14001410 diff.hunks_intersecting_range(Anchor::MIN..Anchor::MAX, &buffer),
@@ -1459,7 +1469,12 @@ mod tests { · 58 unmodified lines
14591469 "
14601470 .unindent();
14611471
1462− let buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), buffer_text);
1472+ let buffer = Buffer::new(
1473+ ReplicaId::LOCAL,
1474+ BufferId::new(1).unwrap(),
1475+ buffer_text,
1476+ cx.background_executor(),
1477+ );
14631478 let unstaged_diff = BufferDiffSnapshot::new_sync(buffer.clone(), index_text, cx);
14641479 let mut uncommitted_diff =
14651480 BufferDiffSnapshot::new_sync(buffer.clone(), head_text.clone(), cx);
@@ -1528,7 +1543,12 @@ mod tests { · 62 unmodified lines
15281543 "
15291544 .unindent();
15301545
1531− let buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), buffer_text);
1546+ let buffer = Buffer::new(
1547+ ReplicaId::LOCAL,
1548+ BufferId::new(1).unwrap(),
1549+ buffer_text,
1550+ cx.background_executor(),
1551+ );
15321552 let diff = cx
15331553 .update(|cx| {
15341554 BufferDiffSnapshot::new_with_base_text(
@@ -1791,7 +1811,12 @@ mod tests { · 256 unmodified lines
17911811
17921812 for example in table {
17931813 let (buffer_text, ranges) = marked_text_ranges(&example.buffer_marked_text, false);
1794− let buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), buffer_text);
1814+ let buffer = Buffer::new(
1815+ ReplicaId::LOCAL,
1816+ BufferId::new(1).unwrap(),
1817+ buffer_text,
1818+ cx.background_executor(),
1819+ );
17951820 let hunk_range =
17961821 buffer.anchor_before(ranges[0].start)..buffer.anchor_before(ranges[0].end);
17971822
@@ -1868,6 +1893,7 @@ mod tests { · 70 unmodified lines
18681893 ReplicaId::LOCAL,
18691894 BufferId::new(1).unwrap(),
18701895 buffer_text.clone(),
1896+ cx.background_executor(),
18711897 );
18721898 let unstaged = BufferDiffSnapshot::new_sync(buffer.clone(), index_text, cx);
18731899 let uncommitted = BufferDiffSnapshot::new_sync(buffer.clone(), head_text.clone(), cx);
@@ -1941,7 +1967,12 @@ mod tests { · 67 unmodified lines
19411967 "
19421968 .unindent();
19431969
1944− let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), buffer_text_1);
1970+ let mut buffer = Buffer::new(
1971+ ReplicaId::LOCAL,
1972+ BufferId::new(1).unwrap(),
1973+ buffer_text_1,
1974+ cx.background_executor(),
1975+ );
19451976
19461977 let empty_diff = cx.update(|cx| BufferDiffSnapshot::empty(&buffer, cx));
19471978 let diff_1 = BufferDiffSnapshot::new_sync(buffer.clone(), base_text.clone(), cx);
@@ -1961,6 +1992,7 @@ mod tests { · 13 unmodified lines
19611992 NINE
19621993 "
19631994 .unindent(),
1995+ cx.background_executor(),
19641996 );
19651997 let diff_2 = BufferDiffSnapshot::new_sync(buffer.clone(), base_text.clone(), cx);
19661998 assert_eq!(None, diff_2.inner.compare(&diff_1.inner, &buffer));
@@ -1978,6 +2010,7 @@ mod tests { · 11 unmodified lines
19782010 NINE
19792011 "
19802012 .unindent(),
2013+ cx.background_executor(),
19812014 );
19822015 let diff_3 = BufferDiffSnapshot::new_sync(buffer.clone(), base_text.clone(), cx);
19832016 let range = diff_3.inner.compare(&diff_2.inner, &buffer).unwrap();
@@ -1995,6 +2028,7 @@ mod tests { · 11 unmodified lines
19952028 NINE
19962029 "
19972030 .unindent(),
2031+ cx.background_executor(),
19982032 );
19992033 let diff_4 = BufferDiffSnapshot::new_sync(buffer.clone(), base_text.clone(), cx);
20002034 let range = diff_4.inner.compare(&diff_3.inner, &buffer).unwrap();
@@ -2013,6 +2047,7 @@ mod tests { · 12 unmodified lines
20132047 NINE
20142048 "
20152049 .unindent(),
2050+ cx.background_executor(),
20162051 );
20172052 let diff_5 = BufferDiffSnapshot::new_sync(buffer.snapshot(), base_text.clone(), cx);
20182053 let range = diff_5.inner.compare(&diff_4.inner, &buffer).unwrap();
@@ -2031,6 +2066,7 @@ mod tests { · 12 unmodified lines
20312066 «nine»
20322067 "
20332068 .unindent(),
2069+ cx.background_executor(),
20342070 );
20352071 let diff_6 = BufferDiffSnapshot::new_sync(buffer.snapshot(), base_text, cx);
20362072 let range = diff_6.inner.compare(&diff_5.inner, &buffer).unwrap();
@@ -2140,14 +2176,14 @@ mod tests { · 103 unmodified lines
21402176 let working_copy = gen_working_copy(rng, &head_text);
21412177 let working_copy = cx.new(|cx| {
21422178 language::Buffer::local_normalized(
2143− Rope::from(working_copy.as_str()),
2179+ Rope::from_str(working_copy.as_str(), cx.background_executor()),
21442180 text::LineEnding::default(),
21452181 cx,
21462182 )
21472183 });
21482184 let working_copy = working_copy.read_with(cx, |working_copy, _| working_copy.snapshot());
21492185 let mut index_text = if rng.random() {
2150− Rope::from(head_text.as_str())
2186+ Rope::from_str(head_text.as_str(), cx.background_executor())
21512187 } else {
21522188 working_copy.as_rope().clone()
21532189 };
crates/channel/src/channel_buffer.rs+1−0
@@ -70,6 +70,7 @@ impl ChannelBuffer { · 69 unmodified lines
7070 ReplicaId::new(response.replica_id as u16),
7171 capability,
7272 base_text,
73+ cx.background_executor(),
7374 )
7475 })?;
7576 buffer.update(cx, |buffer, cx| buffer.apply_ops(operations, cx))?;
crates/collab/src/db/queries/buffers.rs+2−2
@@ -701,12 +701,12 @@ impl Database { · 700 unmodified lines
701701 return Ok(());
702702 }
703703
704− let mut text_buffer = text::Buffer::new(
704+ let mut text_buffer = text::Buffer::new_slow(
705705 clock::ReplicaId::LOCAL,
706706 text::BufferId::new(1).unwrap(),
707707 base_text,
708708 );
709− text_buffer.apply_ops(operations.into_iter().filter_map(operation_from_wire));
709+ text_buffer.apply_ops(operations.into_iter().filter_map(operation_from_wire), None);
710710
711711 let base_text = text_buffer.text();
712712 let epoch = buffer.epoch + 1;
crates/collab/src/db/tests/buffer_tests.rs+36−20
@@ -74,11 +74,21 @@ async fn test_channel_buffers(db: &Arc<Database>) { · 73 unmodified lines
7474 ReplicaId::new(0),
7575 text::BufferId::new(1).unwrap(),
7676 "".to_string(),
77+ &db.test_options.as_ref().unwrap().executor,
7778 );
7879 let operations = vec![
79− buffer_a.edit([(0..0, "hello world")]),
80− buffer_a.edit([(5..5, ", cruel")]),
81− buffer_a.edit([(0..5, "goodbye")]),
80+ buffer_a.edit(
81+ [(0..0, "hello world")],
82+ &db.test_options.as_ref().unwrap().executor,
83+ ),
84+ buffer_a.edit(
85+ [(5..5, ", cruel")],
86+ &db.test_options.as_ref().unwrap().executor,
87+ ),
88+ buffer_a.edit(
89+ [(0..5, "goodbye")],
90+ &db.test_options.as_ref().unwrap().executor,
91+ ),
8292 buffer_a.undo().unwrap().1,
8393 ];
8494 assert_eq!(buffer_a.text(), "hello, cruel world");
@@ -102,15 +112,19 @@ async fn test_channel_buffers(db: &Arc<Database>) { · 17 unmodified lines
102112 ReplicaId::new(0),
103113 text::BufferId::new(1).unwrap(),
104114 buffer_response_b.base_text,
115+ &db.test_options.as_ref().unwrap().executor,
116+ );
117+ buffer_b.apply_ops(
118+ buffer_response_b.operations.into_iter().map(|operation| {
119+ let operation = proto::deserialize_operation(operation).unwrap();
120+ if let language::Operation::Buffer(operation) = operation {
121+ operation
122+ } else {
123+ unreachable!()
124+ }
125+ }),
126+ None,
105127 );
106− buffer_b.apply_ops(buffer_response_b.operations.into_iter().map(|operation| {
107− let operation = proto::deserialize_operation(operation).unwrap();
108− if let language::Operation::Buffer(operation) = operation {
109− operation
110− } else {
111− unreachable!()
112− }
113− }));
114128
115129 assert_eq!(buffer_b.text(), "hello, cruel world");
116130
@@ -247,6 +261,7 @@ async fn test_channel_buffers_last_operations(db: &Database) { · 130 unmodified lines
247261 ReplicaId::new(res.replica_id as u16),
248262 text::BufferId::new(1).unwrap(),
249263 "".to_string(),
264+ &db.test_options.as_ref().unwrap().executor,
250265 ));
251266 }
252267
@@ -255,9 +270,9 @@ async fn test_channel_buffers_last_operations(db: &Database) { · 2 unmodified lines
255270 user_id,
256271 db,
257272 vec![
258− text_buffers[0].edit([(0..0, "a")]),
259− text_buffers[0].edit([(0..0, "b")]),
260− text_buffers[0].edit([(0..0, "c")]),
273+ text_buffers[0].edit([(0..0, "a")], &db.test_options.as_ref().unwrap().executor),
274+ text_buffers[0].edit([(0..0, "b")], &db.test_options.as_ref().unwrap().executor),
275+ text_buffers[0].edit([(0..0, "c")], &db.test_options.as_ref().unwrap().executor),
261276 ],
262277 )
263278 .await;
@@ -267,9 +282,9 @@ async fn test_channel_buffers_last_operations(db: &Database) { · 3 unmodified lines
267282 user_id,
268283 db,
269284 vec![
270− text_buffers[1].edit([(0..0, "d")]),
271− text_buffers[1].edit([(1..1, "e")]),
272− text_buffers[1].edit([(2..2, "f")]),
285+ text_buffers[1].edit([(0..0, "d")], &db.test_options.as_ref().unwrap().executor),
286+ text_buffers[1].edit([(1..1, "e")], &db.test_options.as_ref().unwrap().executor),
287+ text_buffers[1].edit([(2..2, "f")], &db.test_options.as_ref().unwrap().executor),
273288 ],
274289 )
275290 .await;
@@ -286,14 +301,15 @@ async fn test_channel_buffers_last_operations(db: &Database) { · 10 unmodified lines
286301 replica_id,
287302 text::BufferId::new(1).unwrap(),
288303 "def".to_string(),
304+ &db.test_options.as_ref().unwrap().executor,
289305 );
290306 update_buffer(
291307 buffers[1].channel_id,
292308 user_id,
293309 db,
294310 vec![
295− text_buffers[1].edit([(0..0, "g")]),
296− text_buffers[1].edit([(0..0, "h")]),
311+ text_buffers[1].edit([(0..0, "g")], &db.test_options.as_ref().unwrap().executor),
312+ text_buffers[1].edit([(0..0, "h")], &db.test_options.as_ref().unwrap().executor),
297313 ],
298314 )
299315 .await;
@@ -302,7 +318,7 @@ async fn test_channel_buffers_last_operations(db: &Database) { · 2 unmodified lines
302318 buffers[2].channel_id,
303319 user_id,
304320 db,
305− vec![text_buffers[2].edit([(0..0, "i")])],
321+ vec![text_buffers[2].edit([(0..0, "i")], &db.test_options.as_ref().unwrap().executor)],
306322 )
307323 .await;
308324
crates/collab/src/tests/integration_tests.rs+2−2
@@ -3694,7 +3694,7 @@ async fn test_buffer_reloading( · 3693 unmodified lines
36943694 assert_eq!(buf.line_ending(), LineEnding::Unix);
36953695 });
36963696
3697− let new_contents = Rope::from("d\ne\nf");
3697+ let new_contents = Rope::from_str_small("d\ne\nf");
36983698 client_a
36993699 .fs()
37003700 .save(
@@ -4479,7 +4479,7 @@ async fn test_reloading_buffer_manually( · 778 unmodified lines
44794479 .fs()
44804480 .save(
44814481 path!("/a/a.rs").as_ref(),
4482− &Rope::from("let seven = 7;"),
4482+ &Rope::from_str_small("let seven = 7;"),
44834483 LineEnding::Unix,
44844484 )
44854485 .await
crates/collab/src/tests/random_project_collaboration_tests.rs+6−1
@@ -27,6 +27,7 @@ use std::{ · 26 unmodified lines
2727 rc::Rc,
2828 sync::Arc,
2929 };
30+use text::Rope;
3031 use util::{
3132 ResultExt, path,
3233 paths::PathStyle,
@@ -938,7 +939,11 @@ impl RandomizedTest for ProjectCollaborationTest { · 905 unmodified lines
938939
939940 client
940941 .fs()
941− .save(&path, &content.as_str().into(), text::LineEnding::Unix)
942+ .save(
943+ &path,
944+ &Rope::from_str_small(content.as_str()),
945+ text::LineEnding::Unix,
946+ )
942947 .await
943948 .unwrap();
944949 }
crates/diagnostics/src/diagnostics_tests.rs+2−2
@@ -877,7 +877,7 @@ async fn test_random_diagnostics_with_inlays(cx: &mut TestAppContext, mut rng: S · 876 unmodified lines
877877 vec![Inlay::edit_prediction(
878878 post_inc(&mut next_inlay_id),
879879 snapshot.buffer_snapshot().anchor_before(position),
880− Rope::from_iter(["Test inlay ", "next_inlay_id"]),
880+ Rope::from_iter_small(["Test inlay ", "next_inlay_id"]),
881881 )],
882882 cx,
883883 );
@@ -2070,7 +2070,7 @@ fn random_lsp_diagnostic( · 1186 unmodified lines
20702070 const ERROR_MARGIN: usize = 10;
20712071
20722072 let file_content = fs.read_file_sync(path).unwrap();
2073− let file_text = Rope::from(String::from_utf8_lossy(&file_content).as_ref());
2073+ let file_text = Rope::from_str_small(String::from_utf8_lossy(&file_content).as_ref());
20742074
20752075 let start = rng.random_range(0..file_text.len().saturating_add(ERROR_MARGIN));
20762076 let end = rng.random_range(start..file_text.len().saturating_add(ERROR_MARGIN));
crates/edit_prediction_button/src/edit_prediction_button.rs+6−3
@@ -13,7 +13,7 @@ use gpui::{ · 12 unmodified lines
1313 };
1414 use indoc::indoc;
1515 use language::{
16− EditPredictionsMode, File, Language,
16+ EditPredictionsMode, File, Language, Rope,
1717 language_settings::{self, AllLanguageSettings, EditPredictionProvider, all_language_settings},
1818 };
1919 use project::DisableAiSettings;
@@ -1056,8 +1056,11 @@ async fn open_disabled_globs_setting_in_editor( · 1036 unmodified lines
10561056 ) -> Result<()> {
10571057 let settings_editor = workspace
10581058 .update_in(cx, |_, window, cx| {
1059− create_and_open_local_file(paths::settings_file(), window, cx, || {
1060− settings::initial_user_settings_content().as_ref().into()
1059+ create_and_open_local_file(paths::settings_file(), window, cx, |cx| {
1060+ Rope::from_str(
1061+ settings::initial_user_settings_content().as_ref(),
1062+ cx.background_executor(),
1063+ )
10611064 })
10621065 })?
10631066 .await?
crates/editor/src/display_map.rs+2−1
@@ -1569,6 +1569,7 @@ pub mod tests { · 1568 unmodified lines
15691569 use lsp::LanguageServerId;
15701570 use project::Project;
15711571 use rand::{Rng, prelude::*};
1572+ use rope::Rope;
15721573 use settings::{SettingsContent, SettingsStore};
15731574 use smol::stream::StreamExt;
15741575 use std::{env, sync::Arc};
@@ -2074,7 +2075,7 @@ pub mod tests { · 499 unmodified lines
20742075 vec![Inlay::edit_prediction(
20752076 0,
20762077 buffer_snapshot.anchor_after(0),
2077− "\n",
2078+ Rope::from_str_small("\n"),
20782079 )],
20792080 cx,
20802081 );
crates/editor/src/display_map/inlay_map.rs+24−12
@@ -700,16 +700,20 @@ impl InlayMap { · 699 unmodified lines
700700 .collect::<String>();
701701
702702 let next_inlay = if i % 2 == 0 {
703+ use rope::Rope;
704+
703705 Inlay::mock_hint(
704706 post_inc(next_inlay_id),
705707 snapshot.buffer.anchor_at(position, bias),
706− &text,
708+ Rope::from_str_small(&text),
707709 )
708710 } else {
711+ use rope::Rope;
712+
709713 Inlay::edit_prediction(
710714 post_inc(next_inlay_id),
711715 snapshot.buffer.anchor_at(position, bias),
712− &text,
716+ Rope::from_str_small(&text),
713717 )
714718 };
715719 let inlay_id = next_inlay.id;
@@ -1301,7 +1305,7 @@ mod tests { · 585 unmodified lines
13011305 vec![Inlay::mock_hint(
13021306 post_inc(&mut next_inlay_id),
13031307 buffer.read(cx).snapshot(cx).anchor_after(3),
1304− "|123|",
1308+ Rope::from_str_small("|123|"),
13051309 )],
13061310 );
13071311 assert_eq!(inlay_snapshot.text(), "abc|123|defghi");
@@ -1378,12 +1382,12 @@ mod tests { · 70 unmodified lines
13781382 Inlay::mock_hint(
13791383 post_inc(&mut next_inlay_id),
13801384 buffer.read(cx).snapshot(cx).anchor_before(3),
1381− "|123|",
1385+ Rope::from_str_small("|123|"),
13821386 ),
13831387 Inlay::edit_prediction(
13841388 post_inc(&mut next_inlay_id),
13851389 buffer.read(cx).snapshot(cx).anchor_after(3),
1386− "|456|",
1390+ Rope::from_str_small("|456|"),
13871391 ),
13881392 ],
13891393 );
@@ -1593,17 +1597,17 @@ mod tests { · 203 unmodified lines
15931597 Inlay::mock_hint(
15941598 post_inc(&mut next_inlay_id),
15951599 buffer.read(cx).snapshot(cx).anchor_before(0),
1596− "|123|\n",
1600+ Rope::from_str_small("|123|\n"),
15971601 ),
15981602 Inlay::mock_hint(
15991603 post_inc(&mut next_inlay_id),
16001604 buffer.read(cx).snapshot(cx).anchor_before(4),
1601− "|456|",
1605+ Rope::from_str_small("|456|"),
16021606 ),
16031607 Inlay::edit_prediction(
16041608 post_inc(&mut next_inlay_id),
16051609 buffer.read(cx).snapshot(cx).anchor_before(7),
1606− "\n|567|\n",
1610+ Rope::from_str_small("\n|567|\n"),
16071611 ),
16081612 ],
16091613 );
@@ -1677,9 +1681,14 @@ mod tests { · 67 unmodified lines
16771681 (offset, inlay.clone())
16781682 })
16791683 .collect::<Vec<_>>();
1680− let mut expected_text = Rope::from(&buffer_snapshot.text());
1684+ let mut expected_text =
1685+ Rope::from_str(&buffer_snapshot.text(), cx.background_executor());
16811686 for (offset, inlay) in inlays.iter().rev() {
1682− expected_text.replace(*offset..*offset, &inlay.text().to_string());
1687+ expected_text.replace(
1688+ *offset..*offset,
1689+ &inlay.text().to_string(),
1690+ cx.background_executor(),
1691+ );
16831692 }
16841693 assert_eq!(inlay_snapshot.text(), expected_text.to_string());
16851694
@@ -2067,7 +2076,7 @@ mod tests { · 381 unmodified lines
20672076 let inlay = Inlay {
20682077 id: InlayId::Hint(0),
20692078 position,
2070− content: InlayContent::Text(text::Rope::from(inlay_text)),
2079+ content: InlayContent::Text(text::Rope::from_str(inlay_text, cx.background_executor())),
20712080 };
20722081
20732082 let (inlay_snapshot, _) = inlay_map.splice(&[], vec![inlay]);
@@ -2181,7 +2190,10 @@ mod tests { · 107 unmodified lines
21812190 let inlay = Inlay {
21822191 id: InlayId::Hint(0),
21832192 position,
2184− content: InlayContent::Text(text::Rope::from(test_case.inlay_text)),
2193+ content: InlayContent::Text(text::Rope::from_str(
2194+ test_case.inlay_text,
2195+ cx.background_executor(),
2196+ )),
21852197 };
21862198
21872199 let (inlay_snapshot, _) = inlay_map.splice(&[], vec![inlay]);
crates/editor/src/display_map/tab_map.rs+1−1
@@ -1042,7 +1042,7 @@ mod tests { · 1041 unmodified lines
10421042 let (mut tab_map, _) = TabMap::new(fold_snapshot, tab_size);
10431043 let tabs_snapshot = tab_map.set_max_expansion_column(32);
10441044
1045− let text = text::Rope::from(tabs_snapshot.text().as_str());
1045+ let text = text::Rope::from_str(tabs_snapshot.text().as_str(), cx.background_executor());
10461046 log::info!(
10471047 "TabMap text (tab size: {}): {:?}",
10481048 tab_size,
crates/editor/src/display_map/wrap_map.rs+5−4
@@ -863,7 +863,7 @@ impl WrapSnapshot { · 862 unmodified lines
863863 }
864864 }
865865
866− let text = language::Rope::from(self.text().as_str());
866+ let text = language::Rope::from_str_small(self.text().as_str());
867867 let mut input_buffer_rows = self.tab_snapshot.rows(0);
868868 let mut expected_buffer_rows = Vec::new();
869869 let mut prev_tab_row = 0;
@@ -1413,9 +1413,10 @@ mod tests { · 543 unmodified lines
14131413 }
14141414 }
14151415
1416− let mut initial_text = Rope::from(initial_snapshot.text().as_str());
1416+ let mut initial_text =
1417+ Rope::from_str(initial_snapshot.text().as_str(), cx.background_executor());
14171418 for (snapshot, patch) in edits {
1418− let snapshot_text = Rope::from(snapshot.text().as_str());
1419+ let snapshot_text = Rope::from_str(snapshot.text().as_str(), cx.background_executor());
14191420 for edit in &patch {
14201421 let old_start = initial_text.point_to_offset(Point::new(edit.new.start, 0));
14211422 let old_end = initial_text.point_to_offset(cmp::min(
@@ -1431,7 +1432,7 @@ mod tests { · 9 unmodified lines
14311432 .chunks_in_range(new_start..new_end)
14321433 .collect::<String>();
14331434
1434− initial_text.replace(old_start..old_end, &new_text);
1435+ initial_text.replace(old_start..old_end, &new_text, cx.background_executor());
14351436 }
14361437 assert_eq!(initial_text.to_string(), snapshot_text.to_string());
14371438 }
crates/editor/src/editor.rs+1−1
@@ -7852,7 +7852,7 @@ impl Editor { · 7851 unmodified lines
78527852 let inlay = Inlay::edit_prediction(
78537853 post_inc(&mut self.next_inlay_id),
78547854 range.start,
7855− new_text.as_str(),
7855+ Rope::from_str_small(new_text.as_str()),
78567856 );
78577857 inlay_ids.push(inlay.id);
78587858 inlays.push(inlay);
crates/editor/src/git/blame.rs+3−2
@@ -1115,18 +1115,19 @@ mod tests { · 1114 unmodified lines
11151115
11161116 let fs = FakeFs::new(cx.executor());
11171117 let buffer_initial_text_len = rng.random_range(5..15);
1118− let mut buffer_initial_text = Rope::from(
1118+ let mut buffer_initial_text = Rope::from_str(
11191119 RandomCharIter::new(&mut rng)
11201120 .take(buffer_initial_text_len)
11211121 .collect::<String>()
11221122 .as_str(),
1123+ cx.background_executor(),
11231124 );
11241125
11251126 let mut newline_ixs = (0..buffer_initial_text_len).choose_multiple(&mut rng, 5);
11261127 newline_ixs.sort_unstable();
11271128 for newline_ix in newline_ixs.into_iter().rev() {
11281129 let newline_ix = buffer_initial_text.clip_offset(newline_ix, Bias::Right);
1129− buffer_initial_text.replace(newline_ix..newline_ix, "\n");
1130+ buffer_initial_text.replace(newline_ix..newline_ix, "\n", cx.background_executor());
11301131 }
11311132 log::info!("initial buffer text: {:?}", buffer_initial_text);
11321133
crates/editor/src/inlays.rs+9−9
@@ -59,10 +59,10 @@ impl Inlay { · 58 unmodified lines
5959 pub fn hint(id: InlayId, position: Anchor, hint: &InlayHint) -> Self {
6060 let mut text = hint.text();
6161 if hint.padding_right && text.reversed_chars_at(text.len()).next() != Some(' ') {
62− text.push(" ");
62+ text.push_small(" ");
6363 }
6464 if hint.padding_left && text.chars_at(0).next() != Some(' ') {
65− text.push_front(" ");
65+ text.push_front_small(" ");
6666 }
6767 Self {
6868 id,
@@ -72,11 +72,11 @@ impl Inlay { · 3 unmodified lines
7272 }
7373
7474 #[cfg(any(test, feature = "test-support"))]
75− pub fn mock_hint(id: usize, position: Anchor, text: impl Into<Rope>) -> Self {
75+ pub fn mock_hint(id: usize, position: Anchor, text: Rope) -> Self {
7676 Self {
7777 id: InlayId::Hint(id),
7878 position,
79− content: InlayContent::Text(text.into()),
79+ content: InlayContent::Text(text),
8080 }
8181 }
8282
@@ -88,19 +88,19 @@ impl Inlay { · 5 unmodified lines
8888 }
8989 }
9090
91− pub fn edit_prediction<T: Into<Rope>>(id: usize, position: Anchor, text: T) -> Self {
91+ pub fn edit_prediction(id: usize, position: Anchor, text: Rope) -> Self {
9292 Self {
9393 id: InlayId::EditPrediction(id),
9494 position,
95− content: InlayContent::Text(text.into()),
95+ content: InlayContent::Text(text),
9696 }
9797 }
9898
99− pub fn debugger<T: Into<Rope>>(id: usize, position: Anchor, text: T) -> Self {
99+ pub fn debugger(id: usize, position: Anchor, text: Rope) -> Self {
100100 Self {
101101 id: InlayId::DebuggerValue(id),
102102 position,
103− content: InlayContent::Text(text.into()),
103+ content: InlayContent::Text(text),
104104 }
105105 }
106106
@@ -108,7 +108,7 @@ impl Inlay { · 1 unmodified line
108108 static COLOR_TEXT: OnceLock<Rope> = OnceLock::new();
109109 match &self.content {
110110 InlayContent::Text(text) => text,
111− InlayContent::Color(_) => COLOR_TEXT.get_or_init(|| Rope::from("◼")),
111+ InlayContent::Color(_) => COLOR_TEXT.get_or_init(|| Rope::from_str_small("◼")),
112112 }
113113 }
114114
crates/editor/src/movement.rs+5−4
@@ -878,6 +878,7 @@ mod tests { · 877 unmodified lines
878878 use gpui::{AppContext as _, font, px};
879879 use language::Capability;
880880 use project::{Project, project_settings::DiagnosticSeverity};
881+ use rope::Rope;
881882 use settings::SettingsStore;
882883 use util::post_inc;
883884
@@ -1024,22 +1025,22 @@ mod tests { · 140 unmodified lines
10241025 Inlay::edit_prediction(
10251026 post_inc(&mut id),
10261027 buffer_snapshot.anchor_before(offset),
1027− "test",
1028+ Rope::from_str_small("test"),
10281029 ),
10291030 Inlay::edit_prediction(
10301031 post_inc(&mut id),
10311032 buffer_snapshot.anchor_after(offset),
1032− "test",
1033+ Rope::from_str_small("test"),
10331034 ),
10341035 Inlay::mock_hint(
10351036 post_inc(&mut id),
10361037 buffer_snapshot.anchor_before(offset),
1037− "test",
1038+ Rope::from_str_small("test"),
10381039 ),
10391040 Inlay::mock_hint(
10401041 post_inc(&mut id),
10411042 buffer_snapshot.anchor_after(offset),
1042− "test",
1043+ Rope::from_str_small("test"),
10431044 ),
10441045 ]
10451046 })
crates/editor/src/signature_help.rs+1−1
@@ -193,7 +193,7 @@ impl Editor { · 192 unmodified lines
193193
194194 if let Some(language) = language {
195195 for signature in &mut signature_help.signatures {
196− let text = Rope::from(signature.label.as_ref());
196+ let text = Rope::from_str_small(signature.label.as_ref());
197197 let highlights = language
198198 .highlight_text(&text, 0..signature.label.len())
199199 .into_iter()
crates/extension_host/src/extension_host.rs+10−5
@@ -1468,6 +1468,7 @@ impl ExtensionStore { · 1467 unmodified lines
14681468 let extensions_dir = self.installed_dir.clone();
14691469 let index_path = self.index_path.clone();
14701470 let proxy = self.proxy.clone();
1471+ let executor = cx.background_executor().clone();
14711472 cx.background_spawn(async move {
14721473 let start_time = Instant::now();
14731474 let mut index = ExtensionIndex::default();
@@ -1501,10 +1502,14 @@ impl ExtensionStore { · 27 unmodified lines
15011502 }
15021503
15031504 if let Ok(index_json) = serde_json::to_string_pretty(&index) {
1504− fs.save(&index_path, &index_json.as_str().into(), Default::default())
1505− .await
1506− .context("failed to save extension index")
1507− .log_err();
1505+ fs.save(
1506+ &index_path,
1507+ &Rope::from_str(&index_json, &executor),
1508+ Default::default(),
1509+ )
1510+ .await
1511+ .context("failed to save extension index")
1512+ .log_err();
15081513 }
15091514
15101515 log::info!("rebuilt extension index in {:?}", start_time.elapsed());
@@ -1671,7 +1676,7 @@ impl ExtensionStore { · 160 unmodified lines
16711676 let manifest_toml = toml::to_string(&loaded_extension.manifest)?;
16721677 fs.save(
16731678 &tmp_dir.join(EXTENSION_TOML),
1674− &Rope::from(manifest_toml),
1679+ &Rope::from_str_small(&manifest_toml),
16751680 language::LineEnding::Unix,
16761681 )
16771682 .await?;
crates/git_ui/src/commit_view.rs+6−3
@@ -170,7 +170,10 @@ impl CommitView { · 169 unmodified lines
170170 ReplicaId::LOCAL,
171171 cx.entity_id().as_non_zero_u64().into(),
172172 LineEnding::default(),
173− format_commit(&commit, stash.is_some()).into(),
173+ Rope::from_str(
174+ &format_commit(&commit, stash.is_some()),
175+ cx.background_executor(),
176+ ),
174177 );
175178 metadata_buffer_id = Some(buffer.remote_id());
176179 Buffer::build(buffer, Some(file.clone()), Capability::ReadWrite)
@@ -336,7 +339,7 @@ async fn build_buffer( · 159 unmodified lines
336339 ) -> Result<Entity<Buffer>> {
337340 let line_ending = LineEnding::detect(&text);
338341 LineEnding::normalize(&mut text);
339− let text = Rope::from(text);
342+ let text = Rope::from_str(&text, cx.background_executor());
340343 let language = cx.update(|cx| language_registry.language_for_file(&blob, Some(&text), cx))?;
341344 let language = if let Some(language) = language {
342345 language_registry
@@ -376,7 +379,7 @@ async fn build_buffer_diff( · 33 unmodified lines
376379 let base_buffer = cx
377380 .update(|cx| {
378381 Buffer::build_snapshot(
379− old_text.as_deref().unwrap_or("").into(),
382+ Rope::from_str(old_text.as_deref().unwrap_or(""), cx.background_executor()),
380383 buffer.language().cloned(),
381384 Some(language_registry.clone()),
382385 cx,
crates/git_ui/src/file_diff_view.rs+5−6
@@ -359,6 +359,7 @@ mod tests { · 358 unmodified lines
359359 use super::*;
360360 use editor::test::editor_test_context::assert_state_with_diff;
361361 use gpui::TestAppContext;
362+ use language::Rope;
362363 use project::{FakeFs, Fs, Project};
363364 use settings::SettingsStore;
364365 use std::path::PathBuf;
@@ -429,7 +430,7 @@ mod tests { · 64 unmodified lines
429430 // Modify the new file on disk
430431 fs.save(
431432 path!("/test/new_file.txt").as_ref(),
432− &unindent(
433+ &Rope::from_str_small(&unindent(
433434 "
434435 new line 1
435436 line 2
@@ -437,8 +438,7 @@ mod tests { · 1 unmodified line
437438 line 4
438439 new line 5
439440 ",
440− )
441− .into(),
441+ )),
442442 Default::default(),
443443 )
444444 .await
@@ -465,15 +465,14 @@ mod tests { · 20 unmodified lines
465465 // Modify the old file on disk
466466 fs.save(
467467 path!("/test/old_file.txt").as_ref(),
468− &unindent(
468+ &Rope::from_str_small(&unindent(
469469 "
470470 new line 1
471471 line 2
472472 old line 3
473473 line 4
474474 ",
475− )
476− .into(),
475+ )),
477476 Default::default(),
478477 )
479478 .await
crates/gpui/src/app/async_context.rs+13−0
@@ -260,6 +260,19 @@ impl AsyncApp { · 259 unmodified lines
260260 }
261261 }
262262
263+impl sum_tree::BackgroundSpawn for BackgroundExecutor {
264+ type Task<R>
265+ = Task<R>
266+ where
267+ R: Send + Sync;
268+ fn background_spawn<R>(&self, future: impl Future<Output = R> + Send + 'static) -> Self::Task<R>
269+ where
270+ R: Send + Sync + 'static,
271+ {
272+ self.spawn(future)
273+ }
274+}
275+
263276 /// A cloneable, owned handle to the application context,
264277 /// composed with the window associated with the current task.
265278 #[derive(Clone, Deref, DerefMut)]
crates/gpui/src/app/test_context.rs+5−0
@@ -393,6 +393,11 @@ impl TestAppContext { · 392 unmodified lines
393393 }
394394 }
395395
396+ /// Returns the background executor for this context.
397+ pub fn background_executor(&self) -> &BackgroundExecutor {
398+ &self.background_executor
399+ }
400+
396401 /// Wait until there are no more pending tasks.
397402 pub fn run_until_parked(&mut self) {
398403 self.background_executor.run_until_parked()
crates/gpui/src/executor.rs+1−1
@@ -342,7 +342,7 @@ impl BackgroundExecutor { · 341 unmodified lines
342342 /// for all of them to complete before returning.
343343 pub async fn scoped<'scope, F>(&self, scheduler: F)
344344 where
345− F: FnOnce(&mut Scope<'scope>),
345+ F: for<'a> FnOnce(&'a mut Scope<'scope>),
346346 {
347347 let mut scope = Scope::new(self.clone());
348348 (scheduler)(&mut scope);
crates/keymap_editor/src/keymap_editor.rs+2−2
@@ -22,7 +22,7 @@ use gpui::{ · 21 unmodified lines
2222 ScrollWheelEvent, Stateful, StyledText, Subscription, Task, TextStyleRefinement, WeakEntity,
2323 actions, anchored, deferred, div,
2424 };
25−use language::{Language, LanguageConfig, ToOffset as _};
25+use language::{Language, LanguageConfig, Rope, ToOffset as _};
2626 use notifications::status_toast::{StatusToast, ToastIcon};
2727 use project::{CompletionDisplayOptions, Project};
2828 use settings::{
@@ -2119,7 +2119,7 @@ impl RenderOnce for SyntaxHighlightedText { · 2090 unmodified lines
21192119
21202120 let highlights = self
21212121 .language
2122− .highlight_text(&text.as_ref().into(), 0..text.len());
2122+ .highlight_text(&Rope::from_str_small(text.as_ref()), 0..text.len());
21232123 let mut runs = Vec::with_capacity(highlights.len());
21242124 let mut offset = 0;
21252125
crates/language/src/buffer.rs+14−7
@@ -24,8 +24,8 @@ use collections::HashMap; · 23 unmodified lines
2424 use fs::MTime;
2525 use futures::channel::oneshot;
2626 use gpui::{
27− App, AppContext as _, Context, Entity, EventEmitter, HighlightStyle, SharedString, StyledText,
28− Task, TaskLabel, TextStyle,
27+ App, AppContext as _, BackgroundExecutor, Context, Entity, EventEmitter, HighlightStyle,
28+ SharedString, StyledText, Task, TaskLabel, TextStyle,
2929 };
3030
3131 use lsp::{LanguageServerId, NumberOrString};
@@ -832,6 +832,7 @@ impl Buffer { · 800 unmodified lines
832832 ReplicaId::LOCAL,
833833 cx.entity_id().as_non_zero_u64().into(),
834834 base_text.into(),
835+ &cx.background_executor(),
835836 ),
836837 None,
837838 Capability::ReadWrite,
@@ -862,9 +863,10 @@ impl Buffer { · 24 unmodified lines
862863 replica_id: ReplicaId,
863864 capability: Capability,
864865 base_text: impl Into<String>,
866+ cx: &BackgroundExecutor,
865867 ) -> Self {
866868 Self::build(
867− TextBuffer::new(replica_id, remote_id, base_text.into()),
869+ TextBuffer::new(replica_id, remote_id, base_text.into(), cx),
868870 None,
869871 capability,
870872 )
@@ -877,9 +879,10 @@ impl Buffer { · 6 unmodified lines
877879 capability: Capability,
878880 message: proto::BufferState,
879881 file: Option<Arc<dyn File>>,
882+ cx: &BackgroundExecutor,
880883 ) -> Result<Self> {
881884 let buffer_id = BufferId::new(message.id).context("Could not deserialize buffer_id")?;
882− let buffer = TextBuffer::new(replica_id, buffer_id, message.base_text);
885+ let buffer = TextBuffer::new(replica_id, buffer_id, message.base_text, cx);
883886 let mut this = Self::build(buffer, file, capability);
884887 this.text.set_line_ending(proto::deserialize_line_ending(
885888 rpc::proto::LineEnding::from_i32(message.line_ending).context("missing line_ending")?,
@@ -1138,13 +1141,14 @@ impl Buffer { · 252 unmodified lines
11381141 let old_snapshot = self.text.snapshot();
11391142 let mut branch_buffer = self.text.branch();
11401143 let mut syntax_snapshot = self.syntax_map.lock().snapshot();
1144+ let executor = cx.background_executor().clone();
11411145 cx.background_spawn(async move {
11421146 if !edits.is_empty() {
11431147 if let Some(language) = language.clone() {
11441148 syntax_snapshot.reparse(&old_snapshot, registry.clone(), language);
11451149 }
11461150
1147− branch_buffer.edit(edits.iter().cloned());
1151+ branch_buffer.edit(edits.iter().cloned(), &executor);
11481152 let snapshot = branch_buffer.snapshot();
11491153 syntax_snapshot.interpolate(&snapshot);
11501154
@@ -2361,7 +2365,9 @@ impl Buffer { · 1210 unmodified lines
23612365 let autoindent_request = autoindent_mode
23622366 .and_then(|mode| self.language.as_ref().map(|_| (self.snapshot(), mode)));
23632367
2364− let edit_operation = self.text.edit(edits.iter().cloned());
2368+ let edit_operation = self
2369+ .text
2370+ .edit(edits.iter().cloned(), cx.background_executor());
23652371 let edit_id = edit_operation.timestamp();
23662372
23672373 if let Some((before_edit, mode)) = autoindent_request {
@@ -2592,7 +2598,8 @@ impl Buffer { · 224 unmodified lines
25922598 for operation in buffer_ops.iter() {
25932599 self.send_operation(Operation::Buffer(operation.clone()), false, cx);
25942600 }
2595− self.text.apply_ops(buffer_ops);
2601+ self.text
2602+ .apply_ops(buffer_ops, Some(cx.background_executor()));
25962603 self.deferred_ops.insert(deferred_ops);
25972604 self.flush_deferred_ops(cx);
25982605 self.did_edit(&old_version, was_dirty, cx);
crates/language/src/buffer_tests.rs+29−10
@@ -75,6 +75,7 @@ fn test_set_line_ending(cx: &mut TestAppContext) { · 74 unmodified lines
7575 Capability::ReadWrite,
7676 base.read(cx).to_proto(cx),
7777 None,
78+ cx.background_executor(),
7879 )
7980 .unwrap()
8081 });
@@ -255,14 +256,18 @@ async fn test_first_line_pattern(cx: &mut TestAppContext) { · 174 unmodified lines
255256 .is_none()
256257 );
257258 assert!(
258− cx.read(|cx| languages.language_for_file(&file("the/script"), Some(&"nothing".into()), cx))
259− .is_none()
259+ cx.read(|cx| languages.language_for_file(
260+ &file("the/script"),
261+ Some(&Rope::from_str("nothing", cx.background_executor())),
262+ cx
263+ ))
264+ .is_none()
260265 );
261266
262267 assert_eq!(
263268 cx.read(|cx| languages.language_for_file(
264269 &file("the/script"),
265− Some(&"#!/bin/env node".into()),
270+ Some(&Rope::from_str("#!/bin/env node", cx.background_executor())),
266271 cx
267272 ))
268273 .unwrap()
@@ -406,6 +411,7 @@ fn test_edit_events(cx: &mut gpui::App) { · 137 unmodified lines
406411 ReplicaId::new(1),
407412 Capability::ReadWrite,
408413 "abcdef",
414+ cx.background_executor(),
409415 )
410416 });
411417 let buffer1_ops = Arc::new(Mutex::new(Vec::new()));
@@ -2781,8 +2787,14 @@ fn test_serialization(cx: &mut gpui::App) { · 2369 unmodified lines
27812787 .background_executor()
27822788 .block(buffer1.read(cx).serialize_ops(None, cx));
27832789 let buffer2 = cx.new(|cx| {
2784− let mut buffer =
2785− Buffer::from_proto(ReplicaId::new(1), Capability::ReadWrite, state, None).unwrap();
2790+ let mut buffer = Buffer::from_proto(
2791+ ReplicaId::new(1),
2792+ Capability::ReadWrite,
2793+ state,
2794+ None,
2795+ cx.background_executor(),
2796+ )
2797+ .unwrap();
27862798 buffer.apply_ops(
27872799 ops.into_iter()
27882800 .map(|op| proto::deserialize_operation(op).unwrap()),
@@ -2806,6 +2818,7 @@ fn test_branch_and_merge(cx: &mut TestAppContext) { · 17 unmodified lines
28062818 Capability::ReadWrite,
28072819 base.read(cx).to_proto(cx),
28082820 None,
2821+ cx.background_executor(),
28092822 )
28102823 .unwrap()
28112824 });
@@ -3120,9 +3133,14 @@ fn test_random_collaboration(cx: &mut App, mut rng: StdRng) { · 308 unmodified lines
31203133 let ops = cx
31213134 .background_executor()
31223135 .block(base_buffer.read(cx).serialize_ops(None, cx));
3123− let mut buffer =
3124− Buffer::from_proto(ReplicaId::new(i as u16), Capability::ReadWrite, state, None)
3125− .unwrap();
3136+ let mut buffer = Buffer::from_proto(
3137+ ReplicaId::new(i as u16),
3138+ Capability::ReadWrite,
3139+ state,
3140+ None,
3141+ cx.background_executor(),
3142+ )
3143+ .unwrap();
31263144 buffer.apply_ops(
31273145 ops.into_iter()
31283146 .map(|op| proto::deserialize_operation(op).unwrap()),
@@ -3251,6 +3269,7 @@ fn test_random_collaboration(cx: &mut App, mut rng: StdRng) { · 122 unmodified lines
32513269 Capability::ReadWrite,
32523270 old_buffer_state,
32533271 None,
3272+ cx.background_executor(),
32543273 )
32553274 .unwrap();
32563275 new_buffer.apply_ops(
@@ -3414,7 +3433,7 @@ fn test_contiguous_ranges() { · 157 unmodified lines
34143433 }
34153434
34163435 #[gpui::test(iterations = 500)]
3417−fn test_trailing_whitespace_ranges(mut rng: StdRng) {
3436+fn test_trailing_whitespace_ranges(mut rng: StdRng, cx: &mut TestAppContext) {
34183437 // Generate a random multi-line string containing
34193438 // some lines with trailing whitespace.
34203439 let mut text = String::new();
@@ -3438,7 +3457,7 @@ fn test_trailing_whitespace_ranges(mut rng: StdRng) { · 17 unmodified lines
34383457 _ => {}
34393458 }
34403459
3441− let rope = Rope::from(text.as_str());
3460+ let rope = Rope::from_str(text.as_str(), cx.background_executor());
34423461 let actual_ranges = trailing_whitespace_ranges(&rope);
34433462 let expected_ranges = TRAILING_WHITESPACE_REGEX
34443463 .find_iter(&text)
crates/language/src/syntax_map/syntax_map_tests.rs+33−12
@@ -100,6 +100,7 @@ fn test_syntax_map_layers_for_range(cx: &mut App) { · 99 unmodified lines
100100 }
101101 "#
102102 .unindent(),
103+ cx.background_executor(),
103104 );
104105
105106 let mut syntax_map = SyntaxMap::new(&buffer);
@@ -147,7 +148,7 @@ fn test_syntax_map_layers_for_range(cx: &mut App) { · 41 unmodified lines
147148
148149 // Replace a vec! macro invocation with a plain slice, removing a syntactic layer.
149150 let macro_name_range = range_for_text(&buffer, "vec!");
150− buffer.edit([(macro_name_range, "&")]);
151+ buffer.edit([(macro_name_range, "&")], cx.background_executor());
151152 syntax_map.interpolate(&buffer);
152153 syntax_map.reparse(language.clone(), &buffer);
153154
@@ -199,6 +200,7 @@ fn test_dynamic_language_injection(cx: &mut App) { · 45 unmodified lines
199200 ```
200201 "#
201202 .unindent(),
203+ cx.background_executor(),
202204 );
203205
204206 let mut syntax_map = SyntaxMap::new(&buffer);
@@ -218,7 +220,10 @@ fn test_dynamic_language_injection(cx: &mut App) { · 13 unmodified lines
218220
219221 // Replace `rs` with a path to ending in `.rb` in code block.
220222 let macro_name_range = range_for_text(&buffer, "rs");
221− buffer.edit([(macro_name_range, "foo/bar/baz.rb")]);
223+ buffer.edit(
224+ [(macro_name_range, "foo/bar/baz.rb")],
225+ cx.background_executor(),
226+ );
222227 syntax_map.interpolate(&buffer);
223228 syntax_map.reparse(markdown.clone(), &buffer);
224229 syntax_map.reparse(markdown_inline.clone(), &buffer);
@@ -235,7 +240,7 @@ fn test_dynamic_language_injection(cx: &mut App) { · 10 unmodified lines
235240
236241 // Replace Ruby with a language that hasn't been loaded yet.
237242 let macro_name_range = range_for_text(&buffer, "foo/bar/baz.rb");
238− buffer.edit([(macro_name_range, "html")]);
243+ buffer.edit([(macro_name_range, "html")], cx.background_executor());
239244 syntax_map.interpolate(&buffer);
240245 syntax_map.reparse(markdown.clone(), &buffer);
241246 syntax_map.reparse(markdown_inline.clone(), &buffer);
@@ -811,7 +816,12 @@ fn test_syntax_map_languages_loading_with_erb(cx: &mut App) { · 569 unmodified lines
811816 .unindent();
812817
813818 let registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
814− let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), text);
819+ let mut buffer = Buffer::new(
820+ ReplicaId::LOCAL,
821+ BufferId::new(1).unwrap(),
822+ text,
823+ cx.background_executor(),
824+ );
815825
816826 let mut syntax_map = SyntaxMap::new(&buffer);
817827 syntax_map.set_language_registry(registry.clone());
@@ -859,7 +869,7 @@ fn test_syntax_map_languages_loading_with_erb(cx: &mut App) { · 41 unmodified lines
859869 .unindent();
860870
861871 log::info!("editing");
862− buffer.edit_via_marked_text(&text);
872+ buffer.edit_via_marked_text(&text, cx.background_executor());
863873 syntax_map.interpolate(&buffer);
864874 syntax_map.reparse(language, &buffer);
865875
@@ -903,7 +913,7 @@ fn test_random_syntax_map_edits_rust_macros(rng: StdRng, cx: &mut App) { · 37 unmodified lines
903913 let language = Arc::new(rust_lang());
904914 registry.add(language.clone());
905915
906− test_random_edits(text, registry, language, rng);
916+ test_random_edits(text, registry, language, rng, cx);
907917 }
908918
909919 #[gpui::test(iterations = 50)]
@@ -932,7 +942,7 @@ fn test_random_syntax_map_edits_with_erb(rng: StdRng, cx: &mut App) { · 22 unmodified lines
932942 registry.add(Arc::new(ruby_lang()));
933943 registry.add(Arc::new(html_lang()));
934944
935− test_random_edits(text, registry, language, rng);
945+ test_random_edits(text, registry, language, rng, cx);
936946 }
937947
938948 #[gpui::test(iterations = 50)]
@@ -965,7 +975,7 @@ fn test_random_syntax_map_edits_with_heex(rng: StdRng, cx: &mut App) { · 26 unmodified lines
965975 registry.add(Arc::new(heex_lang()));
966976 registry.add(Arc::new(html_lang()));
967977
968− test_random_edits(text, registry, language, rng);
978+ test_random_edits(text, registry, language, rng, cx);
969979 }
970980
971981 fn test_random_edits(
@@ -973,12 +983,18 @@ fn test_random_edits( · 1 unmodified line
973983 registry: Arc<LanguageRegistry>,
974984 language: Arc<Language>,
975985 mut rng: StdRng,
986+ cx: &mut App,
976987 ) {
977988 let operations = env::var("OPERATIONS")
978989 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
979990 .unwrap_or(10);
980991
981− let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), text);
992+ let mut buffer = Buffer::new(
993+ ReplicaId::LOCAL,
994+ BufferId::new(1).unwrap(),
995+ text,
996+ cx.background_executor(),
997+ );
982998
983999 let mut syntax_map = SyntaxMap::new(&buffer);
9841000 syntax_map.set_language_registry(registry.clone());
@@ -993,7 +1009,7 @@ fn test_random_edits( · 8 unmodified lines
9931009 let prev_buffer = buffer.snapshot();
9941010 let prev_syntax_map = syntax_map.snapshot();
9951011
996− buffer.randomly_edit(&mut rng, 3);
1012+ buffer.randomly_edit(&mut rng, 3, cx.background_executor());
9971013 log::info!("text:\n{}", buffer.text());
9981014
9991015 syntax_map.interpolate(&buffer);
@@ -1159,7 +1175,12 @@ fn test_edit_sequence(language_name: &str, steps: &[&str], cx: &mut App) -> (Buf · 159 unmodified lines
11591175 .now_or_never()
11601176 .unwrap()
11611177 .unwrap();
1162− let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "");
1178+ let mut buffer = Buffer::new(
1179+ ReplicaId::LOCAL,
1180+ BufferId::new(1).unwrap(),
1181+ "",
1182+ cx.background_executor(),
1183+ );
11631184
11641185 let mut mutated_syntax_map = SyntaxMap::new(&buffer);
11651186 mutated_syntax_map.set_language_registry(registry.clone());
@@ -1168,7 +1189,7 @@ fn test_edit_sequence(language_name: &str, steps: &[&str], cx: &mut App) -> (Buf · 2 unmodified lines
11681189 for (i, marked_string) in steps.iter().enumerate() {
11691190 let marked_string = marked_string.unindent();
11701191 log::info!("incremental parse {i}: {marked_string:?}");
1171− buffer.edit_via_marked_text(&marked_string);
1192+ buffer.edit_via_marked_text(&marked_string, cx.background_executor());
11721193
11731194 // Reparse the syntax map
11741195 mutated_syntax_map.interpolate(&buffer);
crates/language_extension/src/extension_lsp_adapter.rs+5−2
@@ -11,7 +11,7 @@ use futures::{Future, FutureExt, future::join_all}; · 10 unmodified lines
1111 use gpui::{App, AppContext, AsyncApp, Task};
1212 use language::{
1313 BinaryStatus, CodeLabel, DynLspInstaller, HighlightId, Language, LanguageName, LspAdapter,
14− LspAdapterDelegate, Toolchain,
14+ LspAdapterDelegate, Rope, Toolchain,
1515 };
1616 use lsp::{
1717 CodeActionKind, LanguageServerBinary, LanguageServerBinaryOptions, LanguageServerName,
@@ -403,7 +403,10 @@ fn labels_from_extension( · 385 unmodified lines
403403 let runs = if label.code.is_empty() {
404404 Vec::new()
405405 } else {
406− language.highlight_text(&label.code.as_str().into(), 0..label.code.len())
406+ language.highlight_text(
407+ &Rope::from_str_small(label.code.as_str()),
408+ 0..label.code.len(),
409+ )
407410 };
408411 build_code_label(&label, &runs, language)
409412 })
crates/languages/src/c.rs+6−4
@@ -189,7 +189,7 @@ impl super::LspAdapter for CLspAdapter { · 188 unmodified lines
189189 Some(lsp::CompletionItemKind::FIELD) if completion.detail.is_some() => {
190190 let detail = completion.detail.as_ref().unwrap();
191191 let text = format!("{} {}", detail, label);
192− let source = Rope::from(format!("struct S {{ {} }}", text).as_str());
192+ let source = Rope::from_str_small(format!("struct S {{ {} }}", text).as_str());
193193 let runs = language.highlight_text(&source, 11..11 + text.len());
194194 let filter_range = completion
195195 .filter_text
@@ -206,7 +206,8 @@ impl super::LspAdapter for CLspAdapter { · 10 unmodified lines
206206 {
207207 let detail = completion.detail.as_ref().unwrap();
208208 let text = format!("{} {}", detail, label);
209− let runs = language.highlight_text(&Rope::from(text.as_str()), 0..text.len());
209+ let runs =
210+ language.highlight_text(&Rope::from_str_small(text.as_str()), 0..text.len());
210211 let filter_range = completion
211212 .filter_text
212213 .as_deref()
@@ -222,7 +223,8 @@ impl super::LspAdapter for CLspAdapter { · 9 unmodified lines
222223 {
223224 let detail = completion.detail.as_ref().unwrap();
224225 let text = format!("{} {}", detail, label);
225− let runs = language.highlight_text(&Rope::from(text.as_str()), 0..text.len());
226+ let runs =
227+ language.highlight_text(&Rope::from_str_small(text.as_str()), 0..text.len());
226228 let filter_range = completion
227229 .filter_text
228230 .as_deref()
@@ -326,7 +328,7 @@ impl super::LspAdapter for CLspAdapter { · 97 unmodified lines
326328 Some(CodeLabel::new(
327329 text[display_range.clone()].to_string(),
328330 filter_range,
329− language.highlight_text(&text.as_str().into(), display_range),
331+ language.highlight_text(&Rope::from_str_small(text.as_str()), display_range),
330332 ))
331333 }
332334
crates/languages/src/go.rs+15−9
@@ -221,7 +221,7 @@ impl LspAdapter for GoLspAdapter { · 220 unmodified lines
221221 match completion.kind.zip(completion.detail.as_ref()) {
222222 Some((lsp::CompletionItemKind::MODULE, detail)) => {
223223 let text = format!("{label} {detail}");
224− let source = Rope::from(format!("import {text}").as_str());
224+ let source = Rope::from_str_small(format!("import {text}").as_str());
225225 let runs = language.highlight_text(&source, 7..7 + text[name_offset..].len());
226226 let filter_range = completion
227227 .filter_text
@@ -238,8 +238,9 @@ impl LspAdapter for GoLspAdapter { · 10 unmodified lines
238238 detail,
239239 )) => {
240240 let text = format!("{label} {detail}");
241− let source =
242− Rope::from(format!("var {} {}", &text[name_offset..], detail).as_str());
241+ let source = Rope::from_str_small(
242+ format!("var {} {}", &text[name_offset..], detail).as_str(),
243+ );
243244 let runs = adjust_runs(
244245 name_offset,
245246 language.highlight_text(&source, 4..4 + text[name_offset..].len()),
@@ -256,7 +257,8 @@ impl LspAdapter for GoLspAdapter { · 10 unmodified lines
256257 }
257258 Some((lsp::CompletionItemKind::STRUCT, _)) => {
258259 let text = format!("{label} struct {{}}");
259− let source = Rope::from(format!("type {}", &text[name_offset..]).as_str());
260+ let source =
261+ Rope::from_str_small(format!("type {}", &text[name_offset..]).as_str());
260262 let runs = adjust_runs(
261263 name_offset,
262264 language.highlight_text(&source, 5..5 + text[name_offset..].len()),
@@ -273,7 +275,8 @@ impl LspAdapter for GoLspAdapter { · 10 unmodified lines
273275 }
274276 Some((lsp::CompletionItemKind::INTERFACE, _)) => {
275277 let text = format!("{label} interface {{}}");
276− let source = Rope::from(format!("type {}", &text[name_offset..]).as_str());
278+ let source =
279+ Rope::from_str_small(format!("type {}", &text[name_offset..]).as_str());
277280 let runs = adjust_runs(
278281 name_offset,
279282 language.highlight_text(&source, 5..5 + text[name_offset..].len()),
@@ -290,8 +293,9 @@ impl LspAdapter for GoLspAdapter { · 10 unmodified lines
290293 }
291294 Some((lsp::CompletionItemKind::FIELD, detail)) => {
292295 let text = format!("{label} {detail}");
293− let source =
294− Rope::from(format!("type T struct {{ {} }}", &text[name_offset..]).as_str());
296+ let source = Rope::from_str_small(
297+ format!("type T struct {{ {} }}", &text[name_offset..]).as_str(),
298+ );
295299 let runs = adjust_runs(
296300 name_offset,
297301 language.highlight_text(&source, 16..16 + text[name_offset..].len()),
@@ -309,7 +313,9 @@ impl LspAdapter for GoLspAdapter { · 11 unmodified lines
309313 Some((lsp::CompletionItemKind::FUNCTION | lsp::CompletionItemKind::METHOD, detail)) => {
310314 if let Some(signature) = detail.strip_prefix("func") {
311315 let text = format!("{label}{signature}");
312− let source = Rope::from(format!("func {} {{}}", &text[name_offset..]).as_str());
316+ let source = Rope::from_str_small(
317+ format!("func {} {{}}", &text[name_offset..]).as_str(),
318+ );
313319 let runs = adjust_runs(
314320 name_offset,
315321 language.highlight_text(&source, 5..5 + text[name_offset..].len()),
@@ -385,7 +391,7 @@ impl LspAdapter for GoLspAdapter { · 69 unmodified lines
385391 Some(CodeLabel::new(
386392 text[display_range.clone()].to_string(),
387393 filter_range,
388− language.highlight_text(&text.as_str().into(), display_range),
394+ language.highlight_text(&Rope::from_str_small(text.as_str()), display_range),
389395 ))
390396 }
391397
crates/languages/src/python.rs+4−3
@@ -19,6 +19,7 @@ use pet_core::python_environment::{PythonEnvironment, PythonEnvironmentKind}; · 18 unmodified lines
1919 use pet_virtualenv::is_virtualenv_dir;
2020 use project::Fs;
2121 use project::lsp_store::language_server_settings;
22+use rope::Rope;
2223 use serde::{Deserialize, Serialize};
2324 use serde_json::{Value, json};
2425 use smol::lock::OnceCell;
@@ -466,7 +467,7 @@ impl LspAdapter for PyrightLspAdapter { · 441 unmodified lines
466467 Some(language::CodeLabel::new(
467468 text[display_range.clone()].to_string(),
468469 filter_range,
469− language.highlight_text(&text.as_str().into(), display_range),
470+ language.highlight_text(&Rope::from_str_small(text.as_str()), display_range),
470471 ))
471472 }
472473
@@ -1511,7 +1512,7 @@ impl LspAdapter for PyLspAdapter { · 1038 unmodified lines
15111512 Some(language::CodeLabel::new(
15121513 text[display_range.clone()].to_string(),
15131514 filter_range,
1514− language.highlight_text(&text.as_str().into(), display_range),
1515+ language.highlight_text(&Rope::from_str_small(text.as_str()), display_range),
15151516 ))
15161517 }
15171518
@@ -1800,7 +1801,7 @@ impl LspAdapter for BasedPyrightLspAdapter { · 282 unmodified lines
18001801 Some(language::CodeLabel::new(
18011802 text[display_range.clone()].to_string(),
18021803 filter_range,
1803− language.highlight_text(&text.as_str().into(), display_range),
1804+ language.highlight_text(&Rope::from_str_small(text.as_str()), display_range),
18041805 ))
18051806 }
18061807
crates/languages/src/rust.rs+9−6
@@ -252,7 +252,7 @@ impl LspAdapter for RustLspAdapter { · 251 unmodified lines
252252 let name = &completion.label;
253253 let text = format!("{name}: {signature}");
254254 let prefix = "struct S { ";
255− let source = Rope::from_iter([prefix, &text, " }"]);
255+ let source = Rope::from_iter_small([prefix, &text, " }"]);
256256 let runs =
257257 language.highlight_text(&source, prefix.len()..prefix.len() + text.len());
258258 mk_label(text, &|| 0..completion.label.len(), runs)
@@ -264,7 +264,7 @@ impl LspAdapter for RustLspAdapter { · 5 unmodified lines
264264 let name = &completion.label;
265265 let text = format!("{name}: {signature}",);
266266 let prefix = "let ";
267− let source = Rope::from_iter([prefix, &text, " = ();"]);
267+ let source = Rope::from_iter_small([prefix, &text, " = ();"]);
268268 let runs =
269269 language.highlight_text(&source, prefix.len()..prefix.len() + text.len());
270270 mk_label(text, &|| 0..completion.label.len(), runs)
@@ -302,7 +302,7 @@ impl LspAdapter for RustLspAdapter { · 31 unmodified lines
302302 .filter(|it| it.contains(&label))
303303 .and_then(|it| Some((it, FULL_SIGNATURE_REGEX.find(it)?)))
304304 {
305− let source = Rope::from(function_signature);
305+ let source = Rope::from_str_small(function_signature);
306306 let runs = language.highlight_text(&source, 0..function_signature.len());
307307 mk_label(
308308 function_signature.to_owned(),
@@ -311,7 +311,7 @@ impl LspAdapter for RustLspAdapter { · 2 unmodified lines
311311 )
312312 } else if let Some((prefix, suffix)) = fn_prefixed {
313313 let text = format!("{label}{suffix}");
314− let source = Rope::from_iter([prefix, " ", &text, " {}"]);
314+ let source = Rope::from_iter_small([prefix, " ", &text, " {}"]);
315315 let run_start = prefix.len() + 1;
316316 let runs = language.highlight_text(&source, run_start..run_start + text.len());
317317 mk_label(text, &|| 0..label.len(), runs)
@@ -322,7 +322,7 @@ impl LspAdapter for RustLspAdapter { · 4 unmodified lines
322322 {
323323 let text = completion.label.clone();
324324 let len = text.len();
325− let source = Rope::from(text.as_str());
325+ let source = Rope::from_str_small(text.as_str());
326326 let runs = language.highlight_text(&source, 0..len);
327327 mk_label(text, &|| 0..completion.label.len(), runs)
328328 } else if detail_left.is_none() {
@@ -399,7 +399,10 @@ impl LspAdapter for RustLspAdapter { · 70 unmodified lines
399399 Some(CodeLabel::new(
400400 format!("{prefix}{name}"),
401401 filter_range,
402− language.highlight_text(&Rope::from_iter([prefix, name, suffix]), display_range),
402+ language.highlight_text(
403+ &Rope::from_iter_small([prefix, name, suffix]),
404+ display_range,
405+ ),
403406 ))
404407 }
405408
crates/markdown/src/markdown.rs+3−1
@@ -1558,7 +1558,9 @@ impl MarkdownElementBuilder { · 1557 unmodified lines
15581558
15591559 if let Some(Some(language)) = self.code_block_stack.last() {
15601560 let mut offset = 0;
1561− for (range, highlight_id) in language.highlight_text(&Rope::from(text), 0..text.len()) {
1561+ for (range, highlight_id) in
1562+ language.highlight_text(&Rope::from_str_small(text), 0..text.len())
1563+ {
15621564 if range.start > offset {
15631565 self.pending_line
15641566 .runs
crates/markdown_preview/src/markdown_parser.rs+1−1
@@ -779,7 +779,7 @@ impl<'a> MarkdownParser<'a> { · 778 unmodified lines
779779
780780 let highlights = if let Some(language) = &language {
781781 if let Some(registry) = &self.language_registry {
782− let rope: language::Rope = code.as_str().into();
782+ let rope = language::Rope::from_str_small(code.as_str());
783783 registry
784784 .language_for_name_or_extension(language)
785785 .await
crates/multi_buffer/src/multi_buffer_tests.rs+19−14
@@ -1,6 +1,6 @@
11 use super::*;
22 use buffer_diff::{DiffHunkStatus, DiffHunkStatusKind};
3−use gpui::{App, TestAppContext};
3+use gpui::{App, BackgroundExecutor, TestAppContext};
44 use indoc::indoc;
55 use language::{Buffer, Rope};
66 use parking_lot::RwLock;
@@ -79,9 +79,14 @@ fn test_remote(cx: &mut App) { · 72 unmodified lines
7979 let ops = cx
8080 .background_executor()
8181 .block(host_buffer.read(cx).serialize_ops(None, cx));
82− let mut buffer =
83− Buffer::from_proto(ReplicaId::REMOTE_SERVER, Capability::ReadWrite, state, None)
84− .unwrap();
82+ let mut buffer = Buffer::from_proto(
83+ ReplicaId::REMOTE_SERVER,
84+ Capability::ReadWrite,
85+ state,
86+ None,
87+ cx.background_executor(),
88+ )
89+ .unwrap();
8590 buffer.apply_ops(
8691 ops.into_iter()
8792 .map(|op| language::proto::deserialize_operation(op).unwrap()),
@@ -1224,7 +1229,7 @@ fn test_basic_diff_hunks(cx: &mut TestAppContext) { · 1136 unmodified lines
12241229 assert_chunks_in_ranges(&snapshot);
12251230 assert_consistent_line_numbers(&snapshot);
12261231 assert_position_translation(&snapshot);
1227− assert_line_indents(&snapshot);
1232+ assert_line_indents(&snapshot, cx.background_executor());
12281233
12291234 multibuffer.update(cx, |multibuffer, cx| {
12301235 multibuffer.collapse_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
@@ -1248,7 +1253,7 @@ fn test_basic_diff_hunks(cx: &mut TestAppContext) { · 17 unmodified lines
12481253 assert_chunks_in_ranges(&snapshot);
12491254 assert_consistent_line_numbers(&snapshot);
12501255 assert_position_translation(&snapshot);
1251− assert_line_indents(&snapshot);
1256+ assert_line_indents(&snapshot, cx.background_executor());
12521257
12531258 // Expand the first diff hunk
12541259 multibuffer.update(cx, |multibuffer, cx| {
@@ -1300,7 +1305,7 @@ fn test_basic_diff_hunks(cx: &mut TestAppContext) { · 45 unmodified lines
13001305 assert_chunks_in_ranges(&snapshot);
13011306 assert_consistent_line_numbers(&snapshot);
13021307 assert_position_translation(&snapshot);
1303− assert_line_indents(&snapshot);
1308+ assert_line_indents(&snapshot, cx.background_executor());
13041309
13051310 // Edit the buffer before the first hunk
13061311 buffer.update(cx, |buffer, cx| {
@@ -1342,7 +1347,7 @@ fn test_basic_diff_hunks(cx: &mut TestAppContext) { · 35 unmodified lines
13421347 assert_chunks_in_ranges(&snapshot);
13431348 assert_consistent_line_numbers(&snapshot);
13441349 assert_position_translation(&snapshot);
1345− assert_line_indents(&snapshot);
1350+ assert_line_indents(&snapshot, cx.background_executor());
13461351
13471352 // Recalculate the diff, changing the first diff hunk.
13481353 diff.update(cx, |diff, cx| {
@@ -2067,7 +2072,7 @@ fn test_diff_hunks_with_multiple_excerpts(cx: &mut TestAppContext) { · 718 unmodified lines
20672072 }
20682073
20692074 assert_position_translation(&snapshot);
2070− assert_line_indents(&snapshot);
2075+ assert_line_indents(&snapshot, cx.background_executor());
20712076
20722077 assert_eq!(
20732078 snapshot
@@ -2118,7 +2123,7 @@ fn test_diff_hunks_with_multiple_excerpts(cx: &mut TestAppContext) { · 44 unmodified lines
21182123 ),
21192124 );
21202125
2121− assert_line_indents(&snapshot);
2126+ assert_line_indents(&snapshot, cx.background_executor());
21222127 }
21232128
21242129 /// A naive implementation of a multi-buffer that does not maintain
@@ -2888,7 +2893,7 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) { · 763 unmodified lines
28882893 );
28892894 }
28902895
2891− let text_rope = Rope::from(expected_text.as_str());
2896+ let text_rope = Rope::from_str(expected_text.as_str(), cx.background_executor());
28922897 for _ in 0..10 {
28932898 let end_ix = text_rope.clip_offset(rng.random_range(0..=text_rope.len()), Bias::Right);
28942899 let start_ix = text_rope.clip_offset(rng.random_range(0..=end_ix), Bias::Left);
@@ -3512,7 +3517,7 @@ fn assert_consistent_line_numbers(snapshot: &MultiBufferSnapshot) { · 617 unmodified lines
35123517
35133518 #[track_caller]
35143519 fn assert_position_translation(snapshot: &MultiBufferSnapshot) {
3515− let text = Rope::from(snapshot.text());
3520+ let text = Rope::from_str_small(&snapshot.text());
35163521
35173522 let mut left_anchors = Vec::new();
35183523 let mut right_anchors = Vec::new();
@@ -3636,10 +3641,10 @@ fn assert_position_translation(snapshot: &MultiBufferSnapshot) { · 117 unmodified lines
36363641 }
36373642 }
36383643
3639−fn assert_line_indents(snapshot: &MultiBufferSnapshot) {
3644+fn assert_line_indents(snapshot: &MultiBufferSnapshot, executor: &BackgroundExecutor) {
36403645 let max_row = snapshot.max_point().row;
36413646 let buffer_id = snapshot.excerpts().next().unwrap().1.remote_id();
3642− let text = text::Buffer::new(ReplicaId::LOCAL, buffer_id, snapshot.text());
3647+ let text = text::Buffer::new(ReplicaId::LOCAL, buffer_id, snapshot.text(), executor);
36433648 let mut line_indents = text
36443649 .line_indents_in_row_range(0..max_row + 1)
36453650 .collect::<Vec<_>>();
crates/project/src/buffer_store.rs+15−3
@@ -180,7 +180,13 @@ impl RemoteBufferStore { · 179 unmodified lines
180180 buffer_file = Some(Arc::new(File::from_proto(file, worktree, cx)?)
181181 as Arc<dyn language::File>);
182182 }
183− Buffer::from_proto(replica_id, capability, state, buffer_file)
183+ Buffer::from_proto(
184+ replica_id,
185+ capability,
186+ state,
187+ buffer_file,
188+ cx.background_executor(),
189+ )
184190 });
185191
186192 match buffer_result {
@@ -628,9 +634,10 @@ impl LocalBufferStore { · 441 unmodified lines
628634 Ok(loaded) => {
629635 let reservation = cx.reserve_entity::<Buffer>()?;
630636 let buffer_id = BufferId::from(reservation.entity_id().as_non_zero_u64());
637+ let executor = cx.background_executor().clone();
631638 let text_buffer = cx
632639 .background_spawn(async move {
633− text::Buffer::new(ReplicaId::LOCAL, buffer_id, loaded.text)
640+ text::Buffer::new(ReplicaId::LOCAL, buffer_id, loaded.text, &executor)
634641 })
635642 .await;
636643 cx.insert_entity(reservation, |_| {
@@ -639,7 +646,12 @@ impl LocalBufferStore { · 2 unmodified lines
639646 }
640647 Err(error) if is_not_found_error(&error) => cx.new(|cx| {
641648 let buffer_id = BufferId::from(cx.entity_id().as_non_zero_u64());
642− let text_buffer = text::Buffer::new(ReplicaId::LOCAL, buffer_id, "");
649+ let text_buffer = text::Buffer::new(
650+ ReplicaId::LOCAL,
651+ buffer_id,
652+ "",
653+ cx.background_executor(),
654+ );
643655 Buffer::build(
644656 text_buffer,
645657 Some(Arc::new(File {
crates/project/src/git_store/conflict_set.rs+32−12
@@ -276,8 +276,8 @@ mod tests { · 275 unmodified lines
276276 use util::{path, rel_path::rel_path};
277277 use worktree::WorktreeSettings;
278278
279− #[test]
280− fn test_parse_conflicts_in_buffer() {
279+ #[gpui::test]
280+ fn test_parse_conflicts_in_buffer(cx: &mut TestAppContext) {
281281 // Create a buffer with conflict markers
282282 let test_content = r#"
283283 This is some text before the conflict.
@@ -299,7 +299,12 @@ mod tests { · 15 unmodified lines
299299 .unindent();
300300
301301 let buffer_id = BufferId::new(1).unwrap();
302− let buffer = Buffer::new(ReplicaId::LOCAL, buffer_id, test_content);
302+ let buffer = Buffer::new(
303+ ReplicaId::LOCAL,
304+ buffer_id,
305+ test_content,
306+ cx.background_executor(),
307+ );
303308 let snapshot = buffer.snapshot();
304309
305310 let conflict_snapshot = ConflictSet::parse(&snapshot);
@@ -355,8 +360,8 @@ mod tests { · 49 unmodified lines
355360 assert_eq!(conflicts_in_range.len(), 0);
356361 }
357362
358− #[test]
359− fn test_nested_conflict_markers() {
363+ #[gpui::test]
364+ fn test_nested_conflict_markers(cx: &mut TestAppContext) {
360365 // Create a buffer with nested conflict markers
361366 let test_content = r#"
362367 This is some text before the conflict.
@@ -374,7 +379,12 @@ mod tests { · 11 unmodified lines
374379 .unindent();
375380
376381 let buffer_id = BufferId::new(1).unwrap();
377− let buffer = Buffer::new(ReplicaId::LOCAL, buffer_id, test_content);
382+ let buffer = Buffer::new(
383+ ReplicaId::LOCAL,
384+ buffer_id,
385+ test_content,
386+ cx.background_executor(),
387+ );
378388 let snapshot = buffer.snapshot();
379389
380390 let conflict_snapshot = ConflictSet::parse(&snapshot);
@@ -396,8 +406,8 @@ mod tests { · 15 unmodified lines
396406 assert_eq!(their_text, "This is their version in a nested conflict\n");
397407 }
398408
399− #[test]
400− fn test_conflict_markers_at_eof() {
409+ #[gpui::test]
410+ fn test_conflict_markers_at_eof(cx: &mut TestAppContext) {
401411 let test_content = r#"
402412 <<<<<<< ours
403413 =======
@@ -405,15 +415,20 @@ mod tests { · 1 unmodified line
405415 >>>>>>> "#
406416 .unindent();
407417 let buffer_id = BufferId::new(1).unwrap();
408− let buffer = Buffer::new(ReplicaId::LOCAL, buffer_id, test_content);
418+ let buffer = Buffer::new(
419+ ReplicaId::LOCAL,
420+ buffer_id,
421+ test_content,
422+ cx.background_executor(),
423+ );
409424 let snapshot = buffer.snapshot();
410425
411426 let conflict_snapshot = ConflictSet::parse(&snapshot);
412427 assert_eq!(conflict_snapshot.conflicts.len(), 1);
413428 }
414429
415− #[test]
416− fn test_conflicts_in_range() {
430+ #[gpui::test]
431+ fn test_conflicts_in_range(cx: &mut TestAppContext) {
417432 // Create a buffer with conflict markers
418433 let test_content = r#"
419434 one
@@ -447,7 +462,12 @@ mod tests { · 27 unmodified lines
447462 .unindent();
448463
449464 let buffer_id = BufferId::new(1).unwrap();
450− let buffer = Buffer::new(ReplicaId::LOCAL, buffer_id, test_content.clone());
465+ let buffer = Buffer::new(
466+ ReplicaId::LOCAL,
467+ buffer_id,
468+ test_content.clone(),
469+ cx.background_executor(),
470+ );
451471 let snapshot = buffer.snapshot();
452472
453473 let conflict_snapshot = ConflictSet::parse(&snapshot);
crates/project/src/prettier_store.rs+91−48
@@ -13,7 +13,9 @@ use futures::{ · 12 unmodified lines
1313 future::{self, Shared},
1414 stream::FuturesUnordered,
1515 };
16−use gpui::{AppContext as _, AsyncApp, Context, Entity, EventEmitter, Task, WeakEntity};
16+use gpui::{
17+ AppContext as _, AsyncApp, BackgroundExecutor, Context, Entity, EventEmitter, Task, WeakEntity,
18+};
1719 use language::{
1820 Buffer, LanguageRegistry, LocalFile,
1921 language_settings::{Formatter, LanguageSettings},
@@ -558,99 +560,137 @@ impl PrettierStore { · 538 unmodified lines
558560 let plugins_to_install = new_plugins.clone();
559561 let fs = Arc::clone(&self.fs);
560562 let new_installation_task = cx
561− .spawn(async move |prettier_store, cx| {
562− cx.background_executor().timer(Duration::from_millis(30)).await;
563+ .spawn(async move |prettier_store, cx| {
564+ cx.background_executor()
565+ .timer(Duration::from_millis(30))
566+ .await;
563567 let location_data = prettier_store.update(cx, |prettier_store, cx| {
564− worktree.and_then(|worktree_id| {
565− prettier_store.worktree_store
566− .read(cx)
567− .worktree_for_id(worktree_id, cx)
568− .map(|worktree| worktree.read(cx).abs_path())
569− }).map(|locate_from| {
570− let installed_prettiers = prettier_store.prettier_instances.keys().cloned().collect();
571− (locate_from, installed_prettiers)
572− })
568+ worktree
569+ .and_then(|worktree_id| {
570+ prettier_store
571+ .worktree_store
572+ .read(cx)
573+ .worktree_for_id(worktree_id, cx)
574+ .map(|worktree| worktree.read(cx).abs_path())
575+ })
576+ .map(|locate_from| {
577+ let installed_prettiers =
578+ prettier_store.prettier_instances.keys().cloned().collect();
579+ (locate_from, installed_prettiers)
580+ })
573581 })?;
574582 let locate_prettier_installation = match location_data {
575− Some((locate_from, installed_prettiers)) => Prettier::locate_prettier_installation(
576− fs.as_ref(),
577− &installed_prettiers,
578− locate_from.as_ref(),
579− )
580− .await
581− .context("locate prettier installation").map_err(Arc::new)?,
583+ Some((locate_from, installed_prettiers)) => {
584+ Prettier::locate_prettier_installation(
585+ fs.as_ref(),
586+ &installed_prettiers,
587+ locate_from.as_ref(),
588+ )
589+ .await
590+ .context("locate prettier installation")
591+ .map_err(Arc::new)?
592+ }
582593 None => ControlFlow::Continue(None),
583594 };
584595
585− match locate_prettier_installation
586− {
596+ match locate_prettier_installation {
587597 ControlFlow::Break(()) => return Ok(()),
588598 ControlFlow::Continue(prettier_path) => {
589599 if prettier_path.is_some() {
590600 new_plugins.clear();
591601 }
592− let mut needs_install = should_write_prettier_server_file(fs.as_ref()).await;
602+ let mut needs_install =
603+ should_write_prettier_server_file(fs.as_ref()).await;
593604 if let Some(previous_installation_task) = previous_installation_task
594− && let Err(e) = previous_installation_task.await {
595− log::error!("Failed to install default prettier: {e:#}");
596− prettier_store.update(cx, |prettier_store, _| {
597− if let PrettierInstallation::NotInstalled { attempts, not_installed_plugins, .. } = &mut prettier_store.default_prettier.prettier {
598− *attempts += 1;
599− new_plugins.extend(not_installed_plugins.iter().cloned());
600− installation_attempt = *attempts;
601− needs_install = true;
602− };
603− })?;
604− };
605+ && let Err(e) = previous_installation_task.await
606+ {
607+ log::error!("Failed to install default prettier: {e:#}");
608+ prettier_store.update(cx, |prettier_store, _| {
609+ if let PrettierInstallation::NotInstalled {
610+ attempts,
611+ not_installed_plugins,
612+ ..
613+ } = &mut prettier_store.default_prettier.prettier
614+ {
615+ *attempts += 1;
616+ new_plugins.extend(not_installed_plugins.iter().cloned());
617+ installation_attempt = *attempts;
618+ needs_install = true;
619+ };
620+ })?;
621+ };
605622 if installation_attempt > prettier::FAIL_THRESHOLD {
606623 prettier_store.update(cx, |prettier_store, _| {
607− if let PrettierInstallation::NotInstalled { installation_task, .. } = &mut prettier_store.default_prettier.prettier {
624+ if let PrettierInstallation::NotInstalled {
625+ installation_task,
626+ ..
627+ } = &mut prettier_store.default_prettier.prettier
628+ {
608629 *installation_task = None;
609630 };
610631 })?;
611632 log::warn!(
612− "Default prettier installation had failed {installation_attempt} times, not attempting again",
633+ "Default prettier installation had failed {installation_attempt} \
634+ times, not attempting again",
613635 );
614636 return Ok(());
615637 }
616638 prettier_store.update(cx, |prettier_store, _| {
617639 new_plugins.retain(|plugin| {
618− !prettier_store.default_prettier.installed_plugins.contains(plugin)
640+ !prettier_store
641+ .default_prettier
642+ .installed_plugins
643+ .contains(plugin)
619644 });
620− if let PrettierInstallation::NotInstalled { not_installed_plugins, .. } = &mut prettier_store.default_prettier.prettier {
645+ if let PrettierInstallation::NotInstalled {
646+ not_installed_plugins,
647+ ..
648+ } = &mut prettier_store.default_prettier.prettier
649+ {
621650 not_installed_plugins.retain(|plugin| {
622− !prettier_store.default_prettier.installed_plugins.contains(plugin)
651+ !prettier_store
652+ .default_prettier
653+ .installed_plugins
654+ .contains(plugin)
623655 });
624656 not_installed_plugins.extend(new_plugins.iter().cloned());
625657 }
626658 needs_install |= !new_plugins.is_empty();
627659 })?;
628660 if needs_install {
629− log::info!("Initializing default prettier with plugins {new_plugins:?}");
661+ log::info!(
662+ "Initializing default prettier with plugins {new_plugins:?}"
663+ );
630664 let installed_plugins = new_plugins.clone();
665+ let executor = cx.background_executor().clone();
631666 cx.background_spawn(async move {
632667 install_prettier_packages(fs.as_ref(), new_plugins, node).await?;
633668 // Save the server file last, so the reinstall need could be determined by the absence of the file.
634− save_prettier_server_file(fs.as_ref()).await?;
669+ save_prettier_server_file(fs.as_ref(), &executor).await?;
635670 anyhow::Ok(())
636671 })
637− .await
638− .context("prettier & plugins install")
639− .map_err(Arc::new)?;
640− log::info!("Initialized default prettier with plugins: {installed_plugins:?}");
672+ .await
673+ .context("prettier & plugins install")
674+ .map_err(Arc::new)?;
675+ log::info!(
676+ "Initialized default prettier with plugins: {installed_plugins:?}"
677+ );
641678 prettier_store.update(cx, |prettier_store, _| {
642679 prettier_store.default_prettier.prettier =
643680 PrettierInstallation::Installed(PrettierInstance {
644681 attempt: 0,
645682 prettier: None,
646683 });
647− prettier_store.default_prettier
684+ prettier_store
685+ .default_prettier
648686 .installed_plugins
649687 .extend(installed_plugins);
650688 })?;
651689 } else {
652690 prettier_store.update(cx, |prettier_store, _| {
653− if let PrettierInstallation::NotInstalled { .. } = &mut prettier_store.default_prettier.prettier {
691+ if let PrettierInstallation::NotInstalled { .. } =
692+ &mut prettier_store.default_prettier.prettier
693+ {
654694 prettier_store.default_prettier.prettier =
655695 PrettierInstallation::Installed(PrettierInstance {
656696 attempt: 0,
@@ -936,11 +976,14 @@ async fn install_prettier_packages( · 279 unmodified lines
936976 anyhow::Ok(())
937977 }
938978
939−async fn save_prettier_server_file(fs: &dyn Fs) -> anyhow::Result<()> {
979+async fn save_prettier_server_file(
980+ fs: &dyn Fs,
981+ executor: &BackgroundExecutor,
982+) -> anyhow::Result<()> {
940983 let prettier_wrapper_path = default_prettier_dir().join(prettier::PRETTIER_SERVER_FILE);
941984 fs.save(
942985 &prettier_wrapper_path,
943− &text::Rope::from(prettier::PRETTIER_SERVER_JS),
986+ &text::Rope::from_str(prettier::PRETTIER_SERVER_JS, executor),
944987 text::LineEnding::Unix,
945988 )
946989 .await
crates/project/src/project.rs+10−3
@@ -712,8 +712,10 @@ pub enum ResolveState { · 711 unmodified lines
712712 impl InlayHint {
713713 pub fn text(&self) -> Rope {
714714 match &self.label {
715− InlayHintLabel::String(s) => Rope::from(s),
716− InlayHintLabel::LabelParts(parts) => parts.iter().map(|part| &*part.value).collect(),
715+ InlayHintLabel::String(s) => Rope::from_str_small(s),
716+ InlayHintLabel::LabelParts(parts) => {
717+ Rope::from_iter_small(parts.iter().map(|part| &*part.value))
718+ }
717719 }
718720 }
719721 }
@@ -5402,7 +5404,12 @@ impl Project { · 4682 unmodified lines
54025404 worktree
54035405 .update(cx, |worktree, cx| {
54045406 let line_ending = text::LineEnding::detect(&new_text);
5405− worktree.write_file(rel_path.clone(), new_text.into(), line_ending, cx)
5407+ worktree.write_file(
5408+ rel_path.clone(),
5409+ Rope::from_str(&new_text, cx.background_executor()),
5410+ line_ending,
5411+ cx,
5412+ )
54065413 })?
54075414 .await
54085415 .context("Failed to write settings file")?;
crates/project/src/project_tests.rs+9−9
@@ -1461,21 +1461,21 @@ async fn test_reporting_fs_changes_to_language_servers(cx: &mut gpui::TestAppCon · 1460 unmodified lines
14611461 .unwrap();
14621462 fs.save(
14631463 path!("/the-root/Cargo.lock").as_ref(),
1464− &"".into(),
1464+ &Rope::default(),
14651465 Default::default(),
14661466 )
14671467 .await
14681468 .unwrap();
14691469 fs.save(
14701470 path!("/the-stdlib/LICENSE").as_ref(),
1471− &"".into(),
1471+ &Rope::default(),
14721472 Default::default(),
14731473 )
14741474 .await
14751475 .unwrap();
14761476 fs.save(
14771477 path!("/the/stdlib/src/string.rs").as_ref(),
1478− &"".into(),
1478+ &Rope::default(),
14791479 Default::default(),
14801480 )
14811481 .await
@@ -4072,7 +4072,7 @@ async fn test_file_changes_multiple_times_on_disk(cx: &mut gpui::TestAppContext) · 2590 unmodified lines
40724072 // to be detected by the worktree, so that the buffer starts reloading.
40734073 fs.save(
40744074 path!("/dir/file1").as_ref(),
4075− &"the first contents".into(),
4075+ &Rope::from_str("the first contents", cx.background_executor()),
40764076 Default::default(),
40774077 )
40784078 .await
@@ -4083,7 +4083,7 @@ async fn test_file_changes_multiple_times_on_disk(cx: &mut gpui::TestAppContext) · 4 unmodified lines
40834083 // previous file change may still be in progress.
40844084 fs.save(
40854085 path!("/dir/file1").as_ref(),
4086− &"the second contents".into(),
4086+ &Rope::from_str("the second contents", cx.background_executor()),
40874087 Default::default(),
40884088 )
40894089 .await
@@ -4127,7 +4127,7 @@ async fn test_edit_buffer_while_it_reloads(cx: &mut gpui::TestAppContext) { · 37 unmodified lines
41274127 // to be detected by the worktree, so that the buffer starts reloading.
41284128 fs.save(
41294129 path!("/dir/file1").as_ref(),
4130− &"the first contents".into(),
4130+ &Rope::from_str("the first contents", cx.background_executor()),
41314131 Default::default(),
41324132 )
41334133 .await
@@ -4805,7 +4805,7 @@ async fn test_buffer_file_changes_on_disk(cx: &mut gpui::TestAppContext) { · 671 unmodified lines
48054805 marked_text_offsets("oneˇ\nthree ˇFOURˇ five\nsixtyˇ seven\n");
48064806 fs.save(
48074807 path!("/dir/the-file").as_ref(),
4808− &new_contents.as_str().into(),
4808+ &Rope::from_str(new_contents.as_str(), cx.background_executor()),
48094809 LineEnding::Unix,
48104810 )
48114811 .await
@@ -4837,7 +4837,7 @@ async fn test_buffer_file_changes_on_disk(cx: &mut gpui::TestAppContext) { · 25 unmodified lines
48374837 // Change the file on disk again, adding blank lines to the beginning.
48384838 fs.save(
48394839 path!("/dir/the-file").as_ref(),
4840− &"\n\n\nAAAA\naaa\nBB\nbbbbb\n".into(),
4840+ &Rope::from_str("\n\n\nAAAA\naaa\nBB\nbbbbb\n", cx.background_executor()),
48414841 LineEnding::Unix,
48424842 )
48434843 .await
@@ -4889,7 +4889,7 @@ async fn test_buffer_line_endings(cx: &mut gpui::TestAppContext) { · 45 unmodified lines
48894889 // state updates correctly.
48904890 fs.save(
48914891 path!("/dir/file1").as_ref(),
4892− &"aaa\nb\nc\n".into(),
4892+ &Rope::from_str("aaa\nb\nc\n", cx.background_executor()),
48934893 LineEnding::Windows,
48944894 )
48954895 .await
crates/remote_server/src/remote_editing_tests.rs+4−4
@@ -13,7 +13,7 @@ use fs::{FakeFs, Fs}; · 12 unmodified lines
1313 use gpui::{AppContext as _, Entity, SemanticVersion, SharedString, TestAppContext};
1414 use http_client::{BlockedHttpClient, FakeHttpClient};
1515 use language::{
16− Buffer, FakeLspAdapter, LanguageConfig, LanguageMatcher, LanguageRegistry, LineEnding,
16+ Buffer, FakeLspAdapter, LanguageConfig, LanguageMatcher, LanguageRegistry, LineEnding, Rope,
1717 language_settings::{AllLanguageSettings, language_settings},
1818 };
1919 use lsp::{CompletionContext, CompletionResponse, CompletionTriggerKind, LanguageServerName};
@@ -120,7 +120,7 @@ async fn test_basic_remote_editing(cx: &mut TestAppContext, server_cx: &mut Test · 100 unmodified lines
120120 // sees the new file.
121121 fs.save(
122122 path!("/code/project1/src/main.rs").as_ref(),
123− &"fn main() {}".into(),
123+ &Rope::from_str_small("fn main() {}"),
124124 Default::default(),
125125 )
126126 .await
@@ -766,7 +766,7 @@ async fn test_remote_reload(cx: &mut TestAppContext, server_cx: &mut TestAppCont · 639 unmodified lines
766766
767767 fs.save(
768768 &PathBuf::from(path!("/code/project1/src/lib.rs")),
769− &("bangles".to_string().into()),
769+ &Rope::from_str_small("bangles"),
770770 LineEnding::Unix,
771771 )
772772 .await
@@ -781,7 +781,7 @@ async fn test_remote_reload(cx: &mut TestAppContext, server_cx: &mut TestAppCont · 8 unmodified lines
781781
782782 fs.save(
783783 &PathBuf::from(path!("/code/project1/src/lib.rs")),
784− &("bloop".to_string().into()),
784+ &Rope::from_str_small("bloop"),
785785 LineEnding::Unix,
786786 )
787787 .await
crates/rich_text/src/rich_text.rs+12−5
@@ -1,9 +1,10 @@
11 use futures::FutureExt;
22 use gpui::{
3− AnyElement, AnyView, App, ElementId, FontStyle, FontWeight, HighlightStyle, InteractiveText,
4− IntoElement, SharedString, StrikethroughStyle, StyledText, UnderlineStyle, Window,
3+ AnyElement, AnyView, App, BackgroundExecutor, ElementId, FontStyle, FontWeight, HighlightStyle,
4+ InteractiveText, IntoElement, SharedString, StrikethroughStyle, StyledText, UnderlineStyle,
5+ Window,
56 };
6−use language::{HighlightId, Language, LanguageRegistry};
7+use language::{HighlightId, Language, LanguageRegistry, Rope};
78 use std::{ops::Range, sync::Arc};
89 use theme::ActiveTheme;
910 use ui::LinkPreview;
@@ -56,6 +57,7 @@ impl RichText { · 46 unmodified lines
5657 block: String,
5758 mentions: &[Mention],
5859 language_registry: &Arc<LanguageRegistry>,
60+ executor: &BackgroundExecutor,
5961 ) -> Self {
6062 let mut text = String::new();
6163 let mut highlights = Vec::new();
@@ -70,6 +72,7 @@ impl RichText { · 8 unmodified lines
7072 &mut highlights,
7173 &mut link_ranges,
7274 &mut link_urls,
75+ executor,
7376 );
7477 text.truncate(text.trim_end().len());
7578
@@ -184,6 +187,7 @@ pub fn render_markdown_mut( · 108 unmodified lines
184187 highlights: &mut Vec<(Range<usize>, Highlight)>,
185188 link_ranges: &mut Vec<Range<usize>>,
186189 link_urls: &mut Vec<String>,
190+ executor: &BackgroundExecutor,
187191 ) {
188192 use pulldown_cmark::{CodeBlockKind, Event, Options, Parser, Tag, TagEnd};
189193
@@ -202,7 +206,7 @@ pub fn render_markdown_mut( · 12 unmodified lines
202206 match event {
203207 Event::Text(t) => {
204208 if let Some(language) = &current_language {
205− render_code(text, highlights, t.as_ref(), language);
209+ render_code(text, highlights, t.as_ref(), language, executor);
206210 } else {
207211 while let Some(mention) = mentions.first() {
208212 if !source_range.contains_inclusive(&mention.range) {
@@ -373,11 +377,14 @@ pub fn render_code( · 164 unmodified lines
373377 highlights: &mut Vec<(Range<usize>, Highlight)>,
374378 content: &str,
375379 language: &Arc<Language>,
380+ executor: &BackgroundExecutor,
376381 ) {
377382 let prev_len = text.len();
378383 text.push_str(content);
379384 let mut offset = 0;
380− for (range, highlight_id) in language.highlight_text(&content.into(), 0..content.len()) {
385+ for (range, highlight_id) in
386+ language.highlight_text(&Rope::from_str(content, executor), 0..content.len())
387+ {
381388 if range.start > offset {
382389 highlights.push((prev_len + offset..prev_len + range.start, Highlight::Code));
383390 }
crates/rope/Cargo.toml+1−1
@@ -14,10 +14,10 @@ path = "src/rope.rs" · 13 unmodified lines
1414 [dependencies]
1515 arrayvec = "0.7.1"
1616 log.workspace = true
17−rayon.workspace = true
1817 sum_tree.workspace = true
1918 unicode-segmentation.workspace = true
2019 util.workspace = true
20+gpui.workspace = true
2121
2222 [dev-dependencies]
2323 ctor.workspace = true
crates/rope/benches/rope_benchmark.rs+27−10
@@ -3,6 +3,7 @@ use std::ops::Range; · 2 unmodified lines
33 use criterion::{
44 BatchSize, BenchmarkId, Criterion, Throughput, black_box, criterion_group, criterion_main,
55 };
6+use gpui::{AsyncApp, TestAppContext};
67 use rand::prelude::*;
78 use rand::rngs::StdRng;
89 use rope::{Point, Rope};
@@ -26,10 +27,10 @@ fn generate_random_text(rng: &mut StdRng, len: usize) -> String { · 17 unmodified lines
2627 str
2728 }
2829
29−fn generate_random_rope(rng: &mut StdRng, text_len: usize) -> Rope {
30+fn generate_random_rope(rng: &mut StdRng, text_len: usize, cx: &AsyncApp) -> Rope {
3031 let text = generate_random_text(rng, text_len);
3132 let mut rope = Rope::new();
32− rope.push(&text);
33+ rope.push(&text, cx.background_executor());
3334 rope
3435 }
3536
@@ -82,11 +83,13 @@ fn rope_benchmarks(c: &mut Criterion) { · 46 unmodified lines
8283 group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {
8384 let mut rng = StdRng::seed_from_u64(SEED);
8485 let text = generate_random_text(&mut rng, *size);
86+ let cx = TestAppContext::single();
87+ let cx = cx.to_async();
8588
8689 b.iter(|| {
8790 let mut rope = Rope::new();
8891 for _ in 0..10 {
89− rope.push(&text);
92+ rope.push(&text, cx.background_executor());
9093 }
9194 });
9295 });
@@ -99,8 +102,10 @@ fn rope_benchmarks(c: &mut Criterion) { · 6 unmodified lines
99102 group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {
100103 let mut rng = StdRng::seed_from_u64(SEED);
101104 let mut random_ropes = Vec::new();
105+ let cx = TestAppContext::single();
106+ let cx = cx.to_async();
102107 for _ in 0..5 {
103− let rope = generate_random_rope(&mut rng, *size);
108+ let rope = generate_random_rope(&mut rng, *size, &cx);
104109 random_ropes.push(rope);
105110 }
106111
@@ -119,7 +124,9 @@ fn rope_benchmarks(c: &mut Criterion) { · 12 unmodified lines
119124 group.throughput(Throughput::Bytes(*size as u64));
120125 group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {
121126 let mut rng = StdRng::seed_from_u64(SEED);
122− let rope = generate_random_rope(&mut rng, *size);
127+ let cx = TestAppContext::single();
128+ let cx = cx.to_async();
129+ let rope = generate_random_rope(&mut rng, *size, &cx);
123130
124131 b.iter_batched(
125132 || generate_random_rope_ranges(&mut rng, &rope),
@@ -139,7 +146,9 @@ fn rope_benchmarks(c: &mut Criterion) { · 13 unmodified lines
139146 group.throughput(Throughput::Bytes(*size as u64));
140147 group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {
141148 let mut rng = StdRng::seed_from_u64(SEED);
142− let rope = generate_random_rope(&mut rng, *size);
149+ let cx = TestAppContext::single();
150+ let cx = cx.to_async();
151+ let rope = generate_random_rope(&mut rng, *size, &cx);
143152
144153 b.iter_batched(
145154 || generate_random_rope_ranges(&mut rng, &rope),
@@ -160,7 +169,9 @@ fn rope_benchmarks(c: &mut Criterion) { · 14 unmodified lines
160169 group.throughput(Throughput::Bytes(*size as u64));
161170 group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {
162171 let mut rng = StdRng::seed_from_u64(SEED);
163− let rope = generate_random_rope(&mut rng, *size);
172+ let cx = TestAppContext::single();
173+ let cx = cx.to_async();
174+ let rope = generate_random_rope(&mut rng, *size, &cx);
164175
165176 b.iter(|| {
166177 let chars = rope.chars().count();
@@ -175,7 +186,9 @@ fn rope_benchmarks(c: &mut Criterion) { · 8 unmodified lines
175186 group.throughput(Throughput::Bytes(*size as u64));
176187 group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {
177188 let mut rng = StdRng::seed_from_u64(SEED);
178− let rope = generate_random_rope(&mut rng, *size);
189+ let cx = TestAppContext::single();
190+ let cx = cx.to_async();
191+ let rope = generate_random_rope(&mut rng, *size, &cx);
179192
180193 b.iter_batched(
181194 || generate_random_rope_points(&mut rng, &rope),
@@ -196,7 +209,9 @@ fn rope_benchmarks(c: &mut Criterion) { · 14 unmodified lines
196209 group.throughput(Throughput::Bytes(*size as u64));
197210 group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {
198211 let mut rng = StdRng::seed_from_u64(SEED);
199− let rope = generate_random_rope(&mut rng, *size);
212+ let cx = TestAppContext::single();
213+ let cx = cx.to_async();
214+ let rope = generate_random_rope(&mut rng, *size, &cx);
200215
201216 b.iter_batched(
202217 || generate_random_rope_points(&mut rng, &rope),
@@ -216,7 +231,9 @@ fn rope_benchmarks(c: &mut Criterion) { · 13 unmodified lines
216231 group.throughput(Throughput::Bytes(*size as u64));
217232 group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {
218233 let mut rng = StdRng::seed_from_u64(SEED);
219− let rope = generate_random_rope(&mut rng, *size);
234+ let cx = TestAppContext::single();
235+ let cx = cx.to_async();
236+ let rope = generate_random_rope(&mut rng, *size, &cx);
220237
221238 b.iter_batched(
222239 || {
crates/rope/src/rope.rs+178−98
@@ -5,7 +5,7 @@ mod point_utf16; · 4 unmodified lines
55 mod unclipped;
66
77 use arrayvec::ArrayVec;
8−use rayon::iter::{IntoParallelIterator, ParallelIterator as _};
8+use gpui::BackgroundExecutor;
99 use std::{
1010 cmp, fmt, io, mem,
1111 ops::{self, AddAssign, Range},
@@ -31,6 +31,41 @@ impl Rope { · 19 unmodified lines
3131 Self::default()
3232 }
3333
34+ /// Create a new rope from a string without trying to parallelize the construction for large strings.
35+ pub fn from_str_small(text: &str) -> Self {
36+ let mut rope = Self::new();
37+ rope.push_small(text);
38+ rope
39+ }
40+
41+ /// Create a new rope from a string.
42+ pub fn from_str(text: &str, executor: &BackgroundExecutor) -> Self {
43+ let mut rope = Self::new();
44+ rope.push(text, executor);
45+ rope
46+ }
47+
48+ /// Create a new rope from a string without trying to parallelize the construction for large strings.
49+ pub fn from_iter_small<'a, T: IntoIterator<Item = &'a str>>(iter: T) -> Self {
50+ let mut rope = Rope::new();
51+ for chunk in iter {
52+ rope.push_small(chunk);
53+ }
54+ rope
55+ }
56+
57+ /// Create a new rope from a string.
58+ pub fn from_iter<'a, T: IntoIterator<Item = &'a str>>(
59+ iter: T,
60+ executor: &BackgroundExecutor,
61+ ) -> Self {
62+ let mut rope = Rope::new();
63+ for chunk in iter {
64+ rope.push(chunk, executor);
65+ }
66+ rope
67+ }
68+
3469 /// Checks that `index`-th byte is the first byte in a UTF-8 code point
3570 /// sequence or the end of the string.
3671 ///
@@ -145,12 +180,12 @@ impl Rope { · 108 unmodified lines
145180 self.check_invariants();
146181 }
147182
148− pub fn replace(&mut self, range: Range<usize>, text: &str) {
183+ pub fn replace(&mut self, range: Range<usize>, text: &str, executor: &BackgroundExecutor) {
149184 let mut new_rope = Rope::new();
150185 let mut cursor = self.cursor(0);
151186 new_rope.append(cursor.slice(range.start));
152187 cursor.seek_forward(range.end);
153− new_rope.push(text);
188+ new_rope.push(text, executor);
154189 new_rope.append(cursor.suffix());
155190 *self = new_rope;
156191 }
@@ -168,28 +203,12 @@ impl Rope { · 11 unmodified lines
168203 self.slice(start..end)
169204 }
170205
171− pub fn push(&mut self, mut text: &str) {
172− self.chunks.update_last(
173− |last_chunk| {
174− let split_ix = if last_chunk.text.len() + text.len() <= chunk::MAX_BASE {
175− text.len()
176− } else {
177− let mut split_ix = cmp::min(
178− chunk::MIN_BASE.saturating_sub(last_chunk.text.len()),
179− text.len(),
180− );
181− while !text.is_char_boundary(split_ix) {
182− split_ix += 1;
183− }
184− split_ix
185− };
206+ pub fn push(&mut self, mut text: &str, executor: &BackgroundExecutor) {
207+ self.fill_last_chunk(&mut text);
186208
187− let (suffix, remainder) = text.split_at(split_ix);
188− last_chunk.push_str(suffix);
189− text = remainder;
190− },
191− (),
192− );
209+ if text.is_empty() {
210+ return;
211+ }
193212
194213 #[cfg(all(test, not(rust_analyzer)))]
195214 const NUM_CHUNKS: usize = 16;
@@ -200,7 +219,8 @@ impl Rope { · 4 unmodified lines
200219 // but given the chunk boundary can land within a character
201220 // we need to accommodate for the worst case where every chunk gets cut short by up to 4 bytes
202221 if text.len() > NUM_CHUNKS * chunk::MAX_BASE - NUM_CHUNKS * 4 {
203− return self.push_large(text);
222+ let future = self.push_large(text, executor.clone());
223+ return executor.block(future);
204224 }
205225 // 16 is enough as otherwise we will hit the branch above
206226 let mut new_chunks = ArrayVec::<_, NUM_CHUNKS>::new();
@@ -220,8 +240,57 @@ impl Rope { · 13 unmodified lines
220240 self.check_invariants();
221241 }
222242
243+ /// Pushes a string into the rope. Unlike [`push`], this method does not parallelize the construction on large strings.
244+ pub fn push_small(&mut self, mut text: &str) {
245+ self.fill_last_chunk(&mut text);
246+ if text.is_empty() {
247+ return;
248+ }
249+
250+ // 16 is enough as otherwise we will hit the branch above
251+ let mut new_chunks = Vec::new();
252+
253+ while !text.is_empty() {
254+ let mut split_ix = cmp::min(chunk::MAX_BASE, text.len());
255+ while !text.is_char_boundary(split_ix) {
256+ split_ix -= 1;
257+ }
258+ let (chunk, remainder) = text.split_at(split_ix);
259+ new_chunks.push(chunk);
260+ text = remainder;
261+ }
262+ self.chunks
263+ .extend(new_chunks.into_iter().map(Chunk::new), ());
264+
265+ self.check_invariants();
266+ }
267+
268+ fn fill_last_chunk(&mut self, text: &mut &str) {
269+ self.chunks.update_last(
270+ |last_chunk| {
271+ let split_ix = if last_chunk.text.len() + text.len() <= chunk::MAX_BASE {
272+ text.len()
273+ } else {
274+ let mut split_ix = cmp::min(
275+ chunk::MIN_BASE.saturating_sub(last_chunk.text.len()),
276+ text.len(),
277+ );
278+ while !text.is_char_boundary(split_ix) {
279+ split_ix += 1;
280+ }
281+ split_ix
282+ };
283+
284+ let (suffix, remainder) = text.split_at(split_ix);
285+ last_chunk.push_str(suffix);
286+ *text = remainder;
287+ },
288+ (),
289+ );
290+ }
291+
223292 /// A copy of `push` specialized for working with large quantities of text.
224− fn push_large(&mut self, mut text: &str) {
293+ async fn push_large(&mut self, mut text: &str, executor: BackgroundExecutor) {
225294 // To avoid frequent reallocs when loading large swaths of file contents,
226295 // we estimate worst-case `new_chunks` capacity;
227296 // Chunk is a fixed-capacity buffer. If a character falls on
@@ -254,8 +323,22 @@ impl Rope { · 26 unmodified lines
254323 const PARALLEL_THRESHOLD: usize = 4 * (2 * sum_tree::TREE_BASE);
255324
256325 if new_chunks.len() >= PARALLEL_THRESHOLD {
257− self.chunks
258− .par_extend(new_chunks.into_par_iter().map(Chunk::new), ());
326+ let cx2 = executor.clone();
327+ executor
328+ .scoped(|scope| {
329+ // SAFETY: transmuting to 'static is safe because the future is scoped
330+ // and the underlying string data cannot go out of scope because dropping the scope
331+ // will wait for the task to finish
332+ let new_chunks =
333+ unsafe { std::mem::transmute::<Vec<&str>, Vec<&'static str>>(new_chunks) };
334+
335+ let async_extend = self
336+ .chunks
337+ .async_extend(new_chunks.into_iter().map(Chunk::new), cx2);
338+
339+ scope.spawn(async_extend);
340+ })
341+ .await;
259342 } else {
260343 self.chunks
261344 .extend(new_chunks.into_iter().map(Chunk::new), ());
@@ -292,8 +375,13 @@ impl Rope { · 30 unmodified lines
292375 }
293376 }
294377
295− pub fn push_front(&mut self, text: &str) {
296− let suffix = mem::replace(self, Rope::from(text));
378+ pub fn push_front(&mut self, text: &str, cx: &BackgroundExecutor) {
379+ let suffix = mem::replace(self, Rope::from_str(text, cx));
380+ self.append(suffix);
381+ }
382+
383+ pub fn push_front_small(&mut self, text: &str) {
384+ let suffix = mem::replace(self, Rope::from_str_small(text));
297385 self.append(suffix);
298386 }
299387
@@ -577,37 +665,19 @@ impl Rope { · 277 unmodified lines
577665 }
578666 }
579667
580−impl<'a> From<&'a str> for Rope {
581− fn from(text: &'a str) -> Self {
582− let mut rope = Self::new();
583− rope.push(text);
584− rope
585− }
586−}
668+// impl From<String> for Rope {
669+// #[inline(always)]
670+// fn from(text: String) -> Self {
671+// Rope::from(text.as_str())
672+// }
673+// }
587674
588−impl<'a> FromIterator<&'a str> for Rope {
589− fn from_iter<T: IntoIterator<Item = &'a str>>(iter: T) -> Self {
590− let mut rope = Rope::new();
591− for chunk in iter {
592− rope.push(chunk);
593− }
594− rope
595− }
596−}
597−
598−impl From<String> for Rope {
599− #[inline(always)]
600− fn from(text: String) -> Self {
601− Rope::from(text.as_str())
602− }
603−}
604−
605−impl From<&String> for Rope {
606− #[inline(always)]
607− fn from(text: &String) -> Self {
608− Rope::from(text.as_str())
609− }
610−}
675+// impl From<&String> for Rope {
676+// #[inline(always)]
677+// fn from(text: &String) -> Self {
678+// Rope::from(text.as_str())
679+// }
680+// }
611681
612682 impl fmt::Display for Rope {
613683 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@@ -1639,6 +1709,7 @@ where · 1025 unmodified lines
16391709 mod tests {
16401710 use super::*;
16411711 use Bias::{Left, Right};
1712+ use gpui::TestAppContext;
16421713 use rand::prelude::*;
16431714 use std::{cmp::Ordering, env, io::Read};
16441715 use util::RandomCharIter;
@@ -1648,17 +1719,17 @@ mod tests { · 3 unmodified lines
16481719 zlog::init_test();
16491720 }
16501721
1651− #[test]
1652− fn test_all_4_byte_chars() {
1722+ #[gpui::test]
1723+ async fn test_all_4_byte_chars(cx: &mut TestAppContext) {
16531724 let mut rope = Rope::new();
16541725 let text = "🏀".repeat(256);
1655− rope.push(&text);
1726+ rope.push(&text, cx.background_executor());
16561727 assert_eq!(rope.text(), text);
16571728 }
16581729
1659− #[test]
1660− fn test_clip() {
1661− let rope = Rope::from("🧘");
1730+ #[gpui::test]
1731+ fn test_clip(cx: &mut TestAppContext) {
1732+ let rope = Rope::from_str("🧘", cx.background_executor());
16621733
16631734 assert_eq!(rope.clip_offset(1, Bias::Left), 0);
16641735 assert_eq!(rope.clip_offset(1, Bias::Right), 4);
@@ -1704,9 +1775,9 @@ mod tests { · 39 unmodified lines
17041775 );
17051776 }
17061777
1707− #[test]
1708− fn test_prev_next_line() {
1709− let rope = Rope::from("abc\ndef\nghi\njkl");
1778+ #[gpui::test]
1779+ fn test_prev_next_line(cx: &mut TestAppContext) {
1780+ let rope = Rope::from_str("abc\ndef\nghi\njkl", cx.background_executor());
17101781
17111782 let mut chunks = rope.chunks();
17121783 assert_eq!(chunks.peek().unwrap().chars().next().unwrap(), 'a');
@@ -1748,16 +1819,16 @@ mod tests { · 35 unmodified lines
17481819 assert_eq!(chunks.peek(), None);
17491820 }
17501821
1751− #[test]
1752− fn test_lines() {
1753− let rope = Rope::from("abc\ndefg\nhi");
1822+ #[gpui::test]
1823+ fn test_lines(cx: &mut TestAppContext) {
1824+ let rope = Rope::from_str("abc\ndefg\nhi", cx.background_executor());
17541825 let mut lines = rope.chunks().lines();
17551826 assert_eq!(lines.next(), Some("abc"));
17561827 assert_eq!(lines.next(), Some("defg"));
17571828 assert_eq!(lines.next(), Some("hi"));
17581829 assert_eq!(lines.next(), None);
17591830
1760− let rope = Rope::from("abc\ndefg\nhi\n");
1831+ let rope = Rope::from_str("abc\ndefg\nhi\n", cx.background_executor());
17611832 let mut lines = rope.chunks().lines();
17621833 assert_eq!(lines.next(), Some("abc"));
17631834 assert_eq!(lines.next(), Some("defg"));
@@ -1765,14 +1836,14 @@ mod tests { · 1 unmodified line
17651836 assert_eq!(lines.next(), Some(""));
17661837 assert_eq!(lines.next(), None);
17671838
1768− let rope = Rope::from("abc\ndefg\nhi");
1839+ let rope = Rope::from_str("abc\ndefg\nhi", cx.background_executor());
17691840 let mut lines = rope.reversed_chunks_in_range(0..rope.len()).lines();
17701841 assert_eq!(lines.next(), Some("hi"));
17711842 assert_eq!(lines.next(), Some("defg"));
17721843 assert_eq!(lines.next(), Some("abc"));
17731844 assert_eq!(lines.next(), None);
17741845
1775− let rope = Rope::from("abc\ndefg\nhi\n");
1846+ let rope = Rope::from_str("abc\ndefg\nhi\n", cx.background_executor());
17761847 let mut lines = rope.reversed_chunks_in_range(0..rope.len()).lines();
17771848 assert_eq!(lines.next(), Some(""));
17781849 assert_eq!(lines.next(), Some("hi"));
@@ -1780,14 +1851,14 @@ mod tests { · 1 unmodified line
17801851 assert_eq!(lines.next(), Some("abc"));
17811852 assert_eq!(lines.next(), None);
17821853
1783− let rope = Rope::from("abc\nlonger line test\nhi");
1854+ let rope = Rope::from_str("abc\nlonger line test\nhi", cx.background_executor());
17841855 let mut lines = rope.chunks().lines();
17851856 assert_eq!(lines.next(), Some("abc"));
17861857 assert_eq!(lines.next(), Some("longer line test"));
17871858 assert_eq!(lines.next(), Some("hi"));
17881859 assert_eq!(lines.next(), None);
17891860
1790− let rope = Rope::from("abc\nlonger line test\nhi");
1861+ let rope = Rope::from_str("abc\nlonger line test\nhi", cx.background_executor());
17911862 let mut lines = rope.reversed_chunks_in_range(0..rope.len()).lines();
17921863 assert_eq!(lines.next(), Some("hi"));
17931864 assert_eq!(lines.next(), Some("longer line test"));
@@ -1796,7 +1867,7 @@ mod tests { · 2 unmodified lines
17961867 }
17971868
17981869 #[gpui::test(iterations = 100)]
1799− fn test_random_rope(mut rng: StdRng) {
1870+ async fn test_random_rope(cx: &mut TestAppContext, mut rng: StdRng) {
18001871 let operations = env::var("OPERATIONS")
18011872 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
18021873 .unwrap_or(10);
@@ -1812,7 +1883,7 @@ mod tests { · 9 unmodified lines
18121883 let mut new_actual = Rope::new();
18131884 let mut cursor = actual.cursor(0);
18141885 new_actual.append(cursor.slice(start_ix));
1815− new_actual.push(&new_text);
1886+ new_actual.push(&new_text, cx.background_executor());
18161887 cursor.seek_forward(end_ix);
18171888 new_actual.append(cursor.suffix());
18181889 actual = new_actual;
@@ -2112,10 +2183,10 @@ mod tests { · 293 unmodified lines
21122183 }
21132184 }
21142185
2115− #[test]
2116− fn test_chunks_equals_str() {
2186+ #[gpui::test]
2187+ fn test_chunks_equals_str(cx: &mut TestAppContext) {
21172188 let text = "This is a multi-chunk\n& multi-line test string!";
2118− let rope = Rope::from(text);
2189+ let rope = Rope::from_str(text, cx.background_executor());
21192190 for start in 0..text.len() {
21202191 for end in start..text.len() {
21212192 let range = start..end;
@@ -2158,34 +2229,37 @@ mod tests { · 36 unmodified lines
21582229 }
21592230 }
21602231
2161− let rope = Rope::from("");
2232+ let rope = Rope::from_str("", cx.background_executor());
21622233 assert!(rope.chunks_in_range(0..0).equals_str(""));
21632234 assert!(rope.reversed_chunks_in_range(0..0).equals_str(""));
21642235 assert!(!rope.chunks_in_range(0..0).equals_str("foo"));
21652236 assert!(!rope.reversed_chunks_in_range(0..0).equals_str("foo"));
21662237 }
21672238
2168− #[test]
2169− fn test_is_char_boundary() {
2239+ #[gpui::test]
2240+ fn test_is_char_boundary(cx: &mut TestAppContext) {
21702241 let fixture = "地";
2171− let rope = Rope::from("地");
2242+ let rope = Rope::from_str("地", cx.background_executor());
21722243 for b in 0..=fixture.len() {
21732244 assert_eq!(rope.is_char_boundary(b), fixture.is_char_boundary(b));
21742245 }
21752246 let fixture = "";
2176− let rope = Rope::from("");
2247+ let rope = Rope::from_str("", cx.background_executor());
21772248 for b in 0..=fixture.len() {
21782249 assert_eq!(rope.is_char_boundary(b), fixture.is_char_boundary(b));
21792250 }
21802251 let fixture = "🔴🟠🟡🟢🔵🟣⚫️⚪️🟤\n🏳️‍⚧️🏁🏳️‍🌈🏴‍☠️⛳️📬📭🏴🏳️🚩";
2181− let rope = Rope::from("🔴🟠🟡🟢🔵🟣⚫️⚪️🟤\n🏳️‍⚧️🏁🏳️‍🌈🏴‍☠️⛳️📬📭🏴🏳️🚩");
2252+ let rope = Rope::from_str(
2253+ "🔴🟠🟡🟢🔵🟣⚫️⚪️🟤\n🏳️‍⚧️🏁🏳️‍🌈🏴‍☠️⛳️📬📭🏴🏳️🚩",
2254+ cx.background_executor(),
2255+ );
21822256 for b in 0..=fixture.len() {
21832257 assert_eq!(rope.is_char_boundary(b), fixture.is_char_boundary(b));
21842258 }
21852259 }
21862260
2187− #[test]
2188− fn test_floor_char_boundary() {
2261+ #[gpui::test]
2262+ fn test_floor_char_boundary(cx: &mut TestAppContext) {
21892263 // polyfill of str::floor_char_boundary
21902264 fn floor_char_boundary(str: &str, index: usize) -> usize {
21912265 if index >= str.len() {
@@ -2201,7 +2275,7 @@ mod tests { · 9 unmodified lines
22012275 }
22022276
22032277 let fixture = "地";
2204− let rope = Rope::from("地");
2278+ let rope = Rope::from_str("地", cx.background_executor());
22052279 for b in 0..=fixture.len() {
22062280 assert_eq!(
22072281 rope.floor_char_boundary(b),
@@ -2210,7 +2284,7 @@ mod tests { · 2 unmodified lines
22102284 }
22112285
22122286 let fixture = "";
2213− let rope = Rope::from("");
2287+ let rope = Rope::from_str("", cx.background_executor());
22142288 for b in 0..=fixture.len() {
22152289 assert_eq!(
22162290 rope.floor_char_boundary(b),
@@ -2219,7 +2293,10 @@ mod tests { · 2 unmodified lines
22192293 }
22202294
22212295 let fixture = "🔴🟠🟡🟢🔵🟣⚫️⚪️🟤\n🏳️‍⚧️🏁🏳️‍🌈🏴‍☠️⛳️📬📭🏴🏳️🚩";
2222− let rope = Rope::from("🔴🟠🟡🟢🔵🟣⚫️⚪️🟤\n🏳️‍⚧️🏁🏳️‍🌈🏴‍☠️⛳️📬📭🏴🏳️🚩");
2296+ let rope = Rope::from_str(
2297+ "🔴🟠🟡🟢🔵🟣⚫️⚪️🟤\n🏳️‍⚧️🏁🏳️‍🌈🏴‍☠️⛳️📬📭🏴🏳️🚩",
2298+ cx.background_executor(),
2299+ );
22232300 for b in 0..=fixture.len() {
22242301 assert_eq!(
22252302 rope.floor_char_boundary(b),
@@ -2228,8 +2305,8 @@ mod tests { · 2 unmodified lines
22282305 }
22292306 }
22302307
2231− #[test]
2232− fn test_ceil_char_boundary() {
2308+ #[gpui::test]
2309+ fn test_ceil_char_boundary(cx: &mut TestAppContext) {
22332310 // polyfill of str::ceil_char_boundary
22342311 fn ceil_char_boundary(str: &str, index: usize) -> usize {
22352312 if index > str.len() {
@@ -2244,19 +2321,22 @@ mod tests { · 8 unmodified lines
22442321 }
22452322
22462323 let fixture = "地";
2247− let rope = Rope::from("地");
2324+ let rope = Rope::from_str("地", cx.background_executor());
22482325 for b in 0..=fixture.len() {
22492326 assert_eq!(rope.ceil_char_boundary(b), ceil_char_boundary(&fixture, b));
22502327 }
22512328
22522329 let fixture = "";
2253− let rope = Rope::from("");
2330+ let rope = Rope::from_str("", cx.background_executor());
22542331 for b in 0..=fixture.len() {
22552332 assert_eq!(rope.ceil_char_boundary(b), ceil_char_boundary(&fixture, b));
22562333 }
22572334
22582335 let fixture = "🔴🟠🟡🟢🔵🟣⚫️⚪️🟤\n🏳️‍⚧️🏁🏳️‍🌈🏴‍☠️⛳️📬📭🏴🏳️🚩";
2259− let rope = Rope::from("🔴🟠🟡🟢🔵🟣⚫️⚪️🟤\n🏳️‍⚧️🏁🏳️‍🌈🏴‍☠️⛳️📬📭🏴🏳️🚩");
2336+ let rope = Rope::from_str(
2337+ "🔴🟠🟡🟢🔵🟣⚫️⚪️🟤\n🏳️‍⚧️🏁🏳️‍🌈🏴‍☠️⛳️📬📭🏴🏳️🚩",
2338+ cx.background_executor(),
2339+ );
22602340 for b in 0..=fixture.len() {
22612341 assert_eq!(rope.ceil_char_boundary(b), ceil_char_boundary(&fixture, b));
22622342 }
crates/rules_library/src/rules_library.rs+8−2
@@ -554,7 +554,7 @@ impl RulesLibrary { · 553 unmodified lines
554554
555555 let prompt_id = PromptId::new();
556556 let save = self.store.update(cx, |store, cx| {
557− store.save(prompt_id, None, false, "".into(), cx)
557+ store.save(prompt_id, None, false, Default::default(), cx)
558558 });
559559 self.picker
560560 .update(cx, |picker, cx| picker.refresh(window, cx));
@@ -888,7 +888,13 @@ impl RulesLibrary { · 327 unmodified lines
888888 let new_id = PromptId::new();
889889 let body = rule.body_editor.read(cx).text(cx);
890890 let save = self.store.update(cx, |store, cx| {
891− store.save(new_id, Some(title.into()), false, body.into(), cx)
891+ store.save(
892+ new_id,
893+ Some(title.into()),
894+ false,
895+ Rope::from_str(&body, cx.background_executor()),
896+ cx,
897+ )
892898 });
893899 self.picker
894900 .update(cx, |picker, cx| picker.refresh(window, cx));
crates/streaming_diff/Cargo.toml+1−0
@@ -14,6 +14,7 @@ path = "src/streaming_diff.rs" · 13 unmodified lines
1414 [dependencies]
1515 ordered-float.workspace = true
1616 rope.workspace = true
17+gpui.workspace = true
1718
1819 [dev-dependencies]
1920 rand.workspace = true
crates/streaming_diff/src/streaming_diff.rs+52−47
@@ -503,11 +503,12 @@ fn is_line_end(point: Point, text: &Rope) -> bool { · 502 unmodified lines
503503 #[cfg(test)]
504504 mod tests {
505505 use super::*;
506+ use gpui::BackgroundExecutor;
506507 use rand::prelude::*;
507508 use std::env;
508509
509− #[test]
510− fn test_delete_first_of_two_lines() {
510+ #[gpui::test]
511+ fn test_delete_first_of_two_lines(cx: &mut gpui::TestAppContext) {
511512 let old_text = "aaaa\nbbbb";
512513 let char_ops = vec![
513514 CharOperation::Delete { bytes: 5 },
@@ -523,18 +524,18 @@ mod tests { · 9 unmodified lines
523524 apply_line_operations(old_text, &new_text, &expected_line_ops)
524525 );
525526
526− let line_ops = char_ops_to_line_ops(old_text, &char_ops);
527+ let line_ops = char_ops_to_line_ops(old_text, &char_ops, cx.background_executor());
527528 assert_eq!(line_ops, expected_line_ops);
528529 }
529530
530− #[test]
531− fn test_delete_second_of_two_lines() {
531+ #[gpui::test]
532+ fn test_delete_second_of_two_lines(cx: &mut gpui::TestAppContext) {
532533 let old_text = "aaaa\nbbbb";
533534 let char_ops = vec![
534535 CharOperation::Keep { bytes: 5 },
535536 CharOperation::Delete { bytes: 4 },
536537 ];
537− let line_ops = char_ops_to_line_ops(old_text, &char_ops);
538+ let line_ops = char_ops_to_line_ops(old_text, &char_ops, cx.background_executor());
538539 assert_eq!(
539540 line_ops,
540541 vec![
@@ -550,8 +551,8 @@ mod tests { · 9 unmodified lines
550551 );
551552 }
552553
553− #[test]
554− fn test_add_new_line() {
554+ #[gpui::test]
555+ fn test_add_new_line(cx: &mut gpui::TestAppContext) {
555556 let old_text = "aaaa\nbbbb";
556557 let char_ops = vec![
557558 CharOperation::Keep { bytes: 9 },
@@ -559,7 +560,7 @@ mod tests { · 1 unmodified line
559560 text: "\ncccc".into(),
560561 },
561562 ];
562− let line_ops = char_ops_to_line_ops(old_text, &char_ops);
563+ let line_ops = char_ops_to_line_ops(old_text, &char_ops, cx.background_executor());
563564 assert_eq!(
564565 line_ops,
565566 vec![
@@ -574,15 +575,15 @@ mod tests { · 8 unmodified lines
574575 );
575576 }
576577
577− #[test]
578− fn test_delete_line_in_middle() {
578+ #[gpui::test]
579+ fn test_delete_line_in_middle(cx: &mut gpui::TestAppContext) {
579580 let old_text = "aaaa\nbbbb\ncccc";
580581 let char_ops = vec![
581582 CharOperation::Keep { bytes: 5 },
582583 CharOperation::Delete { bytes: 5 },
583584 CharOperation::Keep { bytes: 4 },
584585 ];
585− let line_ops = char_ops_to_line_ops(old_text, &char_ops);
586+ let line_ops = char_ops_to_line_ops(old_text, &char_ops, cx.background_executor());
586587 assert_eq!(
587588 line_ops,
588589 vec![
@@ -598,8 +599,8 @@ mod tests { · 9 unmodified lines
598599 );
599600 }
600601
601− #[test]
602− fn test_replace_line() {
602+ #[gpui::test]
603+ fn test_replace_line(cx: &mut gpui::TestAppContext) {
603604 let old_text = "aaaa\nbbbb\ncccc";
604605 let char_ops = vec![
605606 CharOperation::Keep { bytes: 5 },
@@ -609,7 +610,7 @@ mod tests { · 3 unmodified lines
609610 },
610611 CharOperation::Keep { bytes: 5 },
611612 ];
612− let line_ops = char_ops_to_line_ops(old_text, &char_ops);
613+ let line_ops = char_ops_to_line_ops(old_text, &char_ops, cx.background_executor());
613614 assert_eq!(
614615 line_ops,
615616 vec![
@@ -626,8 +627,8 @@ mod tests { · 10 unmodified lines
626627 );
627628 }
628629
629− #[test]
630− fn test_multiple_edits_on_different_lines() {
630+ #[gpui::test]
631+ fn test_multiple_edits_on_different_lines(cx: &mut gpui::TestAppContext) {
631632 let old_text = "aaaa\nbbbb\ncccc\ndddd";
632633 let char_ops = vec![
633634 CharOperation::Insert { text: "A".into() },
@@ -638,7 +639,7 @@ mod tests { · 4 unmodified lines
638639 text: "\nEEEE".into(),
639640 },
640641 ];
641− let line_ops = char_ops_to_line_ops(old_text, &char_ops);
642+ let line_ops = char_ops_to_line_ops(old_text, &char_ops, cx.background_executor());
642643 assert_eq!(
643644 line_ops,
644645 vec![
@@ -656,15 +657,15 @@ mod tests { · 11 unmodified lines
656657 );
657658 }
658659
659− #[test]
660− fn test_edit_at_end_of_line() {
660+ #[gpui::test]
661+ fn test_edit_at_end_of_line(cx: &mut gpui::TestAppContext) {
661662 let old_text = "aaaa\nbbbb\ncccc";
662663 let char_ops = vec![
663664 CharOperation::Keep { bytes: 4 },
664665 CharOperation::Insert { text: "A".into() },
665666 CharOperation::Keep { bytes: 10 },
666667 ];
667− let line_ops = char_ops_to_line_ops(old_text, &char_ops);
668+ let line_ops = char_ops_to_line_ops(old_text, &char_ops, cx.background_executor());
668669 assert_eq!(
669670 line_ops,
670671 vec![
@@ -680,8 +681,8 @@ mod tests { · 9 unmodified lines
680681 );
681682 }
682683
683− #[test]
684− fn test_insert_newline_character() {
684+ #[gpui::test]
685+ fn test_insert_newline_character(cx: &mut gpui::TestAppContext) {
685686 let old_text = "aaaabbbb";
686687 let char_ops = vec![
687688 CharOperation::Keep { bytes: 4 },
@@ -689,7 +690,7 @@ mod tests { · 1 unmodified line
689690 CharOperation::Keep { bytes: 4 },
690691 ];
691692 let new_text = apply_char_operations(old_text, &char_ops);
692− let line_ops = char_ops_to_line_ops(old_text, &char_ops);
693+ let line_ops = char_ops_to_line_ops(old_text, &char_ops, cx.background_executor());
693694 assert_eq!(
694695 line_ops,
695696 vec![
@@ -703,14 +704,14 @@ mod tests { · 7 unmodified lines
703704 );
704705 }
705706
706− #[test]
707− fn test_insert_newline_at_beginning() {
707+ #[gpui::test]
708+ fn test_insert_newline_at_beginning(cx: &mut gpui::TestAppContext) {
708709 let old_text = "aaaa\nbbbb";
709710 let char_ops = vec![
710711 CharOperation::Insert { text: "\n".into() },
711712 CharOperation::Keep { bytes: 9 },
712713 ];
713− let line_ops = char_ops_to_line_ops(old_text, &char_ops);
714+ let line_ops = char_ops_to_line_ops(old_text, &char_ops, cx.background_executor());
714715 assert_eq!(
715716 line_ops,
716717 vec![
@@ -725,15 +726,15 @@ mod tests { · 8 unmodified lines
725726 );
726727 }
727728
728− #[test]
729− fn test_delete_newline() {
729+ #[gpui::test]
730+ fn test_delete_newline(cx: &mut gpui::TestAppContext) {
730731 let old_text = "aaaa\nbbbb";
731732 let char_ops = vec![
732733 CharOperation::Keep { bytes: 4 },
733734 CharOperation::Delete { bytes: 1 },
734735 CharOperation::Keep { bytes: 4 },
735736 ];
736− let line_ops = char_ops_to_line_ops(old_text, &char_ops);
737+ let line_ops = char_ops_to_line_ops(old_text, &char_ops, cx.background_executor());
737738 assert_eq!(
738739 line_ops,
739740 vec![
@@ -749,8 +750,8 @@ mod tests { · 9 unmodified lines
749750 );
750751 }
751752
752− #[test]
753− fn test_insert_multiple_newlines() {
753+ #[gpui::test]
754+ fn test_insert_multiple_newlines(cx: &mut gpui::TestAppContext) {
754755 let old_text = "aaaa\nbbbb";
755756 let char_ops = vec![
756757 CharOperation::Keep { bytes: 5 },
@@ -759,7 +760,7 @@ mod tests { · 2 unmodified lines
759760 },
760761 CharOperation::Keep { bytes: 4 },
761762 ];
762− let line_ops = char_ops_to_line_ops(old_text, &char_ops);
763+ let line_ops = char_ops_to_line_ops(old_text, &char_ops, cx.background_executor());
763764 assert_eq!(
764765 line_ops,
765766 vec![
@@ -775,15 +776,15 @@ mod tests { · 9 unmodified lines
775776 );
776777 }
777778
778− #[test]
779− fn test_delete_multiple_newlines() {
779+ #[gpui::test]
780+ fn test_delete_multiple_newlines(cx: &mut gpui::TestAppContext) {
780781 let old_text = "aaaa\n\n\nbbbb";
781782 let char_ops = vec![
782783 CharOperation::Keep { bytes: 5 },
783784 CharOperation::Delete { bytes: 2 },
784785 CharOperation::Keep { bytes: 4 },
785786 ];
786− let line_ops = char_ops_to_line_ops(old_text, &char_ops);
787+ let line_ops = char_ops_to_line_ops(old_text, &char_ops, cx.background_executor());
787788 assert_eq!(
788789 line_ops,
789790 vec![
@@ -799,8 +800,8 @@ mod tests { · 9 unmodified lines
799800 );
800801 }
801802
802− #[test]
803− fn test_complex_scenario() {
803+ #[gpui::test]
804+ fn test_complex_scenario(cx: &mut gpui::TestAppContext) {
804805 let old_text = "line1\nline2\nline3\nline4";
805806 let char_ops = vec![
806807 CharOperation::Keep { bytes: 6 },
@@ -814,7 +815,7 @@ mod tests { · 7 unmodified lines
814815 },
815816 CharOperation::Keep { bytes: 6 },
816817 ];
817− let line_ops = char_ops_to_line_ops(old_text, &char_ops);
818+ let line_ops = char_ops_to_line_ops(old_text, &char_ops, cx.background_executor());
818819 assert_eq!(
819820 line_ops,
820821 vec![
@@ -834,8 +835,8 @@ mod tests { · 13 unmodified lines
834835 );
835836 }
836837
837− #[test]
838− fn test_cleaning_up_common_suffix() {
838+ #[gpui::test]
839+ fn test_cleaning_up_common_suffix(cx: &mut gpui::TestAppContext) {
839840 let old_text = concat!(
840841 " for y in 0..size.y() {\n",
841842 " let a = 10;\n",
@@ -883,7 +884,7 @@ mod tests { · 41 unmodified lines
883884 },
884885 CharOperation::Keep { bytes: 1 },
885886 ];
886− let line_ops = char_ops_to_line_ops(old_text, &char_ops);
887+ let line_ops = char_ops_to_line_ops(old_text, &char_ops, cx.background_executor());
887888 assert_eq!(
888889 line_ops,
889890 vec![
@@ -901,8 +902,8 @@ mod tests { · 11 unmodified lines
901902 );
902903 }
903904
904− #[test]
905− fn test_random_diffs() {
905+ #[gpui::test]
906+ fn test_random_diffs(cx: &mut gpui::TestAppContext) {
906907 random_test(|mut rng| {
907908 let old_text_len = env::var("OLD_TEXT_LEN")
908909 .map(|i| i.parse().expect("invalid `OLD_TEXT_LEN` variable"))
@@ -922,15 +923,19 @@ mod tests { · 13 unmodified lines
922923 assert_eq!(patched, new);
923924
924925 // Test char_ops_to_line_ops
925− let line_ops = char_ops_to_line_ops(&old, &char_operations);
926+ let line_ops = char_ops_to_line_ops(&old, &char_operations, cx.background_executor());
926927 println!("line operations: {:?}", line_ops);
927928 let patched = apply_line_operations(&old, &new, &line_ops);
928929 assert_eq!(patched, new);
929930 });
930931 }
931932
932− fn char_ops_to_line_ops(old_text: &str, char_ops: &[CharOperation]) -> Vec<LineOperation> {
933− let old_rope = Rope::from(old_text);
933+ fn char_ops_to_line_ops(
934+ old_text: &str,
935+ char_ops: &[CharOperation],
936+ executor: &BackgroundExecutor,
937+ ) -> Vec<LineOperation> {
938+ let old_rope = Rope::from_str(old_text, executor);
934939 let mut diff = LineDiff::default();
935940 for op in char_ops {
936941 diff.push_char_operation(op, &old_rope);
crates/sum_tree/Cargo.toml+3−1
@@ -15,10 +15,12 @@ doctest = false · 14 unmodified lines
1515
1616 [dependencies]
1717 arrayvec = "0.7.1"
18−rayon.workspace = true
1918 log.workspace = true
19+futures.workspace = true
20+itertools.workspace = true
2021
2122 [dev-dependencies]
2223 ctor.workspace = true
2324 rand.workspace = true
2425 zlog.workspace = true
26+pollster = "0.4.0"
crates/sum_tree/src/sum_tree.rs+85−46
@@ -3,7 +3,8 @@ mod tree_map; · 2 unmodified lines
33
44 use arrayvec::ArrayVec;
55 pub use cursor::{Cursor, FilterCursor, Iter};
6−use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator as _};
6+use futures::{StreamExt, stream};
7+use itertools::Itertools as _;
78 use std::marker::PhantomData;
89 use std::mem;
910 use std::{cmp::Ordering, fmt, iter::FromIterator, sync::Arc};
@@ -14,6 +15,18 @@ pub const TREE_BASE: usize = 2; · 4 unmodified lines
1415 #[cfg(not(test))]
1516 pub const TREE_BASE: usize = 6;
1617
18+pub trait BackgroundSpawn {
19+ type Task<R>: Future<Output = R> + Send + Sync
20+ where
21+ R: Send + Sync;
22+ fn background_spawn<R>(
23+ &self,
24+ future: impl Future<Output = R> + Send + Sync + 'static,
25+ ) -> Self::Task<R>
26+ where
27+ R: Send + Sync + 'static;
28+}
29+
1730 /// An item that can be stored in a [`SumTree`]
1831 ///
1932 /// Must be summarized by a type that implements [`Summary`]
@@ -298,62 +311,71 @@ impl<T: Item> SumTree<T> { · 278 unmodified lines
298311 }
299312 }
300313
301− pub fn from_par_iter<I, Iter>(iter: I, cx: <T::Summary as Summary>::Context<'_>) -> Self
314+ pub async fn from_iter_async<I, S>(iter: I, spawn: S) -> Self
302315 where
303− I: IntoParallelIterator<Iter = Iter>,
304− Iter: IndexedParallelIterator<Item = T>,
305− T: Send + Sync,
306− T::Summary: Send + Sync,
307− for<'a> <T::Summary as Summary>::Context<'a>: Sync,
316+ T: 'static + Send + Sync,
317+ for<'a> T::Summary: Summary<Context<'a> = ()> + Send + Sync,
318+ S: BackgroundSpawn,
319+ I: IntoIterator<Item = T>,
308320 {
309− let mut nodes = iter
310− .into_par_iter()
311− .chunks(2 * TREE_BASE)
312− .map(|items| {
313− let items: ArrayVec<T, { 2 * TREE_BASE }> = items.into_iter().collect();
321+ let mut futures = vec![];
322+ let chunks = iter.into_iter().chunks(2 * TREE_BASE);
323+ for chunk in chunks.into_iter() {
324+ let items: ArrayVec<T, { 2 * TREE_BASE }> = chunk.into_iter().collect();
325+ futures.push(async move {
314326 let item_summaries: ArrayVec<T::Summary, { 2 * TREE_BASE }> =
315− items.iter().map(|item| item.summary(cx)).collect();
327+ items.iter().map(|item| item.summary(())).collect();
316328 let mut summary = item_summaries[0].clone();
317329 for item_summary in &item_summaries[1..] {
318− <T::Summary as Summary>::add_summary(&mut summary, item_summary, cx);
330+ <T::Summary as Summary>::add_summary(&mut summary, item_summary, ());
319331 }
320332 SumTree(Arc::new(Node::Leaf {
321333 summary,
322334 items,
323335 item_summaries,
324336 }))
325− })
326− .collect::<Vec<_>>();
337+ });
338+ }
339+
340+ let mut nodes = futures::stream::iter(futures)
341+ .map(|future| spawn.background_spawn(future))
342+ .buffered(4)
343+ .collect::<Vec<_>>()
344+ .await;
327345
328346 let mut height = 0;
329347 while nodes.len() > 1 {
330348 height += 1;
331− nodes = nodes
332− .into_par_iter()
349+ let current_nodes = mem::take(&mut nodes);
350+ nodes = stream::iter(current_nodes)
333351 .chunks(2 * TREE_BASE)
334− .map(|child_nodes| {
335− let child_trees: ArrayVec<SumTree<T>, { 2 * TREE_BASE }> =
336− child_nodes.into_iter().collect();
337− let child_summaries: ArrayVec<T::Summary, { 2 * TREE_BASE }> = child_trees
338− .iter()
339− .map(|child_tree| child_tree.summary().clone())
340− .collect();
341− let mut summary = child_summaries[0].clone();
342− for child_summary in &child_summaries[1..] {
343− <T::Summary as Summary>::add_summary(&mut summary, child_summary, cx);
344− }
345− SumTree(Arc::new(Node::Internal {
346− height,
347− summary,
348− child_summaries,
349− child_trees,
350− }))
352+ .map(|chunk| {
353+ spawn.background_spawn(async move {
354+ let child_trees: ArrayVec<SumTree<T>, { 2 * TREE_BASE }> =
355+ chunk.into_iter().collect();
356+ let child_summaries: ArrayVec<T::Summary, { 2 * TREE_BASE }> = child_trees
357+ .iter()
358+ .map(|child_tree| child_tree.summary().clone())
359+ .collect();
360+ let mut summary = child_summaries[0].clone();
361+ for child_summary in &child_summaries[1..] {
362+ <T::Summary as Summary>::add_summary(&mut summary, child_summary, ());
363+ }
364+ SumTree(Arc::new(Node::Internal {
365+ height,
366+ summary,
367+ child_summaries,
368+ child_trees,
369+ }))
370+ })
351371 })
352− .collect::<Vec<_>>();
372+ .buffered(4)
373+ .collect::<Vec<_>>()
374+ .await;
353375 }
354376
355377 if nodes.is_empty() {
356− Self::new(cx)
378+ Self::new(())
357379 } else {
358380 debug_assert_eq!(nodes.len(), 1);
359381 nodes.pop().unwrap()
@@ -597,15 +619,15 @@ impl<T: Item> SumTree<T> { · 237 unmodified lines
597619 self.append(Self::from_iter(iter, cx), cx);
598620 }
599621
600− pub fn par_extend<I, Iter>(&mut self, iter: I, cx: <T::Summary as Summary>::Context<'_>)
622+ pub async fn async_extend<S, I>(&mut self, iter: I, spawn: S)
601623 where
602− I: IntoParallelIterator<Iter = Iter>,
603− Iter: IndexedParallelIterator<Item = T>,
604− T: Send + Sync,
605− T::Summary: Send + Sync,
606− for<'a> <T::Summary as Summary>::Context<'a>: Sync,
624+ S: BackgroundSpawn,
625+ I: IntoIterator<Item = T> + 'static,
626+ T: 'static + Send + Sync,
627+ for<'b> T::Summary: Summary<Context<'b> = ()> + Send + Sync,
607628 {
608− self.append(Self::from_par_iter(iter, cx), cx);
629+ let other = Self::from_iter_async(iter, spawn);
630+ self.append(other.await, ());
609631 }
610632
611633 pub fn push(&mut self, item: T, cx: <T::Summary as Summary>::Context<'_>) {
@@ -1070,6 +1092,23 @@ mod tests { · 458 unmodified lines
10701092
10711093 #[test]
10721094 fn test_random() {
1095+ struct NoSpawn;
1096+ impl BackgroundSpawn for NoSpawn {
1097+ type Task<R>
1098+ = std::pin::Pin<Box<dyn Future<Output = R> + Sync + Send>>
1099+ where
1100+ R: Send + Sync;
1101+ fn background_spawn<R>(
1102+ &self,
1103+ future: impl Future<Output = R> + Send + Sync + 'static,
1104+ ) -> Self::Task<R>
1105+ where
1106+ R: Send + Sync + 'static,
1107+ {
1108+ Box::pin(future)
1109+ }
1110+ }
1111+
10731112 let mut starting_seed = 0;
10741113 if let Ok(value) = std::env::var("SEED") {
10751114 starting_seed = value.parse().expect("invalid SEED variable");
@@ -1095,7 +1134,7 @@ mod tests { · 19 unmodified lines
10951134 .sample_iter(StandardUniform)
10961135 .take(count)
10971136 .collect::<Vec<_>>();
1098− tree.par_extend(items, ());
1137+ pollster::block_on(tree.async_extend(items, NoSpawn));
10991138 }
11001139
11011140 for _ in 0..num_operations {
@@ -1117,7 +1156,7 @@ mod tests { · 15 unmodified lines
11171156 if rng.random() {
11181157 new_tree.extend(new_items, ());
11191158 } else {
1120− new_tree.par_extend(new_items, ());
1159+ pollster::block_on(new_tree.async_extend(new_items, NoSpawn));
11211160 }
11221161 cursor.seek(&Count(splice_end), Bias::Right);
11231162 new_tree.append(cursor.slice(&tree_end, Bias::Right), ());
crates/text/Cargo.toml+1−0
@@ -28,6 +28,7 @@ rope.workspace = true · 27 unmodified lines
2828 smallvec.workspace = true
2929 sum_tree.workspace = true
3030 util.workspace = true
31+gpui.workspace = true
3132
3233 [dev-dependencies]
3334 collections = { workspace = true, features = ["test-support"] }
crates/text/src/tests.rs+184−97
@@ -14,24 +14,29 @@ fn init_logger() { · 13 unmodified lines
1414 zlog::init_test();
1515 }
1616
17−#[test]
18−fn test_edit() {
19− let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "abc");
17+#[gpui::test]
18+fn test_edit(cx: &mut gpui::TestAppContext) {
19+ let mut buffer = Buffer::new(
20+ ReplicaId::LOCAL,
21+ BufferId::new(1).unwrap(),
22+ "abc",
23+ cx.background_executor(),
24+ );
2025 assert_eq!(buffer.text(), "abc");
21− buffer.edit([(3..3, "def")]);
26+ buffer.edit([(3..3, "def")], cx.background_executor());
2227 assert_eq!(buffer.text(), "abcdef");
23− buffer.edit([(0..0, "ghi")]);
28+ buffer.edit([(0..0, "ghi")], cx.background_executor());
2429 assert_eq!(buffer.text(), "ghiabcdef");
25− buffer.edit([(5..5, "jkl")]);
30+ buffer.edit([(5..5, "jkl")], cx.background_executor());
2631 assert_eq!(buffer.text(), "ghiabjklcdef");
27− buffer.edit([(6..7, "")]);
32+ buffer.edit([(6..7, "")], cx.background_executor());
2833 assert_eq!(buffer.text(), "ghiabjlcdef");
29− buffer.edit([(4..9, "mno")]);
34+ buffer.edit([(4..9, "mno")], cx.background_executor());
3035 assert_eq!(buffer.text(), "ghiamnoef");
3136 }
3237
3338 #[gpui::test(iterations = 100)]
34−fn test_random_edits(mut rng: StdRng) {
39+fn test_random_edits(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
3540 let operations = env::var("OPERATIONS")
3641 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
3742 .unwrap_or(10);
@@ -44,6 +49,7 @@ fn test_random_edits(mut rng: StdRng) { · 6 unmodified lines
4449 ReplicaId::LOCAL,
4550 BufferId::new(1).unwrap(),
4651 reference_string.clone(),
52+ cx.background_executor(),
4753 );
4854 LineEnding::normalize(&mut reference_string);
4955
@@ -56,7 +62,7 @@ fn test_random_edits(mut rng: StdRng) { · 6 unmodified lines
5662 );
5763
5864 for _i in 0..operations {
59− let (edits, _) = buffer.randomly_edit(&mut rng, 5);
65+ let (edits, _) = buffer.randomly_edit(&mut rng, 5, cx.background_executor());
6066 for (old_range, new_text) in edits.iter().rev() {
6167 reference_string.replace_range(old_range.clone(), new_text);
6268 }
@@ -106,7 +112,11 @@ fn test_random_edits(mut rng: StdRng) { · 43 unmodified lines
106112 let mut text = old_buffer.visible_text.clone();
107113 for edit in edits {
108114 let new_text: String = buffer.text_for_range(edit.new.clone()).collect();
109− text.replace(edit.new.start..edit.new.start + edit.old.len(), &new_text);
115+ text.replace(
116+ edit.new.start..edit.new.start + edit.old.len(),
117+ &new_text,
118+ cx.background_executor(),
119+ );
110120 }
111121 assert_eq!(text.to_string(), buffer.text());
112122
@@ -161,14 +171,18 @@ fn test_random_edits(mut rng: StdRng) { · 48 unmodified lines
161171 let mut text = old_buffer.visible_text.clone();
162172 for edit in subscription_edits.into_inner() {
163173 let new_text: String = buffer.text_for_range(edit.new.clone()).collect();
164− text.replace(edit.new.start..edit.new.start + edit.old.len(), &new_text);
174+ text.replace(
175+ edit.new.start..edit.new.start + edit.old.len(),
176+ &new_text,
177+ cx.background_executor(),
178+ );
165179 }
166180 assert_eq!(text.to_string(), buffer.text());
167181 }
168182 }
169183
170−#[test]
171−fn test_line_endings() {
184+#[gpui::test]
185+fn test_line_endings(cx: &mut gpui::TestAppContext) {
172186 assert_eq!(LineEnding::detect(&"🍐✅\n".repeat(1000)), LineEnding::Unix);
173187 assert_eq!(LineEnding::detect(&"abcd\n".repeat(1000)), LineEnding::Unix);
174188 assert_eq!(
@@ -184,25 +198,34 @@ fn test_line_endings() { · 9 unmodified lines
184198 ReplicaId::LOCAL,
185199 BufferId::new(1).unwrap(),
186200 "one\r\ntwo\rthree",
201+ cx.background_executor(),
187202 );
188203 assert_eq!(buffer.text(), "one\ntwo\nthree");
189204 assert_eq!(buffer.line_ending(), LineEnding::Windows);
190205 buffer.check_invariants();
191206
192− buffer.edit([(buffer.len()..buffer.len(), "\r\nfour")]);
193− buffer.edit([(0..0, "zero\r\n")]);
207+ buffer.edit(
208+ [(buffer.len()..buffer.len(), "\r\nfour")],
209+ cx.background_executor(),
210+ );
211+ buffer.edit([(0..0, "zero\r\n")], cx.background_executor());
194212 assert_eq!(buffer.text(), "zero\none\ntwo\nthree\nfour");
195213 assert_eq!(buffer.line_ending(), LineEnding::Windows);
196214 buffer.check_invariants();
197215 }
198216
199−#[test]
200−fn test_line_len() {
201− let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "");
202− buffer.edit([(0..0, "abcd\nefg\nhij")]);
203− buffer.edit([(12..12, "kl\nmno")]);
204− buffer.edit([(18..18, "\npqrs\n")]);
205− buffer.edit([(18..21, "\nPQ")]);
217+#[gpui::test]
218+fn test_line_len(cx: &mut gpui::TestAppContext) {
219+ let mut buffer = Buffer::new(
220+ ReplicaId::LOCAL,
221+ BufferId::new(1).unwrap(),
222+ "",
223+ cx.background_executor(),
224+ );
225+ buffer.edit([(0..0, "abcd\nefg\nhij")], cx.background_executor());
226+ buffer.edit([(12..12, "kl\nmno")], cx.background_executor());
227+ buffer.edit([(18..18, "\npqrs\n")], cx.background_executor());
228+ buffer.edit([(18..21, "\nPQ")], cx.background_executor());
206229
207230 assert_eq!(buffer.line_len(0), 4);
208231 assert_eq!(buffer.line_len(1), 3);
@@ -212,10 +235,15 @@ fn test_line_len() { · 3 unmodified lines
212235 assert_eq!(buffer.line_len(5), 0);
213236 }
214237
215−#[test]
216−fn test_common_prefix_at_position() {
238+#[gpui::test]
239+fn test_common_prefix_at_position(cx: &mut gpui::TestAppContext) {
217240 let text = "a = str; b = δα";
218− let buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), text);
241+ let buffer = Buffer::new(
242+ ReplicaId::LOCAL,
243+ BufferId::new(1).unwrap(),
244+ text,
245+ cx.background_executor(),
246+ );
219247
220248 let offset1 = offset_after(text, "str");
221249 let offset2 = offset_after(text, "δα");
@@ -261,12 +289,13 @@ fn test_common_prefix_at_position() { · 39 unmodified lines
261289 }
262290 }
263291
264−#[test]
265−fn test_text_summary_for_range() {
292+#[gpui::test]
293+fn test_text_summary_for_range(cx: &mut gpui::TestAppContext) {
266294 let buffer = Buffer::new(
267295 ReplicaId::LOCAL,
268296 BufferId::new(1).unwrap(),
269297 "ab\nefg\nhklm\nnopqrs\ntuvwxyz",
298+ cx.background_executor(),
270299 );
271300 assert_eq!(
272301 buffer.text_summary_for_range::<TextSummary, _>(0..2),
@@ -354,13 +383,18 @@ fn test_text_summary_for_range() { · 81 unmodified lines
354383 );
355384 }
356385
357−#[test]
358−fn test_chars_at() {
359− let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "");
360− buffer.edit([(0..0, "abcd\nefgh\nij")]);
361− buffer.edit([(12..12, "kl\nmno")]);
362− buffer.edit([(18..18, "\npqrs")]);
363− buffer.edit([(18..21, "\nPQ")]);
386+#[gpui::test]
387+fn test_chars_at(cx: &mut gpui::TestAppContext) {
388+ let mut buffer = Buffer::new(
389+ ReplicaId::LOCAL,
390+ BufferId::new(1).unwrap(),
391+ "",
392+ cx.background_executor(),
393+ );
394+ buffer.edit([(0..0, "abcd\nefgh\nij")], cx.background_executor());
395+ buffer.edit([(12..12, "kl\nmno")], cx.background_executor());
396+ buffer.edit([(18..18, "\npqrs")], cx.background_executor());
397+ buffer.edit([(18..21, "\nPQ")], cx.background_executor());
364398
365399 let chars = buffer.chars_at(Point::new(0, 0));
366400 assert_eq!(chars.collect::<String>(), "abcd\nefgh\nijkl\nmno\nPQrs");
@@ -378,43 +412,53 @@ fn test_chars_at() { · 11 unmodified lines
378412 assert_eq!(chars.collect::<String>(), "PQrs");
379413
380414 // Regression test:
381− let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "");
382− buffer.edit([(0..0, "[workspace]\nmembers = [\n \"xray_core\",\n \"xray_server\",\n \"xray_cli\",\n \"xray_wasm\",\n]\n")]);
383− buffer.edit([(60..60, "\n")]);
415+ let mut buffer = Buffer::new(
416+ ReplicaId::LOCAL,
417+ BufferId::new(1).unwrap(),
418+ "",
419+ cx.background_executor(),
420+ );
421+ buffer.edit([(0..0, "[workspace]\nmembers = [\n \"xray_core\",\n \"xray_server\",\n \"xray_cli\",\n \"xray_wasm\",\n]\n")], cx.background_executor());
422+ buffer.edit([(60..60, "\n")], cx.background_executor());
384423
385424 let chars = buffer.chars_at(Point::new(6, 0));
386425 assert_eq!(chars.collect::<String>(), " \"xray_wasm\",\n]\n");
387426 }
388427
389−#[test]
390−fn test_anchors() {
391− let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "");
392− buffer.edit([(0..0, "abc")]);
428+#[gpui::test]
429+fn test_anchors(cx: &mut gpui::TestAppContext) {
430+ let mut buffer = Buffer::new(
431+ ReplicaId::LOCAL,
432+ BufferId::new(1).unwrap(),
433+ "",
434+ cx.background_executor(),
435+ );
436+ buffer.edit([(0..0, "abc")], cx.background_executor());
393437 let left_anchor = buffer.anchor_before(2);
394438 let right_anchor = buffer.anchor_after(2);
395439
396− buffer.edit([(1..1, "def\n")]);
440+ buffer.edit([(1..1, "def\n")], cx.background_executor());
397441 assert_eq!(buffer.text(), "adef\nbc");
398442 assert_eq!(left_anchor.to_offset(&buffer), 6);
399443 assert_eq!(right_anchor.to_offset(&buffer), 6);
400444 assert_eq!(left_anchor.to_point(&buffer), Point { row: 1, column: 1 });
401445 assert_eq!(right_anchor.to_point(&buffer), Point { row: 1, column: 1 });
402446
403− buffer.edit([(2..3, "")]);
447+ buffer.edit([(2..3, "")], cx.background_executor());
404448 assert_eq!(buffer.text(), "adf\nbc");
405449 assert_eq!(left_anchor.to_offset(&buffer), 5);
406450 assert_eq!(right_anchor.to_offset(&buffer), 5);
407451 assert_eq!(left_anchor.to_point(&buffer), Point { row: 1, column: 1 });
408452 assert_eq!(right_anchor.to_point(&buffer), Point { row: 1, column: 1 });
409453
410− buffer.edit([(5..5, "ghi\n")]);
454+ buffer.edit([(5..5, "ghi\n")], cx.background_executor());
411455 assert_eq!(buffer.text(), "adf\nbghi\nc");
412456 assert_eq!(left_anchor.to_offset(&buffer), 5);
413457 assert_eq!(right_anchor.to_offset(&buffer), 9);
414458 assert_eq!(left_anchor.to_point(&buffer), Point { row: 1, column: 1 });
415459 assert_eq!(right_anchor.to_point(&buffer), Point { row: 2, column: 0 });
416460
417− buffer.edit([(7..9, "")]);
461+ buffer.edit([(7..9, "")], cx.background_executor());
418462 assert_eq!(buffer.text(), "adf\nbghc");
419463 assert_eq!(left_anchor.to_offset(&buffer), 5);
420464 assert_eq!(right_anchor.to_offset(&buffer), 7);
@@ -504,13 +548,18 @@ fn test_anchors() { · 83 unmodified lines
504548 );
505549 }
506550
507−#[test]
508−fn test_anchors_at_start_and_end() {
509− let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "");
551+#[gpui::test]
552+fn test_anchors_at_start_and_end(cx: &mut gpui::TestAppContext) {
553+ let mut buffer = Buffer::new(
554+ ReplicaId::LOCAL,
555+ BufferId::new(1).unwrap(),
556+ "",
557+ cx.background_executor(),
558+ );
510559 let before_start_anchor = buffer.anchor_before(0);
511560 let after_end_anchor = buffer.anchor_after(0);
512561
513− buffer.edit([(0..0, "abc")]);
562+ buffer.edit([(0..0, "abc")], cx.background_executor());
514563 assert_eq!(buffer.text(), "abc");
515564 assert_eq!(before_start_anchor.to_offset(&buffer), 0);
516565 assert_eq!(after_end_anchor.to_offset(&buffer), 3);
@@ -518,8 +567,8 @@ fn test_anchors_at_start_and_end() { · 1 unmodified line
518567 let after_start_anchor = buffer.anchor_after(0);
519568 let before_end_anchor = buffer.anchor_before(3);
520569
521− buffer.edit([(3..3, "def")]);
522− buffer.edit([(0..0, "ghi")]);
570+ buffer.edit([(3..3, "def")], cx.background_executor());
571+ buffer.edit([(0..0, "ghi")], cx.background_executor());
523572 assert_eq!(buffer.text(), "ghiabcdef");
524573 assert_eq!(before_start_anchor.to_offset(&buffer), 0);
525574 assert_eq!(after_start_anchor.to_offset(&buffer), 3);
@@ -527,15 +576,20 @@ fn test_anchors_at_start_and_end() { · 1 unmodified line
527576 assert_eq!(after_end_anchor.to_offset(&buffer), 9);
528577 }
529578
530−#[test]
531−fn test_undo_redo() {
532− let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "1234");
579+#[gpui::test]
580+fn test_undo_redo(cx: &mut gpui::TestAppContext) {
581+ let mut buffer = Buffer::new(
582+ ReplicaId::LOCAL,
583+ BufferId::new(1).unwrap(),
584+ "1234",
585+ cx.background_executor(),
586+ );
533587 // Set group interval to zero so as to not group edits in the undo stack.
534588 buffer.set_group_interval(Duration::from_secs(0));
535589
536− buffer.edit([(1..1, "abx")]);
537− buffer.edit([(3..4, "yzef")]);
538− buffer.edit([(3..5, "cd")]);
590+ buffer.edit([(1..1, "abx")], cx.background_executor());
591+ buffer.edit([(3..4, "yzef")], cx.background_executor());
592+ buffer.edit([(3..5, "cd")], cx.background_executor());
539593 assert_eq!(buffer.text(), "1abcdef234");
540594
541595 let entries = buffer.history.undo_stack.clone();
@@ -563,26 +617,31 @@ fn test_undo_redo() { · 21 unmodified lines
563617 assert_eq!(buffer.text(), "1234");
564618 }
565619
566−#[test]
567−fn test_history() {
620+#[gpui::test]
621+fn test_history(cx: &mut gpui::TestAppContext) {
568622 let mut now = Instant::now();
569− let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "123456");
623+ let mut buffer = Buffer::new(
624+ ReplicaId::LOCAL,
625+ BufferId::new(1).unwrap(),
626+ "123456",
627+ cx.background_executor(),
628+ );
570629 buffer.set_group_interval(Duration::from_millis(300));
571630
572631 let transaction_1 = buffer.start_transaction_at(now).unwrap();
573− buffer.edit([(2..4, "cd")]);
632+ buffer.edit([(2..4, "cd")], cx.background_executor());
574633 buffer.end_transaction_at(now);
575634 assert_eq!(buffer.text(), "12cd56");
576635
577636 buffer.start_transaction_at(now);
578− buffer.edit([(4..5, "e")]);
637+ buffer.edit([(4..5, "e")], cx.background_executor());
579638 buffer.end_transaction_at(now).unwrap();
580639 assert_eq!(buffer.text(), "12cde6");
581640
582641 now += buffer.transaction_group_interval() + Duration::from_millis(1);
583642 buffer.start_transaction_at(now);
584− buffer.edit([(0..1, "a")]);
585− buffer.edit([(1..1, "b")]);
643+ buffer.edit([(0..1, "a")], cx.background_executor());
644+ buffer.edit([(1..1, "b")], cx.background_executor());
586645 buffer.end_transaction_at(now).unwrap();
587646 assert_eq!(buffer.text(), "ab2cde6");
588647
@@ -609,7 +668,7 @@ fn test_history() { · 20 unmodified lines
609668
610669 // Redo stack gets cleared after performing an edit.
611670 buffer.start_transaction_at(now);
612− buffer.edit([(0..0, "X")]);
671+ buffer.edit([(0..0, "X")], cx.background_executor());
613672 buffer.end_transaction_at(now);
614673 assert_eq!(buffer.text(), "X12cde6");
615674 buffer.redo();
@@ -630,26 +689,31 @@ fn test_history() { · 14 unmodified lines
630689 assert_eq!(buffer.text(), "X12cde6");
631690 }
632691
633−#[test]
634−fn test_finalize_last_transaction() {
692+#[gpui::test]
693+fn test_finalize_last_transaction(cx: &mut gpui::TestAppContext) {
635694 let now = Instant::now();
636− let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "123456");
695+ let mut buffer = Buffer::new(
696+ ReplicaId::LOCAL,
697+ BufferId::new(1).unwrap(),
698+ "123456",
699+ cx.background_executor(),
700+ );
637701 buffer.history.group_interval = Duration::from_millis(1);
638702
639703 buffer.start_transaction_at(now);
640− buffer.edit([(2..4, "cd")]);
704+ buffer.edit([(2..4, "cd")], cx.background_executor());
641705 buffer.end_transaction_at(now);
642706 assert_eq!(buffer.text(), "12cd56");
643707
644708 buffer.finalize_last_transaction();
645709 buffer.start_transaction_at(now);
646− buffer.edit([(4..5, "e")]);
710+ buffer.edit([(4..5, "e")], cx.background_executor());
647711 buffer.end_transaction_at(now).unwrap();
648712 assert_eq!(buffer.text(), "12cde6");
649713
650714 buffer.start_transaction_at(now);
651− buffer.edit([(0..1, "a")]);
652− buffer.edit([(1..1, "b")]);
715+ buffer.edit([(0..1, "a")], cx.background_executor());
716+ buffer.edit([(1..1, "b")], cx.background_executor());
653717 buffer.end_transaction_at(now).unwrap();
654718 assert_eq!(buffer.text(), "ab2cde6");
655719
@@ -666,14 +730,19 @@ fn test_finalize_last_transaction() { · 10 unmodified lines
666730 assert_eq!(buffer.text(), "ab2cde6");
667731 }
668732
669−#[test]
670−fn test_edited_ranges_for_transaction() {
733+#[gpui::test]
734+fn test_edited_ranges_for_transaction(cx: &mut gpui::TestAppContext) {
671735 let now = Instant::now();
672− let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "1234567");
736+ let mut buffer = Buffer::new(
737+ ReplicaId::LOCAL,
738+ BufferId::new(1).unwrap(),
739+ "1234567",
740+ cx.background_executor(),
741+ );
673742
674743 buffer.start_transaction_at(now);
675− buffer.edit([(2..4, "cd")]);
676− buffer.edit([(6..6, "efg")]);
744+ buffer.edit([(2..4, "cd")], cx.background_executor());
745+ buffer.edit([(6..6, "efg")], cx.background_executor());
677746 buffer.end_transaction_at(now);
678747 assert_eq!(buffer.text(), "12cd56efg7");
679748
@@ -685,7 +754,7 @@ fn test_edited_ranges_for_transaction() { · 5 unmodified lines
685754 [2..4, 6..9]
686755 );
687756
688− buffer.edit([(5..5, "hijk")]);
757+ buffer.edit([(5..5, "hijk")], cx.background_executor());
689758 assert_eq!(buffer.text(), "12cd5hijk6efg7");
690759 assert_eq!(
691760 buffer
@@ -694,7 +763,7 @@ fn test_edited_ranges_for_transaction() { · 2 unmodified lines
694763 [2..4, 10..13]
695764 );
696765
697− buffer.edit([(4..4, "l")]);
766+ buffer.edit([(4..4, "l")], cx.background_executor());
698767 assert_eq!(buffer.text(), "12cdl5hijk6efg7");
699768 assert_eq!(
700769 buffer
@@ -704,27 +773,42 @@ fn test_edited_ranges_for_transaction() { · 3 unmodified lines
704773 );
705774 }
706775
707−#[test]
708−fn test_concurrent_edits() {
776+#[gpui::test]
777+fn test_concurrent_edits(cx: &mut gpui::TestAppContext) {
709778 let text = "abcdef";
710779
711− let mut buffer1 = Buffer::new(ReplicaId::new(1), BufferId::new(1).unwrap(), text);
712− let mut buffer2 = Buffer::new(ReplicaId::new(2), BufferId::new(1).unwrap(), text);
713− let mut buffer3 = Buffer::new(ReplicaId::new(3), BufferId::new(1).unwrap(), text);
780+ let mut buffer1 = Buffer::new(
781+ ReplicaId::new(1),
782+ BufferId::new(1).unwrap(),
783+ text,
784+ cx.background_executor(),
785+ );
786+ let mut buffer2 = Buffer::new(
787+ ReplicaId::new(2),
788+ BufferId::new(1).unwrap(),
789+ text,
790+ cx.background_executor(),
791+ );
792+ let mut buffer3 = Buffer::new(
793+ ReplicaId::new(3),
794+ BufferId::new(1).unwrap(),
795+ text,
796+ cx.background_executor(),
797+ );
714798
715− let buf1_op = buffer1.edit([(1..2, "12")]);
799+ let buf1_op = buffer1.edit([(1..2, "12")], cx.background_executor());
716800 assert_eq!(buffer1.text(), "a12cdef");
717− let buf2_op = buffer2.edit([(3..4, "34")]);
801+ let buf2_op = buffer2.edit([(3..4, "34")], cx.background_executor());
718802 assert_eq!(buffer2.text(), "abc34ef");
719− let buf3_op = buffer3.edit([(5..6, "56")]);
803+ let buf3_op = buffer3.edit([(5..6, "56")], cx.background_executor());
720804 assert_eq!(buffer3.text(), "abcde56");
721805
722− buffer1.apply_op(buf2_op.clone());
723− buffer1.apply_op(buf3_op.clone());
724− buffer2.apply_op(buf1_op.clone());
725− buffer2.apply_op(buf3_op);
726− buffer3.apply_op(buf1_op);
727− buffer3.apply_op(buf2_op);
806+ buffer1.apply_op(buf2_op.clone(), Some(cx.background_executor()));
807+ buffer1.apply_op(buf3_op.clone(), Some(cx.background_executor()));
808+ buffer2.apply_op(buf1_op.clone(), Some(cx.background_executor()));
809+ buffer2.apply_op(buf3_op, Some(cx.background_executor()));
810+ buffer3.apply_op(buf1_op, Some(cx.background_executor()));
811+ buffer3.apply_op(buf2_op, Some(cx.background_executor()));
728812
729813 assert_eq!(buffer1.text(), "a12c34e56");
730814 assert_eq!(buffer2.text(), "a12c34e56");
@@ -732,7 +816,7 @@ fn test_concurrent_edits() { · 1 unmodified line
732816 }
733817
734818 #[gpui::test(iterations = 100)]
735−fn test_random_concurrent_edits(mut rng: StdRng) {
819+fn test_random_concurrent_edits(mut rng: StdRng, cx: &mut gpui::TestAppContext) {
736820 let peers = env::var("PEERS")
737821 .map(|i| i.parse().expect("invalid `PEERS` variable"))
738822 .unwrap_or(5);
@@ -753,6 +837,7 @@ fn test_random_concurrent_edits(mut rng: StdRng) { · 14 unmodified lines
753837 ReplicaId::new(i as u16),
754838 BufferId::new(1).unwrap(),
755839 base_text.clone(),
840+ cx.background_executor(),
756841 );
757842 buffer.history.group_interval = Duration::from_millis(rng.random_range(0..=200));
758843 buffers.push(buffer);
@@ -769,7 +854,9 @@ fn test_random_concurrent_edits(mut rng: StdRng) { · 10 unmodified lines
769854 let buffer = &mut buffers[replica_index];
770855 match rng.random_range(0..=100) {
771856 0..=50 if mutation_count != 0 => {
772− let op = buffer.randomly_edit(&mut rng, 5).1;
857+ let op = buffer
858+ .randomly_edit(&mut rng, 5, cx.background_executor())
859+ .1;
773860 network.broadcast(buffer.replica_id, vec![op]);
774861 log::info!("buffer {:?} text: {:?}", buffer.replica_id, buffer.text());
775862 mutation_count -= 1;
@@ -787,7 +874,7 @@ fn test_random_concurrent_edits(mut rng: StdRng) { · 11 unmodified lines
787874 replica_id,
788875 ops.len()
789876 );
790− buffer.apply_ops(ops);
877+ buffer.apply_ops(ops, Some(cx.background_executor()));
791878 }
792879 }
793880 _ => {}
crates/text/src/text.rs+63−17
@@ -15,6 +15,7 @@ use anyhow::{Context as _, Result}; · 14 unmodified lines
1515 use clock::Lamport;
1616 pub use clock::ReplicaId;
1717 use collections::{HashMap, HashSet};
18+use gpui::BackgroundExecutor;
1819 use locator::Locator;
1920 use operation_queue::OperationQueue;
2021 pub use patch::Patch;
@@ -709,11 +710,41 @@ impl FromIterator<char> for LineIndent { · 688 unmodified lines
709710 }
710711
711712 impl Buffer {
712− pub fn new(replica_id: ReplicaId, remote_id: BufferId, base_text: impl Into<String>) -> Buffer {
713+ /// Create a new buffer from a string.
714+ pub fn new(
715+ replica_id: ReplicaId,
716+ remote_id: BufferId,
717+ base_text: impl Into<String>,
718+ executor: &BackgroundExecutor,
719+ ) -> Buffer {
713720 let mut base_text = base_text.into();
714721 let line_ending = LineEnding::detect(&base_text);
715722 LineEnding::normalize(&mut base_text);
716− Self::new_normalized(replica_id, remote_id, line_ending, Rope::from(&*base_text))
723+ Self::new_normalized(
724+ replica_id,
725+ remote_id,
726+ line_ending,
727+ Rope::from_str(&base_text, executor),
728+ )
729+ }
730+
731+ /// Create a new buffer from a string.
732+ ///
733+ /// Unlike [`Buffer::new`], this does not construct the backing rope in parallel if it is large enough.
734+ pub fn new_slow(
735+ replica_id: ReplicaId,
736+ remote_id: BufferId,
737+ base_text: impl Into<String>,
738+ ) -> Buffer {
739+ let mut base_text = base_text.into();
740+ let line_ending = LineEnding::detect(&base_text);
741+ LineEnding::normalize(&mut base_text);
742+ Self::new_normalized(
743+ replica_id,
744+ remote_id,
745+ line_ending,
746+ Rope::from_str_small(&base_text),
747+ )
717748 }
718749
719750 pub fn new_normalized(
@@ -808,7 +839,7 @@ impl Buffer { · 88 unmodified lines
808839 self.history.group_interval
809840 }
810841
811− pub fn edit<R, I, S, T>(&mut self, edits: R) -> Operation
842+ pub fn edit<R, I, S, T>(&mut self, edits: R, cx: &BackgroundExecutor) -> Operation
812843 where
813844 R: IntoIterator<IntoIter = I>,
814845 I: ExactSizeIterator<Item = (Range<S>, T)>,
@@ -821,7 +852,7 @@ impl Buffer { · 6 unmodified lines
821852
822853 self.start_transaction();
823854 let timestamp = self.lamport_clock.tick();
824− let operation = Operation::Edit(self.apply_local_edit(edits, timestamp));
855+ let operation = Operation::Edit(self.apply_local_edit(edits, timestamp, cx));
825856
826857 self.history.push(operation.clone());
827858 self.history.push_undo(operation.timestamp());
@@ -834,6 +865,7 @@ impl Buffer { · 6 unmodified lines
834865 &mut self,
835866 edits: impl ExactSizeIterator<Item = (Range<S>, T)>,
836867 timestamp: clock::Lamport,
868+ executor: &BackgroundExecutor,
837869 ) -> EditOperation {
838870 let mut edits_patch = Patch::default();
839871 let mut edit_op = EditOperation {
@@ -922,7 +954,7 @@ impl Buffer { · 82 unmodified lines
922954 });
923955 insertion_slices.push(InsertionSlice::from_fragment(timestamp, &fragment));
924956 new_insertions.push(InsertionFragment::insert_new(&fragment));
925− new_ropes.push_str(new_text.as_ref());
957+ new_ropes.push_str(new_text.as_ref(), executor);
926958 new_fragments.push(fragment, &None);
927959 insertion_offset += new_text.len();
928960 }
@@ -1001,22 +1033,26 @@ impl Buffer { · 72 unmodified lines
10011033 self.snapshot.line_ending = line_ending;
10021034 }
10031035
1004− pub fn apply_ops<I: IntoIterator<Item = Operation>>(&mut self, ops: I) {
1036+ pub fn apply_ops<I: IntoIterator<Item = Operation>>(
1037+ &mut self,
1038+ ops: I,
1039+ executor: Option<&BackgroundExecutor>,
1040+ ) {
10051041 let mut deferred_ops = Vec::new();
10061042 for op in ops {
10071043 self.history.push(op.clone());
10081044 if self.can_apply_op(&op) {
1009− self.apply_op(op);
1045+ self.apply_op(op, executor);
10101046 } else {
10111047 self.deferred_replicas.insert(op.replica_id());
10121048 deferred_ops.push(op);
10131049 }
10141050 }
10151051 self.deferred_ops.insert(deferred_ops);
1016− self.flush_deferred_ops();
1052+ self.flush_deferred_ops(executor);
10171053 }
10181054
1019− fn apply_op(&mut self, op: Operation) {
1055+ fn apply_op(&mut self, op: Operation, executor: Option<&BackgroundExecutor>) {
10201056 match op {
10211057 Operation::Edit(edit) => {
10221058 if !self.version.observed(edit.timestamp) {
@@ -1025,6 +1061,7 @@ impl Buffer { · 2 unmodified lines
10251061 &edit.ranges,
10261062 &edit.new_text,
10271063 edit.timestamp,
1064+ executor,
10281065 );
10291066 self.snapshot.version.observe(edit.timestamp);
10301067 self.lamport_clock.observe(edit.timestamp);
@@ -1055,6 +1092,7 @@ impl Buffer { · 24 unmodified lines
10551092 ranges: &[Range<FullOffset>],
10561093 new_text: &[Arc<str>],
10571094 timestamp: clock::Lamport,
1095+ executor: Option<&BackgroundExecutor>,
10581096 ) {
10591097 if ranges.is_empty() {
10601098 return;
@@ -1170,7 +1208,10 @@ impl Buffer { · 109 unmodified lines
11701208 });
11711209 insertion_slices.push(InsertionSlice::from_fragment(timestamp, &fragment));
11721210 new_insertions.push(InsertionFragment::insert_new(&fragment));
1173− new_ropes.push_str(new_text);
1211+ match executor {
1212+ Some(executor) => new_ropes.push_str(new_text, executor),
1213+ None => new_ropes.push_str_small(new_text),
1214+ }
11741215 new_fragments.push(fragment, &None);
11751216 insertion_offset += new_text.len();
11761217 }
@@ -1348,12 +1389,12 @@ impl Buffer { · 171 unmodified lines
13481389 self.subscriptions.publish_mut(&edits);
13491390 }
13501391
1351− fn flush_deferred_ops(&mut self) {
1392+ fn flush_deferred_ops(&mut self, executor: Option<&BackgroundExecutor>) {
13521393 self.deferred_replicas.clear();
13531394 let mut deferred_ops = Vec::new();
13541395 for op in self.deferred_ops.drain().iter().cloned() {
13551396 if self.can_apply_op(&op) {
1356− self.apply_op(op);
1397+ self.apply_op(op, executor);
13571398 } else {
13581399 self.deferred_replicas.insert(op.replica_id());
13591400 deferred_ops.push(op);
@@ -1711,9 +1752,9 @@ impl Buffer { · 351 unmodified lines
17111752 #[cfg(any(test, feature = "test-support"))]
17121753 impl Buffer {
17131754 #[track_caller]
1714− pub fn edit_via_marked_text(&mut self, marked_string: &str) {
1755+ pub fn edit_via_marked_text(&mut self, marked_string: &str, cx: &BackgroundExecutor) {
17151756 let edits = self.edits_for_marked_text(marked_string);
1716− self.edit(edits);
1757+ self.edit(edits, cx);
17171758 }
17181759
17191760 #[track_caller]
@@ -1850,6 +1891,7 @@ impl Buffer { · 130 unmodified lines
18501891 &mut self,
18511892 rng: &mut T,
18521893 edit_count: usize,
1894+ executor: &BackgroundExecutor,
18531895 ) -> (Vec<(Range<usize>, Arc<str>)>, Operation)
18541896 where
18551897 T: rand::Rng,
@@ -1857,7 +1899,7 @@ impl Buffer { · 1 unmodified line
18571899 let mut edits = self.get_random_edits(rng, edit_count);
18581900 log::info!("mutating buffer {:?} with {:?}", self.replica_id, edits);
18591901
1860− let op = self.edit(edits.iter().cloned());
1902+ let op = self.edit(edits.iter().cloned(), executor);
18611903 if let Operation::Edit(edit) = &op {
18621904 assert_eq!(edits.len(), edit.new_text.len());
18631905 for (edit, new_text) in edits.iter_mut().zip(&edit.new_text) {
@@ -2692,8 +2734,12 @@ impl<'a> RopeBuilder<'a> { · 828 unmodified lines
26922734 }
26932735 }
26942736
2695− fn push_str(&mut self, text: &str) {
2696− self.new_visible.push(text);
2737+ fn push_str(&mut self, text: &str, cx: &BackgroundExecutor) {
2738+ self.new_visible.push(text, cx);
2739+ }
2740+
2741+ fn push_str_small(&mut self, text: &str) {
2742+ self.new_visible.push_small(text);
26972743 }
26982744
26992745 fn finish(mut self) -> (Rope, Rope) {
crates/vim/src/motion.rs+6−4
@@ -3096,6 +3096,7 @@ mod test { · 3095 unmodified lines
30963096 use indoc::indoc;
30973097 use language::Point;
30983098 use multi_buffer::MultiBufferRow;
3099+ use text::Rope;
30993100
31003101 #[gpui::test]
31013102 async fn test_start_end_of_paragraph(cx: &mut gpui::TestAppContext) {
@@ -3822,7 +3823,7 @@ mod test { · 720 unmodified lines
38223823 cx.update_editor(|editor, _window, cx| {
38233824 let range = editor.selections.newest_anchor().range();
38243825 let inlay_text = " field: int,\n field2: string\n field3: float";
3825− let inlay = Inlay::edit_prediction(1, range.start, inlay_text);
3826+ let inlay = Inlay::edit_prediction(1, range.start, Rope::from_str_small(inlay_text));
38263827 editor.splice_inlays(&[], vec![inlay], cx);
38273828 });
38283829
@@ -3854,7 +3855,7 @@ mod test { · 25 unmodified lines
38543855 let end_of_line =
38553856 snapshot.anchor_after(Point::new(0, snapshot.line_len(MultiBufferRow(0))));
38563857 let inlay_text = " hint";
3857− let inlay = Inlay::edit_prediction(1, end_of_line, inlay_text);
3858+ let inlay = Inlay::edit_prediction(1, end_of_line, Rope::from_str_small(inlay_text));
38583859 editor.splice_inlays(&[], vec![inlay], cx);
38593860 });
38603861 cx.simulate_keystrokes("$");
@@ -3893,7 +3894,7 @@ mod test { · 32 unmodified lines
38933894 // The empty line is at line 3 (0-indexed)
38943895 let line_start = snapshot.anchor_after(Point::new(3, 0));
38953896 let inlay_text = ": Vec<u32>";
3896− let inlay = Inlay::edit_prediction(1, line_start, inlay_text);
3897+ let inlay = Inlay::edit_prediction(1, line_start, Rope::from_str_small(inlay_text));
38973898 editor.splice_inlays(&[], vec![inlay], cx);
38983899 });
38993900
@@ -3937,7 +3938,8 @@ mod test { · 37 unmodified lines
39373938 let snapshot = editor.buffer().read(cx).snapshot(cx);
39383939 let empty_line_start = snapshot.anchor_after(Point::new(2, 0));
39393940 let inlay_text = ": i32";
3940− let inlay = Inlay::edit_prediction(2, empty_line_start, inlay_text);
3941+ let inlay =
3942+ Inlay::edit_prediction(2, empty_line_start, Rope::from_str_small(inlay_text));
39413943 editor.splice_inlays(&[], vec![inlay], cx);
39423944 });
39433945
crates/workspace/src/workspace.rs+2−2
@@ -7580,13 +7580,13 @@ pub fn create_and_open_local_file( · 7579 unmodified lines
75807580 path: &'static Path,
75817581 window: &mut Window,
75827582 cx: &mut Context<Workspace>,
7583− default_content: impl 'static + Send + FnOnce() -> Rope,
7583+ default_content: impl 'static + Send + FnOnce(&mut AsyncApp) -> Rope,
75847584 ) -> Task<Result<Box<dyn ItemHandle>>> {
75857585 cx.spawn_in(window, async move |workspace, cx| {
75867586 let fs = workspace.read_with(cx, |workspace, _| workspace.app_state().fs.clone())?;
75877587 if !fs.is_file(path).await {
75887588 fs.create_file(path, Default::default()).await?;
7589− fs.save(path, &default_content(), Default::default())
7589+ fs.save(path, &default_content(cx), Default::default())
75907590 .await?;
75917591 }
75927592
crates/worktree/src/worktree_tests.rs+22−12
@@ -20,6 +20,7 @@ use std::{ · 19 unmodified lines
2020 path::{Path, PathBuf},
2121 sync::Arc,
2222 };
23+use text::Rope;
2324 use util::{
2425 ResultExt, path,
2526 rel_path::{RelPath, rel_path},
@@ -646,9 +647,13 @@ async fn test_dirs_no_longer_ignored(cx: &mut TestAppContext) { · 620 unmodified lines
646647
647648 // Update the gitignore so that node_modules is no longer ignored,
648649 // but a subdirectory is ignored
649− fs.save("/root/.gitignore".as_ref(), &"e".into(), Default::default())
650− .await
651− .unwrap();
650+ fs.save(
651+ "/root/.gitignore".as_ref(),
652+ &Rope::from_str("e", cx.background_executor()),
653+ Default::default(),
654+ )
655+ .await
656+ .unwrap();
652657 cx.executor().run_until_parked();
653658
654659 // All of the directories that are no longer ignored are now loaded.
@@ -716,7 +721,7 @@ async fn test_write_file(cx: &mut TestAppContext) { · 61 unmodified lines
716721 .update(cx, |tree, cx| {
717722 tree.write_file(
718723 rel_path("tracked-dir/file.txt").into(),
719− "hello".into(),
724+ Rope::from_str("hello", cx.background_executor()),
720725 Default::default(),
721726 cx,
722727 )
@@ -727,7 +732,7 @@ async fn test_write_file(cx: &mut TestAppContext) { · 4 unmodified lines
727732 .update(cx, |tree, cx| {
728733 tree.write_file(
729734 rel_path("ignored-dir/file.txt").into(),
730− "world".into(),
735+ Rope::from_str("world", cx.background_executor()),
731736 Default::default(),
732737 cx,
733738 )
@@ -1465,7 +1470,7 @@ async fn test_random_worktree_operations_during_initial_scan( · 731 unmodified lines
14651470 let fs = FakeFs::new(cx.background_executor.clone()) as Arc<dyn Fs>;
14661471 fs.as_fake().insert_tree(root_dir, json!({})).await;
14671472 for _ in 0..initial_entries {
1468− randomly_mutate_fs(&fs, root_dir, 1.0, &mut rng).await;
1473+ randomly_mutate_fs(&fs, root_dir, 1.0, &mut rng, cx.background_executor()).await;
14691474 }
14701475 log::info!("generated initial tree");
14711476
@@ -1555,7 +1560,7 @@ async fn test_random_worktree_changes(cx: &mut TestAppContext, mut rng: StdRng) · 83 unmodified lines
15551560 let fs = FakeFs::new(cx.background_executor.clone()) as Arc<dyn Fs>;
15561561 fs.as_fake().insert_tree(root_dir, json!({})).await;
15571562 for _ in 0..initial_entries {
1558− randomly_mutate_fs(&fs, root_dir, 1.0, &mut rng).await;
1563+ randomly_mutate_fs(&fs, root_dir, 1.0, &mut rng, cx.background_executor()).await;
15591564 }
15601565 log::info!("generated initial tree");
15611566
@@ -1598,7 +1603,7 @@ async fn test_random_worktree_changes(cx: &mut TestAppContext, mut rng: StdRng) · 36 unmodified lines
15981603 .await
15991604 .log_err();
16001605 } else {
1601− randomly_mutate_fs(&fs, root_dir, 1.0, &mut rng).await;
1606+ randomly_mutate_fs(&fs, root_dir, 1.0, &mut rng, cx.background_executor()).await;
16021607 }
16031608
16041609 let buffered_event_count = fs.as_fake().buffered_event_count();
@@ -1607,7 +1612,7 @@ async fn test_random_worktree_changes(cx: &mut TestAppContext, mut rng: StdRng) · 2 unmodified lines
16071612 log::info!("flushing {} events", len);
16081613 fs.as_fake().flush_events(len);
16091614 } else {
1610− randomly_mutate_fs(&fs, root_dir, 0.6, &mut rng).await;
1615+ randomly_mutate_fs(&fs, root_dir, 0.6, &mut rng, cx.background_executor()).await;
16111616 mutations_len -= 1;
16121617 }
16131618
@@ -1759,8 +1764,12 @@ fn randomly_mutate_worktree( · 145 unmodified lines
17591764 })
17601765 } else {
17611766 log::info!("overwriting file {:?} ({})", &entry.path, entry.id.0);
1762− let task =
1763− worktree.write_file(entry.path.clone(), "".into(), Default::default(), cx);
1767+ let task = worktree.write_file(
1768+ entry.path.clone(),
1769+ Rope::default(),
1770+ Default::default(),
1771+ cx,
1772+ );
17641773 cx.background_spawn(async move {
17651774 task.await?;
17661775 Ok(())
@@ -1775,6 +1784,7 @@ async fn randomly_mutate_fs( · 8 unmodified lines
17751784 root_path: &Path,
17761785 insertion_probability: f64,
17771786 rng: &mut impl Rng,
1787+ executor: &BackgroundExecutor,
17781788 ) {
17791789 log::info!("mutating fs");
17801790 let mut files = Vec::new();
@@ -1849,7 +1859,7 @@ async fn randomly_mutate_fs( · 68 unmodified lines
18491859 );
18501860 fs.save(
18511861 &ignore_path,
1852− &ignore_contents.as_str().into(),
1862+ &Rope::from_str(ignore_contents.as_str(), executor),
18531863 Default::default(),
18541864 )
18551865 .await
crates/zed/src/zed.rs+37−17
@@ -28,10 +28,10 @@ use git_ui::commit_view::CommitViewToolbar; · 27 unmodified lines
2828 use git_ui::git_panel::GitPanel;
2929 use git_ui::project_diff::ProjectDiffToolbar;
3030 use gpui::{
31− Action, App, AppContext as _, Context, DismissEvent, Element, Entity, Focusable, KeyBinding,
32− ParentElement, PathPromptOptions, PromptLevel, ReadGlobal, SharedString, Styled, Task,
33− TitlebarOptions, UpdateGlobal, Window, WindowKind, WindowOptions, actions, image_cache, point,
34− px, retain_all,
31+ Action, App, AppContext as _, AsyncApp, Context, DismissEvent, Element, Entity, Focusable,
32+ KeyBinding, ParentElement, PathPromptOptions, PromptLevel, ReadGlobal, SharedString, Styled,
33+ Task, TitlebarOptions, UpdateGlobal, Window, WindowKind, WindowOptions, actions, image_cache,
34+ point, px, retain_all,
3535 };
3636 use image_viewer::ImageInfo;
3737 use language::Capability;
@@ -201,7 +201,12 @@ pub fn init(cx: &mut App) { · 163 unmodified lines
201201 with_active_or_new_workspace(cx, |_, window, cx| {
202202 open_settings_file(
203203 paths::keymap_file(),
204− || settings::initial_keymap_content().as_ref().into(),
204+ |cx| {
205+ Rope::from_str(
206+ settings::initial_keymap_content().as_ref(),
207+ cx.background_executor(),
208+ )
209+ },
205210 window,
206211 cx,
207212 );
@@ -211,7 +216,12 @@ pub fn init(cx: &mut App) { · 3 unmodified lines
211216 with_active_or_new_workspace(cx, |_, window, cx| {
212217 open_settings_file(
213218 paths::settings_file(),
214− || settings::initial_user_settings_content().as_ref().into(),
219+ |cx| {
220+ Rope::from_str(
221+ settings::initial_user_settings_content().as_ref(),
222+ cx.background_executor(),
223+ )
224+ },
215225 window,
216226 cx,
217227 );
@@ -226,7 +236,12 @@ pub fn init(cx: &mut App) { · 8 unmodified lines
226236 with_active_or_new_workspace(cx, |_, window, cx| {
227237 open_settings_file(
228238 paths::tasks_file(),
229− || settings::initial_tasks_content().as_ref().into(),
239+ |cx| {
240+ Rope::from_str(
241+ settings::initial_tasks_content().as_ref(),
242+ cx.background_executor(),
243+ )
244+ },
230245 window,
231246 cx,
232247 );
@@ -236,7 +251,12 @@ pub fn init(cx: &mut App) { · 3 unmodified lines
236251 with_active_or_new_workspace(cx, |_, window, cx| {
237252 open_settings_file(
238253 paths::debug_scenarios_file(),
239− || settings::initial_debug_tasks_content().as_ref().into(),
254+ |cx| {
255+ Rope::from_str(
256+ settings::initial_debug_tasks_content().as_ref(),
257+ cx.background_executor(),
258+ )
259+ },
240260 window,
241261 cx,
242262 );
@@ -1939,7 +1959,7 @@ fn open_bundled_file( · 1696 unmodified lines
19391959
19401960 fn open_settings_file(
19411961 abs_path: &'static Path,
1942− default_content: impl FnOnce() -> Rope + Send + 'static,
1962+ default_content: impl FnOnce(&mut AsyncApp) -> Rope + Send + 'static,
19431963 window: &mut Window,
19441964 cx: &mut Context<Workspace>,
19451965 ) {
@@ -4355,7 +4375,7 @@ mod tests { · 2409 unmodified lines
43554375 .fs
43564376 .save(
43574377 "/settings.json".as_ref(),
4358− &r#"{"base_keymap": "Atom"}"#.into(),
4378+ &Rope::from_str_small(r#"{"base_keymap": "Atom"}"#),
43594379 Default::default(),
43604380 )
43614381 .await
@@ -4365,7 +4385,7 @@ mod tests { · 3 unmodified lines
43654385 .fs
43664386 .save(
43674387 "/keymap.json".as_ref(),
4368− &r#"[{"bindings": {"backspace": "test_only::ActionA"}}]"#.into(),
4388+ &Rope::from_str_small(r#"[{"bindings": {"backspace": "test_only::ActionA"}}]"#),
43694389 Default::default(),
43704390 )
43714391 .await
@@ -4413,7 +4433,7 @@ mod tests { · 41 unmodified lines
44134433 .fs
44144434 .save(
44154435 "/keymap.json".as_ref(),
4416− &r#"[{"bindings": {"backspace": "test_only::ActionB"}}]"#.into(),
4436+ &Rope::from_str_small(r#"[{"bindings": {"backspace": "test_only::ActionB"}}]"#),
44174437 Default::default(),
44184438 )
44194439 .await
@@ -4433,7 +4453,7 @@ mod tests { · 13 unmodified lines
44334453 .fs
44344454 .save(
44354455 "/settings.json".as_ref(),
4436− &r#"{"base_keymap": "JetBrains"}"#.into(),
4456+ &Rope::from_str_small(r#"{"base_keymap": "JetBrains"}"#),
44374457 Default::default(),
44384458 )
44394459 .await
@@ -4473,7 +4493,7 @@ mod tests { · 33 unmodified lines
44734493 .fs
44744494 .save(
44754495 "/settings.json".as_ref(),
4476− &r#"{"base_keymap": "Atom"}"#.into(),
4496+ &Rope::from_str_small(r#"{"base_keymap": "Atom"}"#),
44774497 Default::default(),
44784498 )
44794499 .await
@@ -4482,7 +4502,7 @@ mod tests { · 2 unmodified lines
44824502 .fs
44834503 .save(
44844504 "/keymap.json".as_ref(),
4485− &r#"[{"bindings": {"backspace": "test_only::ActionA"}}]"#.into(),
4505+ &Rope::from_str_small(r#"[{"bindings": {"backspace": "test_only::ActionA"}}]"#),
44864506 Default::default(),
44874507 )
44884508 .await
@@ -4525,7 +4545,7 @@ mod tests { · 36 unmodified lines
45254545 .fs
45264546 .save(
45274547 "/keymap.json".as_ref(),
4528− &r#"[{"bindings": {"backspace": null}}]"#.into(),
4548+ &Rope::from_str_small(r#"[{"bindings": {"backspace": null}}]"#),
45294549 Default::default(),
45304550 )
45314551 .await
@@ -4545,7 +4565,7 @@ mod tests { · 13 unmodified lines
45454565 .fs
45464566 .save(
45474567 "/settings.json".as_ref(),
4548− &r#"{"base_keymap": "JetBrains"}"#.into(),
4568+ &Rope::from_str_small(r#"{"base_keymap": "JetBrains"}"#),
45494569 Default::default(),
45504570 )
45514571 .await
crates/zed/src/zed/open_listener.rs+2−2
@@ -861,7 +861,7 @@ mod tests { · 860 unmodified lines
861861 .fs
862862 .save(
863863 Path::new(file1_path),
864− &Rope::from("content1"),
864+ &Rope::from_str("content1", cx.background_executor()),
865865 LineEnding::Unix,
866866 )
867867 .await
@@ -875,7 +875,7 @@ mod tests { · 7 unmodified lines
875875 .fs
876876 .save(
877877 Path::new(file2_path),
878− &Rope::from("content2"),
878+ &Rope::from_str("content2", cx.background_executor()),
879879 LineEnding::Unix,
880880 )
881881 .await
crates/zeta/src/zeta.rs+2−1
@@ -1836,12 +1836,13 @@ mod tests { · 1835 unmodified lines
18361836 let fs = project::FakeFs::new(cx.executor());
18371837 let project = Project::test(fs.clone(), [], cx).await;
18381838
1839− let buffer = cx.new(|_cx| {
1839+ let buffer = cx.new(|cx| {
18401840 Buffer::remote(
18411841 language::BufferId::new(1).unwrap(),
18421842 ReplicaId::new(1),
18431843 language::Capability::ReadWrite,
18441844 "fn main() {\n println!(\"Hello\");\n}",
1845+ cx.background_executor(),
18451846 )
18461847 });
18471848