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
12711source = "registry+https://github.com/rust-lang/crates.io-index"12711source = "registry+https://github.com/rust-lang/crates.io-index"
12712checksum = "5da3b0203fd7ee5720aa0b5e790b591aa5d3f41c3ed2c34a3a393382198af2f7"12712checksum = "5da3b0203fd7ee5720aa0b5e790b591aa5d3f41c3ed2c34a3a393382198af2f7"
12713 12713
12714[[package]]
12715name = "pollster"
12716version = "0.4.0"
12717source = "registry+https://github.com/rust-lang/crates.io-index"
12718checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3"
12719
12714[[package]]12720[[package]]
12715name = "portable-atomic"12721name = "portable-atomic"
12716version = "1.11.1"12722version = "1.11.1"
@@ -12759,7 +12765,7 @@ dependencies = [ · 42 unmodified lines
12759 "log",12765 "log",
12760 "parking_lot",12766 "parking_lot",
12761 "pin-project",12767 "pin-project",
12762 "pollster",12768 "pollster 0.2.5",
12763 "static_assertions",12769 "static_assertions",
12764 "thiserror 1.0.69",12770 "thiserror 1.0.69",
12765]12771]
@@ -14311,7 +14317,6 @@ dependencies = [ · 1545 unmodified lines
14311 "gpui",14317 "gpui",
14312 "log",14318 "log",
14313 "rand 0.9.2",14319 "rand 0.9.2",
14314 "rayon",
14315 "sum_tree",14320 "sum_tree",
14316 "unicode-segmentation",14321 "unicode-segmentation",
14317 "util",14322 "util",
@@ -16237,6 +16242,7 @@ checksum = "2b2231b7c3057d5e4ad0156fb3dc807d900806020c5ffa3ee6ff2c8c76fb8520" · 1919 unmodified lines
16237name = "streaming_diff"16242name = "streaming_diff"
16238version = "0.1.0"16243version = "0.1.0"
16239dependencies = [16244dependencies = [
16245 "gpui",
16240 "ordered-float 2.10.1",16246 "ordered-float 2.10.1",
16241 "rand 0.9.2",16247 "rand 0.9.2",
16242 "rope",16248 "rope",
@@ -16355,9 +16361,11 @@ version = "0.1.0" · 112 unmodified lines
16355dependencies = [16361dependencies = [
16356 "arrayvec",16362 "arrayvec",
16357 "ctor",16363 "ctor",
16364 "futures 0.3.31",
16365 "itertools 0.14.0",
16358 "log",16366 "log",
16367 "pollster 0.4.0",
16359 "rand 0.9.2",16368 "rand 0.9.2",
16360 "rayon",
16361 "zlog",16369 "zlog",
16362]16370]
16363 16371
crates/acp_thread/src/diff.rs+3−1
@@ -361,10 +361,12 @@ async fn build_buffer_diff( · 360 unmodified lines
361) -> Result<Entity<BufferDiff>> {361) -> Result<Entity<BufferDiff>> {
362 let buffer = cx.update(|cx| buffer.read(cx).snapshot())?;362 let buffer = cx.update(|cx| buffer.read(cx).snapshot())?;
363 363
364 let executor = cx.background_executor().clone();
364 let old_text_rope = cx365 let old_text_rope = cx
365 .background_spawn({366 .background_spawn({
366 let old_text = old_text.clone();367 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) }
368 })370 })
369 .await;371 .await;
370 let base_buffer = cx372 let base_buffer = cx
crates/action_log/src/action_log.rs+15−3
@@ -3,7 +3,9 @@ use buffer_diff::BufferDiff; · 2 unmodified lines
3use clock;3use clock;
4use collections::BTreeMap;4use collections::BTreeMap;
5use futures::{FutureExt, StreamExt, channel::mpsc};5use futures::{FutureExt, StreamExt, channel::mpsc};
6use gpui::{App, AppContext, AsyncApp, Context, Entity, Subscription, Task, WeakEntity};6use gpui::{
7 App, AppContext, AsyncApp, BackgroundExecutor, Context, Entity, Subscription, Task, WeakEntity,
8};
7use language::{Anchor, Buffer, BufferEvent, DiskState, Point, ToPoint};9use language::{Anchor, Buffer, BufferEvent, DiskState, Point, ToPoint};
8use project::{Project, ProjectItem, lsp_store::OpenLspBufferHandle};10use project::{Project, ProjectItem, lsp_store::OpenLspBufferHandle};
9use std::{cmp, ops::Range, sync::Arc};11use std::{cmp, ops::Range, sync::Arc};
@@ -321,6 +323,7 @@ impl ActionLog { · 311 unmodified lines
321 let unreviewed_edits = tracked_buffer.unreviewed_edits.clone();323 let unreviewed_edits = tracked_buffer.unreviewed_edits.clone();
322 let edits = diff_snapshots(&old_snapshot, &new_snapshot);324 let edits = diff_snapshots(&old_snapshot, &new_snapshot);
323 let mut has_user_changes = false;325 let mut has_user_changes = false;
326 let executor = cx.background_executor().clone();
324 async move {327 async move {
325 if let ChangeAuthor::User = author {328 if let ChangeAuthor::User = author {
326 has_user_changes = apply_non_conflicting_edits(329 has_user_changes = apply_non_conflicting_edits(
@@ -328,6 +331,7 @@ impl ActionLog { · 1 unmodified line
328 edits,331 edits,
329 &mut base_text,332 &mut base_text,
330 new_snapshot.as_rope(),333 new_snapshot.as_rope(),
334 &executor,
331 );335 );
332 }336 }
333 337
@@ -382,6 +386,7 @@ impl ActionLog { · 48 unmodified lines
382 let agent_diff_base = tracked_buffer.diff_base.clone();386 let agent_diff_base = tracked_buffer.diff_base.clone();
383 let git_diff_base = git_diff.read(cx).base_text().as_rope().clone();387 let git_diff_base = git_diff.read(cx).base_text().as_rope().clone();
384 let buffer_text = tracked_buffer.snapshot.as_rope().clone();388 let buffer_text = tracked_buffer.snapshot.as_rope().clone();
389 let executor = cx.background_executor().clone();
385 anyhow::Ok(cx.background_spawn(async move {390 anyhow::Ok(cx.background_spawn(async move {
386 let mut old_unreviewed_edits = old_unreviewed_edits.into_iter().peekable();391 let mut old_unreviewed_edits = old_unreviewed_edits.into_iter().peekable();
387 let committed_edits = language::line_diff(392 let committed_edits = language::line_diff(
@@ -416,8 +421,11 @@ impl ActionLog { · 28 unmodified lines
416 ),421 ),
417 new_agent_diff_base.max_point(),422 new_agent_diff_base.max_point(),
418 ));423 ));
419 new_agent_diff_base424 new_agent_diff_base.replace(
420 .replace(old_byte_start..old_byte_end, &unreviewed_new);425 old_byte_start..old_byte_end,
426 &unreviewed_new,
427 &executor,
428 );
421 row_delta +=429 row_delta +=
422 unreviewed.new_len() as i32 - unreviewed.old_len() as i32;430 unreviewed.new_len() as i32 - unreviewed.old_len() as i32;
423 }431 }
@@ -611,6 +619,7 @@ impl ActionLog { · 187 unmodified lines
611 .snapshot619 .snapshot
612 .text_for_range(new_range)620 .text_for_range(new_range)
613 .collect::<String>(),621 .collect::<String>(),
622 cx.background_executor(),
614 );623 );
615 delta += edit.new_len() as i32 - edit.old_len() as i32;624 delta += edit.new_len() as i32 - edit.old_len() as i32;
616 false625 false
@@ -824,6 +833,7 @@ fn apply_non_conflicting_edits( · 207 unmodified lines
824 edits: Vec<Edit<u32>>,833 edits: Vec<Edit<u32>>,
825 old_text: &mut Rope,834 old_text: &mut Rope,
826 new_text: &Rope,835 new_text: &Rope,
836 executor: &BackgroundExecutor,
827) -> bool {837) -> bool {
828 let mut old_edits = patch.edits().iter().cloned().peekable();838 let mut old_edits = patch.edits().iter().cloned().peekable();
829 let mut new_edits = edits.into_iter().peekable();839 let mut new_edits = edits.into_iter().peekable();
@@ -877,6 +887,7 @@ fn apply_non_conflicting_edits( · 47 unmodified lines
877 old_text.replace(887 old_text.replace(
878 old_bytes,888 old_bytes,
879 &new_text.chunks_in_range(new_bytes).collect::<String>(),889 &new_text.chunks_in_range(new_bytes).collect::<String>(),
890 executor,
880 );891 );
881 applied_delta += new_edit.new_len() as i32 - new_edit.old_len() as i32;892 applied_delta += new_edit.new_len() as i32 - new_edit.old_len() as i32;
882 has_made_changes = true;893 has_made_changes = true;
@@ -2282,6 +2293,7 @@ mod tests { · 1399 unmodified lines
2282 old_text.replace(2293 old_text.replace(
2283 old_start..old_end,2294 old_start..old_end,
2284 &new_text.slice_rows(edit.new.clone()).to_string(),2295 &new_text.slice_rows(edit.new.clone()).to_string(),
2296 cx.background_executor(),
2285 );2297 );
2286 }2298 }
2287 pretty_assertions::assert_eq!(old_text.to_string(), new_text.to_string());2299 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
305#[cfg(test)]305#[cfg(test)]
306mod tests {306mod tests {
307 use super::*;307 use super::*;
308 use gpui::TestAppContext;
308 use indoc::indoc;309 use indoc::indoc;
309 use language::{BufferId, TextBuffer};310 use language::{BufferId, TextBuffer};
310 use rand::prelude::*;311 use rand::prelude::*;
311 use text::ReplicaId;312 use text::ReplicaId;
312 use util::test::{generate_marked_text, marked_text_ranges};313 use util::test::{generate_marked_text, marked_text_ranges};
313 314
314 #[test]315 #[gpui::test]
315 fn test_empty_query() {316 fn test_empty_query(cx: &mut gpui::TestAppContext) {
316 let buffer = TextBuffer::new(317 let buffer = TextBuffer::new(
317 ReplicaId::LOCAL,318 ReplicaId::LOCAL,
318 BufferId::new(1).unwrap(),319 BufferId::new(1).unwrap(),
319 "Hello world\nThis is a test\nFoo bar baz",320 "Hello world\nThis is a test\nFoo bar baz",
321 cx.background_executor(),
320 );322 );
321 let snapshot = buffer.snapshot();323 let snapshot = buffer.snapshot();
322 324
@@ -325,12 +327,13 @@ mod tests { · 2 unmodified lines
325 assert_eq!(finish(finder), None);327 assert_eq!(finish(finder), None);
326 }328 }
327 329
328 #[test]330 #[gpui::test]
329 fn test_streaming_exact_match() {331 fn test_streaming_exact_match(cx: &mut gpui::TestAppContext) {
330 let buffer = TextBuffer::new(332 let buffer = TextBuffer::new(
331 ReplicaId::LOCAL,333 ReplicaId::LOCAL,
332 BufferId::new(1).unwrap(),334 BufferId::new(1).unwrap(),
333 "Hello world\nThis is a test\nFoo bar baz",335 "Hello world\nThis is a test\nFoo bar baz",
336 cx.background_executor(),
334 );337 );
335 let snapshot = buffer.snapshot();338 let snapshot = buffer.snapshot();
336 339
@@ -349,8 +352,8 @@ mod tests { · 12 unmodified lines
349 assert_eq!(finish(finder), Some("This is a test".to_string()));352 assert_eq!(finish(finder), Some("This is a test".to_string()));
350 }353 }
351 354
352 #[test]355 #[gpui::test]
353 fn test_streaming_fuzzy_match() {356 fn test_streaming_fuzzy_match(cx: &mut gpui::TestAppContext) {
354 let buffer = TextBuffer::new(357 let buffer = TextBuffer::new(
355 ReplicaId::LOCAL,358 ReplicaId::LOCAL,
356 BufferId::new(1).unwrap(),359 BufferId::new(1).unwrap(),
@@ -363,6 +366,7 @@ mod tests { · 6 unmodified lines
363 return x * y;366 return x * y;
364 }367 }
365 "},368 "},
369 cx.background_executor(),
366 );370 );
367 let snapshot = buffer.snapshot();371 let snapshot = buffer.snapshot();
368 372
@@ -383,12 +387,13 @@ mod tests { · 14 unmodified lines
383 );387 );
384 }388 }
385 389
386 #[test]390 #[gpui::test]
387 fn test_incremental_improvement() {391 fn test_incremental_improvement(cx: &mut gpui::TestAppContext) {
388 let buffer = TextBuffer::new(392 let buffer = TextBuffer::new(
389 ReplicaId::LOCAL,393 ReplicaId::LOCAL,
390 BufferId::new(1).unwrap(),394 BufferId::new(1).unwrap(),
391 "Line 1\nLine 2\nLine 3\nLine 4\nLine 5",395 "Line 1\nLine 2\nLine 3\nLine 4\nLine 5",
396 cx.background_executor(),
392 );397 );
393 let snapshot = buffer.snapshot();398 let snapshot = buffer.snapshot();
394 399
@@ -408,8 +413,8 @@ mod tests { · 13 unmodified lines
408 assert_eq!(finish(finder), Some("Line 3\nLine 4".to_string()));413 assert_eq!(finish(finder), Some("Line 3\nLine 4".to_string()));
409 }414 }
410 415
411 #[test]416 #[gpui::test]
412 fn test_incomplete_lines_buffering() {417 fn test_incomplete_lines_buffering(cx: &mut gpui::TestAppContext) {
413 let buffer = TextBuffer::new(418 let buffer = TextBuffer::new(
414 ReplicaId::LOCAL,419 ReplicaId::LOCAL,
415 BufferId::new(1).unwrap(),420 BufferId::new(1).unwrap(),
@@ -418,6 +423,7 @@ mod tests { · 2 unmodified lines
418 jumps over the lazy dog423 jumps over the lazy dog
419 Pack my box with five dozen liquor jugs424 Pack my box with five dozen liquor jugs
420 "},425 "},
426 cx.background_executor(),
421 );427 );
422 let snapshot = buffer.snapshot();428 let snapshot = buffer.snapshot();
423 429
@@ -435,8 +441,8 @@ mod tests { · 11 unmodified lines
435 );441 );
436 }442 }
437 443
438 #[test]444 #[gpui::test]
439 fn test_multiline_fuzzy_match() {445 fn test_multiline_fuzzy_match(cx: &mut gpui::TestAppContext) {
440 let buffer = TextBuffer::new(446 let buffer = TextBuffer::new(
441 ReplicaId::LOCAL,447 ReplicaId::LOCAL,
442 BufferId::new(1).unwrap(),448 BufferId::new(1).unwrap(),
@@ -456,6 +462,7 @@ mod tests { · 13 unmodified lines
456 }462 }
457 }463 }
458 "#},464 "#},
465 cx.background_executor(),
459 );466 );
460 let snapshot = buffer.snapshot();467 let snapshot = buffer.snapshot();
461 468
@@ -509,7 +516,7 @@ mod tests { · 47 unmodified lines
509 }516 }
510 517
511 #[gpui::test(iterations = 100)]518 #[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) {
513 assert_location_resolution(520 assert_location_resolution(
514 concat!(521 concat!(
515 " Lorem\n",522 " Lorem\n",
@@ -519,11 +526,12 @@ mod tests { · 3 unmodified lines
519 ),526 ),
520 "ipsum",527 "ipsum",
521 &mut rng,528 &mut rng,
529 cx,
522 );530 );
523 }531 }
524 532
525 #[gpui::test(iterations = 100)]533 #[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) {
527 assert_location_resolution(535 assert_location_resolution(
528 concat!(536 concat!(
529 " Lorem\n",537 " Lorem\n",
@@ -533,11 +541,12 @@ mod tests { · 3 unmodified lines
533 ),541 ),
534 "ipsum\ndolor sit amet",542 "ipsum\ndolor sit amet",
535 &mut rng,543 &mut rng,
544 cx,
536 );545 );
537 }546 }
538 547
539 #[gpui::test(iterations = 100)]548 #[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) {
541 assert_location_resolution(550 assert_location_resolution(
542 indoc! {"551 indoc! {"
543 «fn foo1(a: usize) -> usize {552 «fn foo1(a: usize) -> usize {
@@ -550,11 +559,12 @@ mod tests { · 6 unmodified lines
550 "},559 "},
551 "fn foo1(a: usize) -> u32 {\n40\n}",560 "fn foo1(a: usize) -> u32 {\n40\n}",
552 &mut rng,561 &mut rng,
562 cx,
553 );563 );
554 }564 }
555 565
556 #[gpui::test(iterations = 100)]566 #[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) {
558 assert_location_resolution(568 assert_location_resolution(
559 indoc! {"569 indoc! {"
560 class Something {570 class Something {
@@ -575,11 +585,12 @@ mod tests { · 14 unmodified lines
575 six() { return 6666; }585 six() { return 6666; }
576 "},586 "},
577 &mut rng,587 &mut rng,
588 cx,
578 );589 );
579 }590 }
580 591
581 #[gpui::test(iterations = 100)]592 #[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) {
583 assert_location_resolution(594 assert_location_resolution(
584 indoc! {"595 indoc! {"
585 use std::ops::Range;596 use std::ops::Range;
@@ -609,11 +620,12 @@ mod tests { · 23 unmodified lines
609 use std::sync::Arc;620 use std::sync::Arc;
610 "},621 "},
611 &mut rng,622 &mut rng,
623 cx,
612 );624 );
613 }625 }
614 626
615 #[gpui::test(iterations = 100)]627 #[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) {
617 assert_location_resolution(629 assert_location_resolution(
618 indoc! {"630 indoc! {"
619 impl Foo {631 impl Foo {
@@ -641,11 +653,12 @@ mod tests { · 21 unmodified lines
641 " });",653 " });",
642 ),654 ),
643 &mut rng,655 &mut rng,
656 cx,
644 );657 );
645 }658 }
646 659
647 #[gpui::test(iterations = 100)]660 #[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) {
649 assert_location_resolution(662 assert_location_resolution(
650 indoc! {r#"663 indoc! {r#"
651 let tool = cx664 let tool = cx
@@ -673,11 +686,12 @@ mod tests { · 21 unmodified lines
673 " .output;",686 " .output;",
674 ),687 ),
675 &mut rng,688 &mut rng,
689 cx,
676 );690 );
677 }691 }
678 692
679 #[gpui::test]693 #[gpui::test]
680 fn test_line_hint_selection() {694 fn test_line_hint_selection(cx: &mut TestAppContext) {
681 let text = indoc! {r#"695 let text = indoc! {r#"
682 fn first_function() {696 fn first_function() {
683 return 42;697 return 42;
@@ -696,6 +710,7 @@ mod tests { · 12 unmodified lines
696 ReplicaId::LOCAL,710 ReplicaId::LOCAL,
697 BufferId::new(1).unwrap(),711 BufferId::new(1).unwrap(),
698 text.to_string(),712 text.to_string(),
713 cx.background_executor(),
699 );714 );
700 let snapshot = buffer.snapshot();715 let snapshot = buffer.snapshot();
701 let mut matcher = StreamingFuzzyMatcher::new(snapshot.clone());716 let mut matcher = StreamingFuzzyMatcher::new(snapshot.clone());
@@ -727,9 +742,19 @@ mod tests { · 25 unmodified lines
727 }742 }
728 743
729 #[track_caller]744 #[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 ) {
731 let (text, expected_ranges) = marked_text_ranges(text_with_expected_range, false);751 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 );
733 let snapshot = buffer.snapshot();758 let snapshot = buffer.snapshot();
734 759
735 let mut matcher = StreamingFuzzyMatcher::new(snapshot);760 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
569 use prompt_store::ProjectContext;569 use prompt_store::ProjectContext;
570 use serde_json::json;570 use serde_json::json;
571 use settings::SettingsStore;571 use settings::SettingsStore;
572 use text::Rope;
572 use util::{path, rel_path::rel_path};573 use util::{path, rel_path::rel_path};
573 574
574 #[gpui::test]575 #[gpui::test]
@@ -741,7 +742,7 @@ mod tests { · 166 unmodified lines
741 // Create the file742 // Create the file
742 fs.save(743 fs.save(
743 path!("/root/src/main.rs").as_ref(),744 path!("/root/src/main.rs").as_ref(),
744 &"initial content".into(),745 &Rope::from_str_small("initial content"),
745 language::LineEnding::Unix,746 language::LineEnding::Unix,
746 )747 )
747 .await748 .await
@@ -908,7 +909,7 @@ mod tests { · 160 unmodified lines
908 // Create a simple file with trailing whitespace909 // Create a simple file with trailing whitespace
909 fs.save(910 fs.save(
910 path!("/root/src/main.rs").as_ref(),911 path!("/root/src/main.rs").as_ref(),
911 &"initial content".into(),912 &Rope::from_str_small("initial content"),
912 language::LineEnding::Unix,913 language::LineEnding::Unix,
913 )914 )
914 .await915 .await
crates/agent_ui/src/agent_configuration.rs+6−2
@@ -28,6 +28,7 @@ use project::{ · 27 unmodified lines
28 agent_server_store::{AgentServerStore, CLAUDE_CODE_NAME, CODEX_NAME, GEMINI_NAME},28 agent_server_store::{AgentServerStore, CLAUDE_CODE_NAME, CODEX_NAME, GEMINI_NAME},
29 context_server_store::{ContextServerConfiguration, ContextServerStatus, ContextServerStore},29 context_server_store::{ContextServerConfiguration, ContextServerStatus, ContextServerStore},
30};30};
31use rope::Rope;
31use settings::{SettingsStore, update_settings_file};32use settings::{SettingsStore, update_settings_file};
32use ui::{33use ui::{
33 Chip, CommonAnimationExt, ContextMenu, Disclosure, Divider, DividerColor, ElevationIndex,34 Chip, CommonAnimationExt, ContextMenu, Disclosure, Divider, DividerColor, ElevationIndex,
@@ -1114,8 +1115,11 @@ async fn open_new_agent_servers_entry_in_settings_editor( · 1080 unmodified lines
1114) -> Result<()> {1115) -> Result<()> {
1115 let settings_editor = workspace1116 let settings_editor = workspace
1116 .update_in(cx, |_, window, cx| {1117 .update_in(cx, |_, window, cx| {
1117 create_and_open_local_file(paths::settings_file(), window, cx, || {1118 create_and_open_local_file(paths::settings_file(), window, cx, |cx| {
1118 settings::initial_user_settings_content().as_ref().into()1119 Rope::from_str(
1120 &settings::initial_user_settings_content(),
1121 cx.background_executor(),
1122 )
1119 })1123 })
1120 })?1124 })?
1121 .await?1125 .await?
crates/agent_ui/src/buffer_codegen.rs+4−3
@@ -487,9 +487,10 @@ impl CodegenAlternative { · 486 unmodified lines
487 ) {487 ) {
488 let start_time = Instant::now();488 let start_time = Instant::now();
489 let snapshot = self.snapshot.clone();489 let snapshot = self.snapshot.clone();
490 let selected_text = snapshot490 let selected_text = Rope::from_iter(
491 .text_for_range(self.range.start..self.range.end)491 snapshot.text_for_range(self.range.start..self.range.end),
492 .collect::<Rope>();492 cx.background_executor(),
493 );
493 494
494 let selection_start = self.range.start.to_point(&snapshot);495 let selection_start = self.range.start.to_point(&snapshot);
495 496
crates/assistant_text_thread/src/text_thread.rs+2−1
@@ -744,12 +744,13 @@ impl TextThread { · 743 unmodified lines
744 telemetry: Option<Arc<Telemetry>>,744 telemetry: Option<Arc<Telemetry>>,
745 cx: &mut Context<Self>,745 cx: &mut Context<Self>,
746 ) -> Self {746 ) -> Self {
747 let buffer = cx.new(|_cx| {747 let buffer = cx.new(|cx| {
748 let buffer = Buffer::remote(748 let buffer = Buffer::remote(
749 language::BufferId::new(1).unwrap(),749 language::BufferId::new(1).unwrap(),
750 replica_id,750 replica_id,
751 capability,751 capability,
752 "",752 "",
753 cx.background_executor(),
753 );754 );
754 buffer.set_language_registry(language_registry.clone());755 buffer.set_language_registry(language_registry.clone());
755 buffer756 buffer
crates/buffer_diff/src/buffer_diff.rs+47−11
@@ -1,6 +1,9 @@
1use futures::channel::oneshot;1use futures::channel::oneshot;
2use git2::{DiffLineType as GitDiffLineType, DiffOptions as GitOptions, Patch as GitPatch};2use git2::{DiffLineType as GitDiffLineType, DiffOptions as GitOptions, Patch as GitPatch};
3use gpui::{App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, Task, TaskLabel};3use gpui::{
4 App, AppContext as _, AsyncApp, BackgroundExecutor, Context, Entity, EventEmitter, Task,
5 TaskLabel,
6};
4use language::{Language, LanguageRegistry};7use language::{Language, LanguageRegistry};
5use rope::Rope;8use rope::Rope;
6use std::{9use std::{
@@ -191,7 +194,7 @@ impl BufferDiffSnapshot { · 184 unmodified lines
191 let base_text_exists;194 let base_text_exists;
192 let base_text_snapshot;195 let base_text_snapshot;
193 if let Some(text) = &base_text {196 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());
195 base_text_pair = Some((text.clone(), base_text_rope.clone()));198 base_text_pair = Some((text.clone(), base_text_rope.clone()));
196 let snapshot =199 let snapshot =
197 language::Buffer::build_snapshot(base_text_rope, language, language_registry, cx);200 language::Buffer::build_snapshot(base_text_rope, language, language_registry, cx);
@@ -311,6 +314,7 @@ impl BufferDiffInner { · 113 unmodified lines
311 hunks: &[DiffHunk],314 hunks: &[DiffHunk],
312 buffer: &text::BufferSnapshot,315 buffer: &text::BufferSnapshot,
313 file_exists: bool,316 file_exists: bool,
317 cx: &BackgroundExecutor,
314 ) -> Option<Rope> {318 ) -> Option<Rope> {
315 let head_text = self319 let head_text = self
316 .base_text_exists320 .base_text_exists
@@ -505,7 +509,7 @@ impl BufferDiffInner { · 188 unmodified lines
505 for (old_range, replacement_text) in edits {509 for (old_range, replacement_text) in edits {
506 new_index_text.append(index_cursor.slice(old_range.start));510 new_index_text.append(index_cursor.slice(old_range.start));
507 index_cursor.seek_forward(old_range.end);511 index_cursor.seek_forward(old_range.end);
508 new_index_text.push(&replacement_text);512 new_index_text.push(&replacement_text, cx);
509 }513 }
510 new_index_text.append(index_cursor.suffix());514 new_index_text.append(index_cursor.suffix());
511 Some(new_index_text)515 Some(new_index_text)
@@ -962,6 +966,7 @@ impl BufferDiff { · 450 unmodified lines
962 hunks,966 hunks,
963 buffer,967 buffer,
964 file_exists,968 file_exists,
969 cx.background_executor(),
965 );970 );
966 971
967 cx.emit(BufferDiffEvent::HunksStagedOrUnstaged(972 cx.emit(BufferDiffEvent::HunksStagedOrUnstaged(
@@ -1385,7 +1390,12 @@ mod tests { · 417 unmodified lines
1385 "1390 "
1386 .unindent();1391 .unindent();
1387 1392
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 );
1389 let mut diff = BufferDiffSnapshot::new_sync(buffer.clone(), diff_base.clone(), cx);1399 let mut diff = BufferDiffSnapshot::new_sync(buffer.clone(), diff_base.clone(), cx);
1390 assert_hunks(1400 assert_hunks(
1391 diff.hunks_intersecting_range(Anchor::MIN..Anchor::MAX, &buffer),1401 diff.hunks_intersecting_range(Anchor::MIN..Anchor::MAX, &buffer),
@@ -1394,7 +1404,7 @@ mod tests { · 2 unmodified lines
1394 &[(1..2, "two\n", "HELLO\n", DiffHunkStatus::modified_none())],1404 &[(1..2, "two\n", "HELLO\n", DiffHunkStatus::modified_none())],
1395 );1405 );
1396 1406
1397 buffer.edit([(0..0, "point five\n")]);1407 buffer.edit([(0..0, "point five\n")], cx.background_executor());
1398 diff = BufferDiffSnapshot::new_sync(buffer.clone(), diff_base.clone(), cx);1408 diff = BufferDiffSnapshot::new_sync(buffer.clone(), diff_base.clone(), cx);
1399 assert_hunks(1409 assert_hunks(
1400 diff.hunks_intersecting_range(Anchor::MIN..Anchor::MAX, &buffer),1410 diff.hunks_intersecting_range(Anchor::MIN..Anchor::MAX, &buffer),
@@ -1459,7 +1469,12 @@ mod tests { · 58 unmodified lines
1459 "1469 "
1460 .unindent();1470 .unindent();
1461 1471
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 );
1463 let unstaged_diff = BufferDiffSnapshot::new_sync(buffer.clone(), index_text, cx);1478 let unstaged_diff = BufferDiffSnapshot::new_sync(buffer.clone(), index_text, cx);
1464 let mut uncommitted_diff =1479 let mut uncommitted_diff =
1465 BufferDiffSnapshot::new_sync(buffer.clone(), head_text.clone(), cx);1480 BufferDiffSnapshot::new_sync(buffer.clone(), head_text.clone(), cx);
@@ -1528,7 +1543,12 @@ mod tests { · 62 unmodified lines
1528 "1543 "
1529 .unindent();1544 .unindent();
1530 1545
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 );
1532 let diff = cx1552 let diff = cx
1533 .update(|cx| {1553 .update(|cx| {
1534 BufferDiffSnapshot::new_with_base_text(1554 BufferDiffSnapshot::new_with_base_text(
@@ -1791,7 +1811,12 @@ mod tests { · 256 unmodified lines
1791 1811
1792 for example in table {1812 for example in table {
1793 let (buffer_text, ranges) = marked_text_ranges(&example.buffer_marked_text, false);1813 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 );
1795 let hunk_range =1820 let hunk_range =
1796 buffer.anchor_before(ranges[0].start)..buffer.anchor_before(ranges[0].end);1821 buffer.anchor_before(ranges[0].start)..buffer.anchor_before(ranges[0].end);
1797 1822
@@ -1868,6 +1893,7 @@ mod tests { · 70 unmodified lines
1868 ReplicaId::LOCAL,1893 ReplicaId::LOCAL,
1869 BufferId::new(1).unwrap(),1894 BufferId::new(1).unwrap(),
1870 buffer_text.clone(),1895 buffer_text.clone(),
1896 cx.background_executor(),
1871 );1897 );
1872 let unstaged = BufferDiffSnapshot::new_sync(buffer.clone(), index_text, cx);1898 let unstaged = BufferDiffSnapshot::new_sync(buffer.clone(), index_text, cx);
1873 let uncommitted = BufferDiffSnapshot::new_sync(buffer.clone(), head_text.clone(), cx);1899 let uncommitted = BufferDiffSnapshot::new_sync(buffer.clone(), head_text.clone(), cx);
@@ -1941,7 +1967,12 @@ mod tests { · 67 unmodified lines
1941 "1967 "
1942 .unindent();1968 .unindent();
1943 1969
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 );
1945 1976
1946 let empty_diff = cx.update(|cx| BufferDiffSnapshot::empty(&buffer, cx));1977 let empty_diff = cx.update(|cx| BufferDiffSnapshot::empty(&buffer, cx));
1947 let diff_1 = BufferDiffSnapshot::new_sync(buffer.clone(), base_text.clone(), cx);1978 let diff_1 = BufferDiffSnapshot::new_sync(buffer.clone(), base_text.clone(), cx);
@@ -1961,6 +1992,7 @@ mod tests { · 13 unmodified lines
1961 NINE1992 NINE
1962 "1993 "
1963 .unindent(),1994 .unindent(),
1995 cx.background_executor(),
1964 );1996 );
1965 let diff_2 = BufferDiffSnapshot::new_sync(buffer.clone(), base_text.clone(), cx);1997 let diff_2 = BufferDiffSnapshot::new_sync(buffer.clone(), base_text.clone(), cx);
1966 assert_eq!(None, diff_2.inner.compare(&diff_1.inner, &buffer));1998 assert_eq!(None, diff_2.inner.compare(&diff_1.inner, &buffer));
@@ -1978,6 +2010,7 @@ mod tests { · 11 unmodified lines
1978 NINE2010 NINE
1979 "2011 "
1980 .unindent(),2012 .unindent(),
2013 cx.background_executor(),
1981 );2014 );
1982 let diff_3 = BufferDiffSnapshot::new_sync(buffer.clone(), base_text.clone(), cx);2015 let diff_3 = BufferDiffSnapshot::new_sync(buffer.clone(), base_text.clone(), cx);
1983 let range = diff_3.inner.compare(&diff_2.inner, &buffer).unwrap();2016 let range = diff_3.inner.compare(&diff_2.inner, &buffer).unwrap();
@@ -1995,6 +2028,7 @@ mod tests { · 11 unmodified lines
1995 NINE2028 NINE
1996 "2029 "
1997 .unindent(),2030 .unindent(),
2031 cx.background_executor(),
1998 );2032 );
1999 let diff_4 = BufferDiffSnapshot::new_sync(buffer.clone(), base_text.clone(), cx);2033 let diff_4 = BufferDiffSnapshot::new_sync(buffer.clone(), base_text.clone(), cx);
2000 let range = diff_4.inner.compare(&diff_3.inner, &buffer).unwrap();2034 let range = diff_4.inner.compare(&diff_3.inner, &buffer).unwrap();
@@ -2013,6 +2047,7 @@ mod tests { · 12 unmodified lines
2013 NINE2047 NINE
2014 "2048 "
2015 .unindent(),2049 .unindent(),
2050 cx.background_executor(),
2016 );2051 );
2017 let diff_5 = BufferDiffSnapshot::new_sync(buffer.snapshot(), base_text.clone(), cx);2052 let diff_5 = BufferDiffSnapshot::new_sync(buffer.snapshot(), base_text.clone(), cx);
2018 let range = diff_5.inner.compare(&diff_4.inner, &buffer).unwrap();2053 let range = diff_5.inner.compare(&diff_4.inner, &buffer).unwrap();
@@ -2031,6 +2066,7 @@ mod tests { · 12 unmodified lines
2031 «nine»2066 «nine»
2032 "2067 "
2033 .unindent(),2068 .unindent(),
2069 cx.background_executor(),
2034 );2070 );
2035 let diff_6 = BufferDiffSnapshot::new_sync(buffer.snapshot(), base_text, cx);2071 let diff_6 = BufferDiffSnapshot::new_sync(buffer.snapshot(), base_text, cx);
2036 let range = diff_6.inner.compare(&diff_5.inner, &buffer).unwrap();2072 let range = diff_6.inner.compare(&diff_5.inner, &buffer).unwrap();
@@ -2140,14 +2176,14 @@ mod tests { · 103 unmodified lines
2140 let working_copy = gen_working_copy(rng, &head_text);2176 let working_copy = gen_working_copy(rng, &head_text);
2141 let working_copy = cx.new(|cx| {2177 let working_copy = cx.new(|cx| {
2142 language::Buffer::local_normalized(2178 language::Buffer::local_normalized(
2143 Rope::from(working_copy.as_str()),2179 Rope::from_str(working_copy.as_str(), cx.background_executor()),
2144 text::LineEnding::default(),2180 text::LineEnding::default(),
2145 cx,2181 cx,
2146 )2182 )
2147 });2183 });
2148 let working_copy = working_copy.read_with(cx, |working_copy, _| working_copy.snapshot());2184 let working_copy = working_copy.read_with(cx, |working_copy, _| working_copy.snapshot());
2149 let mut index_text = if rng.random() {2185 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())
2151 } else {2187 } else {
2152 working_copy.as_rope().clone()2188 working_copy.as_rope().clone()
2153 };2189 };
crates/channel/src/channel_buffer.rs+1−0
@@ -70,6 +70,7 @@ impl ChannelBuffer { · 69 unmodified lines
70 ReplicaId::new(response.replica_id as u16),70 ReplicaId::new(response.replica_id as u16),
71 capability,71 capability,
72 base_text,72 base_text,
73 cx.background_executor(),
73 )74 )
74 })?;75 })?;
75 buffer.update(cx, |buffer, cx| buffer.apply_ops(operations, cx))?;76 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
701 return Ok(());701 return Ok(());
702 }702 }
703 703
704 let mut text_buffer = text::Buffer::new(704 let mut text_buffer = text::Buffer::new_slow(
705 clock::ReplicaId::LOCAL,705 clock::ReplicaId::LOCAL,
706 text::BufferId::new(1).unwrap(),706 text::BufferId::new(1).unwrap(),
707 base_text,707 base_text,
708 );708 );
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);
710 710
711 let base_text = text_buffer.text();711 let base_text = text_buffer.text();
712 let epoch = buffer.epoch + 1;712 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
74 ReplicaId::new(0),74 ReplicaId::new(0),
75 text::BufferId::new(1).unwrap(),75 text::BufferId::new(1).unwrap(),
76 "".to_string(),76 "".to_string(),
77 &db.test_options.as_ref().unwrap().executor,
77 );78 );
78 let operations = vec![79 let operations = vec![
79 buffer_a.edit([(0..0, "hello world")]),80 buffer_a.edit(
80 buffer_a.edit([(5..5, ", cruel")]),81 [(0..0, "hello world")],
81 buffer_a.edit([(0..5, "goodbye")]),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 ),
82 buffer_a.undo().unwrap().1,92 buffer_a.undo().unwrap().1,
83 ];93 ];
84 assert_eq!(buffer_a.text(), "hello, cruel world");94 assert_eq!(buffer_a.text(), "hello, cruel world");
@@ -102,15 +112,19 @@ async fn test_channel_buffers(db: &Arc<Database>) { · 17 unmodified lines
102 ReplicaId::new(0),112 ReplicaId::new(0),
103 text::BufferId::new(1).unwrap(),113 text::BufferId::new(1).unwrap(),
104 buffer_response_b.base_text,114 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,
105 );127 );
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 }));
114 128
115 assert_eq!(buffer_b.text(), "hello, cruel world");129 assert_eq!(buffer_b.text(), "hello, cruel world");
116 130
@@ -247,6 +261,7 @@ async fn test_channel_buffers_last_operations(db: &Database) { · 130 unmodified lines
247 ReplicaId::new(res.replica_id as u16),261 ReplicaId::new(res.replica_id as u16),
248 text::BufferId::new(1).unwrap(),262 text::BufferId::new(1).unwrap(),
249 "".to_string(),263 "".to_string(),
264 &db.test_options.as_ref().unwrap().executor,
250 ));265 ));
251 }266 }
252 267
@@ -255,9 +270,9 @@ async fn test_channel_buffers_last_operations(db: &Database) { · 2 unmodified lines
255 user_id,270 user_id,
256 db,271 db,
257 vec![272 vec![
258 text_buffers[0].edit([(0..0, "a")]),273 text_buffers[0].edit([(0..0, "a")], &db.test_options.as_ref().unwrap().executor),
259 text_buffers[0].edit([(0..0, "b")]),274 text_buffers[0].edit([(0..0, "b")], &db.test_options.as_ref().unwrap().executor),
260 text_buffers[0].edit([(0..0, "c")]),275 text_buffers[0].edit([(0..0, "c")], &db.test_options.as_ref().unwrap().executor),
261 ],276 ],
262 )277 )
263 .await;278 .await;
@@ -267,9 +282,9 @@ async fn test_channel_buffers_last_operations(db: &Database) { · 3 unmodified lines
267 user_id,282 user_id,
268 db,283 db,
269 vec![284 vec![
270 text_buffers[1].edit([(0..0, "d")]),285 text_buffers[1].edit([(0..0, "d")], &db.test_options.as_ref().unwrap().executor),
271 text_buffers[1].edit([(1..1, "e")]),286 text_buffers[1].edit([(1..1, "e")], &db.test_options.as_ref().unwrap().executor),
272 text_buffers[1].edit([(2..2, "f")]),287 text_buffers[1].edit([(2..2, "f")], &db.test_options.as_ref().unwrap().executor),
273 ],288 ],
274 )289 )
275 .await;290 .await;
@@ -286,14 +301,15 @@ async fn test_channel_buffers_last_operations(db: &Database) { · 10 unmodified lines
286 replica_id,301 replica_id,
287 text::BufferId::new(1).unwrap(),302 text::BufferId::new(1).unwrap(),
288 "def".to_string(),303 "def".to_string(),
304 &db.test_options.as_ref().unwrap().executor,
289 );305 );
290 update_buffer(306 update_buffer(
291 buffers[1].channel_id,307 buffers[1].channel_id,
292 user_id,308 user_id,
293 db,309 db,
294 vec![310 vec![
295 text_buffers[1].edit([(0..0, "g")]),311 text_buffers[1].edit([(0..0, "g")], &db.test_options.as_ref().unwrap().executor),
296 text_buffers[1].edit([(0..0, "h")]),312 text_buffers[1].edit([(0..0, "h")], &db.test_options.as_ref().unwrap().executor),
297 ],313 ],
298 )314 )
299 .await;315 .await;
@@ -302,7 +318,7 @@ async fn test_channel_buffers_last_operations(db: &Database) { · 2 unmodified lines
302 buffers[2].channel_id,318 buffers[2].channel_id,
303 user_id,319 user_id,
304 db,320 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)],
306 )322 )
307 .await;323 .await;
308 324
crates/collab/src/tests/integration_tests.rs+2−2
@@ -3694,7 +3694,7 @@ async fn test_buffer_reloading( · 3693 unmodified lines
3694 assert_eq!(buf.line_ending(), LineEnding::Unix);3694 assert_eq!(buf.line_ending(), LineEnding::Unix);
3695 });3695 });
3696 3696
3697 let new_contents = Rope::from("d\ne\nf");3697 let new_contents = Rope::from_str_small("d\ne\nf");
3698 client_a3698 client_a
3699 .fs()3699 .fs()
3700 .save(3700 .save(
@@ -4479,7 +4479,7 @@ async fn test_reloading_buffer_manually( · 778 unmodified lines
4479 .fs()4479 .fs()
4480 .save(4480 .save(
4481 path!("/a/a.rs").as_ref(),4481 path!("/a/a.rs").as_ref(),
4482 &Rope::from("let seven = 7;"),4482 &Rope::from_str_small("let seven = 7;"),
4483 LineEnding::Unix,4483 LineEnding::Unix,
4484 )4484 )
4485 .await4485 .await
crates/collab/src/tests/random_project_collaboration_tests.rs+6−1
@@ -27,6 +27,7 @@ use std::{ · 26 unmodified lines
27 rc::Rc,27 rc::Rc,
28 sync::Arc,28 sync::Arc,
29};29};
30use text::Rope;
30use util::{31use util::{
31 ResultExt, path,32 ResultExt, path,
32 paths::PathStyle,33 paths::PathStyle,
@@ -938,7 +939,11 @@ impl RandomizedTest for ProjectCollaborationTest { · 905 unmodified lines
938 939
939 client940 client
940 .fs()941 .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 )
942 .await947 .await
943 .unwrap();948 .unwrap();
944 }949 }
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
877 vec![Inlay::edit_prediction(877 vec![Inlay::edit_prediction(
878 post_inc(&mut next_inlay_id),878 post_inc(&mut next_inlay_id),
879 snapshot.buffer_snapshot().anchor_before(position),879 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"]),
881 )],881 )],
882 cx,882 cx,
883 );883 );
@@ -2070,7 +2070,7 @@ fn random_lsp_diagnostic( · 1186 unmodified lines
2070 const ERROR_MARGIN: usize = 10;2070 const ERROR_MARGIN: usize = 10;
2071 2071
2072 let file_content = fs.read_file_sync(path).unwrap();2072 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());
2074 2074
2075 let start = rng.random_range(0..file_text.len().saturating_add(ERROR_MARGIN));2075 let start = rng.random_range(0..file_text.len().saturating_add(ERROR_MARGIN));
2076 let end = rng.random_range(start..file_text.len().saturating_add(ERROR_MARGIN));2076 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
13};13};
14use indoc::indoc;14use indoc::indoc;
15use language::{15use language::{
16 EditPredictionsMode, File, Language,16 EditPredictionsMode, File, Language, Rope,
17 language_settings::{self, AllLanguageSettings, EditPredictionProvider, all_language_settings},17 language_settings::{self, AllLanguageSettings, EditPredictionProvider, all_language_settings},
18};18};
19use project::DisableAiSettings;19use project::DisableAiSettings;
@@ -1056,8 +1056,11 @@ async fn open_disabled_globs_setting_in_editor( · 1036 unmodified lines
1056) -> Result<()> {1056) -> Result<()> {
1057 let settings_editor = workspace1057 let settings_editor = workspace
1058 .update_in(cx, |_, window, cx| {1058 .update_in(cx, |_, window, cx| {
1059 create_and_open_local_file(paths::settings_file(), window, cx, || {1059 create_and_open_local_file(paths::settings_file(), window, cx, |cx| {
1060 settings::initial_user_settings_content().as_ref().into()1060 Rope::from_str(
1061 settings::initial_user_settings_content().as_ref(),
1062 cx.background_executor(),
1063 )
1061 })1064 })
1062 })?1065 })?
1063 .await?1066 .await?
crates/editor/src/display_map.rs+2−1
@@ -1569,6 +1569,7 @@ pub mod tests { · 1568 unmodified lines
1569 use lsp::LanguageServerId;1569 use lsp::LanguageServerId;
1570 use project::Project;1570 use project::Project;
1571 use rand::{Rng, prelude::*};1571 use rand::{Rng, prelude::*};
1572 use rope::Rope;
1572 use settings::{SettingsContent, SettingsStore};1573 use settings::{SettingsContent, SettingsStore};
1573 use smol::stream::StreamExt;1574 use smol::stream::StreamExt;
1574 use std::{env, sync::Arc};1575 use std::{env, sync::Arc};
@@ -2074,7 +2075,7 @@ pub mod tests { · 499 unmodified lines
2074 vec![Inlay::edit_prediction(2075 vec![Inlay::edit_prediction(
2075 0,2076 0,
2076 buffer_snapshot.anchor_after(0),2077 buffer_snapshot.anchor_after(0),
2077 "\n",2078 Rope::from_str_small("\n"),
2078 )],2079 )],
2079 cx,2080 cx,
2080 );2081 );
crates/editor/src/display_map/inlay_map.rs+24−12
@@ -700,16 +700,20 @@ impl InlayMap { · 699 unmodified lines
700 .collect::<String>();700 .collect::<String>();
701 701
702 let next_inlay = if i % 2 == 0 {702 let next_inlay = if i % 2 == 0 {
703 use rope::Rope;
704
703 Inlay::mock_hint(705 Inlay::mock_hint(
704 post_inc(next_inlay_id),706 post_inc(next_inlay_id),
705 snapshot.buffer.anchor_at(position, bias),707 snapshot.buffer.anchor_at(position, bias),
706 &text,708 Rope::from_str_small(&text),
707 )709 )
708 } else {710 } else {
711 use rope::Rope;
712
709 Inlay::edit_prediction(713 Inlay::edit_prediction(
710 post_inc(next_inlay_id),714 post_inc(next_inlay_id),
711 snapshot.buffer.anchor_at(position, bias),715 snapshot.buffer.anchor_at(position, bias),
712 &text,716 Rope::from_str_small(&text),
713 )717 )
714 };718 };
715 let inlay_id = next_inlay.id;719 let inlay_id = next_inlay.id;
@@ -1301,7 +1305,7 @@ mod tests { · 585 unmodified lines
1301 vec![Inlay::mock_hint(1305 vec![Inlay::mock_hint(
1302 post_inc(&mut next_inlay_id),1306 post_inc(&mut next_inlay_id),
1303 buffer.read(cx).snapshot(cx).anchor_after(3),1307 buffer.read(cx).snapshot(cx).anchor_after(3),
1304 "|123|",1308 Rope::from_str_small("|123|"),
1305 )],1309 )],
1306 );1310 );
1307 assert_eq!(inlay_snapshot.text(), "abc|123|defghi");1311 assert_eq!(inlay_snapshot.text(), "abc|123|defghi");
@@ -1378,12 +1382,12 @@ mod tests { · 70 unmodified lines
1378 Inlay::mock_hint(1382 Inlay::mock_hint(
1379 post_inc(&mut next_inlay_id),1383 post_inc(&mut next_inlay_id),
1380 buffer.read(cx).snapshot(cx).anchor_before(3),1384 buffer.read(cx).snapshot(cx).anchor_before(3),
1381 "|123|",1385 Rope::from_str_small("|123|"),
1382 ),1386 ),
1383 Inlay::edit_prediction(1387 Inlay::edit_prediction(
1384 post_inc(&mut next_inlay_id),1388 post_inc(&mut next_inlay_id),
1385 buffer.read(cx).snapshot(cx).anchor_after(3),1389 buffer.read(cx).snapshot(cx).anchor_after(3),
1386 "|456|",1390 Rope::from_str_small("|456|"),
1387 ),1391 ),
1388 ],1392 ],
1389 );1393 );
@@ -1593,17 +1597,17 @@ mod tests { · 203 unmodified lines
1593 Inlay::mock_hint(1597 Inlay::mock_hint(
1594 post_inc(&mut next_inlay_id),1598 post_inc(&mut next_inlay_id),
1595 buffer.read(cx).snapshot(cx).anchor_before(0),1599 buffer.read(cx).snapshot(cx).anchor_before(0),
1596 "|123|\n",1600 Rope::from_str_small("|123|\n"),
1597 ),1601 ),
1598 Inlay::mock_hint(1602 Inlay::mock_hint(
1599 post_inc(&mut next_inlay_id),1603 post_inc(&mut next_inlay_id),
1600 buffer.read(cx).snapshot(cx).anchor_before(4),1604 buffer.read(cx).snapshot(cx).anchor_before(4),
1601 "|456|",1605 Rope::from_str_small("|456|"),
1602 ),1606 ),
1603 Inlay::edit_prediction(1607 Inlay::edit_prediction(
1604 post_inc(&mut next_inlay_id),1608 post_inc(&mut next_inlay_id),
1605 buffer.read(cx).snapshot(cx).anchor_before(7),1609 buffer.read(cx).snapshot(cx).anchor_before(7),
1606 "\n|567|\n",1610 Rope::from_str_small("\n|567|\n"),
1607 ),1611 ),
1608 ],1612 ],
1609 );1613 );
@@ -1677,9 +1681,14 @@ mod tests { · 67 unmodified lines
1677 (offset, inlay.clone())1681 (offset, inlay.clone())
1678 })1682 })
1679 .collect::<Vec<_>>();1683 .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());
1681 for (offset, inlay) in inlays.iter().rev() {1686 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 );
1683 }1692 }
1684 assert_eq!(inlay_snapshot.text(), expected_text.to_string());1693 assert_eq!(inlay_snapshot.text(), expected_text.to_string());
1685 1694
@@ -2067,7 +2076,7 @@ mod tests { · 381 unmodified lines
2067 let inlay = Inlay {2076 let inlay = Inlay {
2068 id: InlayId::Hint(0),2077 id: InlayId::Hint(0),
2069 position,2078 position,
2070 content: InlayContent::Text(text::Rope::from(inlay_text)),2079 content: InlayContent::Text(text::Rope::from_str(inlay_text, cx.background_executor())),
2071 };2080 };
2072 2081
2073 let (inlay_snapshot, _) = inlay_map.splice(&[], vec![inlay]);2082 let (inlay_snapshot, _) = inlay_map.splice(&[], vec![inlay]);
@@ -2181,7 +2190,10 @@ mod tests { · 107 unmodified lines
2181 let inlay = Inlay {2190 let inlay = Inlay {
2182 id: InlayId::Hint(0),2191 id: InlayId::Hint(0),
2183 position,2192 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 )),
2185 };2197 };
2186 2198
2187 let (inlay_snapshot, _) = inlay_map.splice(&[], vec![inlay]);2199 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
1042 let (mut tab_map, _) = TabMap::new(fold_snapshot, tab_size);1042 let (mut tab_map, _) = TabMap::new(fold_snapshot, tab_size);
1043 let tabs_snapshot = tab_map.set_max_expansion_column(32);1043 let tabs_snapshot = tab_map.set_max_expansion_column(32);
1044 1044
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());
1046 log::info!(1046 log::info!(
1047 "TabMap text (tab size: {}): {:?}",1047 "TabMap text (tab size: {}): {:?}",
1048 tab_size,1048 tab_size,
crates/editor/src/display_map/wrap_map.rs+5−4
@@ -863,7 +863,7 @@ impl WrapSnapshot { · 862 unmodified lines
863 }863 }
864 }864 }
865 865
866 let text = language::Rope::from(self.text().as_str());866 let text = language::Rope::from_str_small(self.text().as_str());
867 let mut input_buffer_rows = self.tab_snapshot.rows(0);867 let mut input_buffer_rows = self.tab_snapshot.rows(0);
868 let mut expected_buffer_rows = Vec::new();868 let mut expected_buffer_rows = Vec::new();
869 let mut prev_tab_row = 0;869 let mut prev_tab_row = 0;
@@ -1413,9 +1413,10 @@ mod tests { · 543 unmodified lines
1413 }1413 }
1414 }1414 }
1415 1415
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());
1417 for (snapshot, patch) in edits {1418 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());
1419 for edit in &patch {1420 for edit in &patch {
1420 let old_start = initial_text.point_to_offset(Point::new(edit.new.start, 0));1421 let old_start = initial_text.point_to_offset(Point::new(edit.new.start, 0));
1421 let old_end = initial_text.point_to_offset(cmp::min(1422 let old_end = initial_text.point_to_offset(cmp::min(
@@ -1431,7 +1432,7 @@ mod tests { · 9 unmodified lines
1431 .chunks_in_range(new_start..new_end)1432 .chunks_in_range(new_start..new_end)
1432 .collect::<String>();1433 .collect::<String>();
1433 1434
1434 initial_text.replace(old_start..old_end, &new_text);1435 initial_text.replace(old_start..old_end, &new_text, cx.background_executor());
1435 }1436 }
1436 assert_eq!(initial_text.to_string(), snapshot_text.to_string());1437 assert_eq!(initial_text.to_string(), snapshot_text.to_string());
1437 }1438 }
crates/editor/src/editor.rs+1−1
@@ -7852,7 +7852,7 @@ impl Editor { · 7851 unmodified lines
7852 let inlay = Inlay::edit_prediction(7852 let inlay = Inlay::edit_prediction(
7853 post_inc(&mut self.next_inlay_id),7853 post_inc(&mut self.next_inlay_id),
7854 range.start,7854 range.start,
7855 new_text.as_str(),7855 Rope::from_str_small(new_text.as_str()),
7856 );7856 );
7857 inlay_ids.push(inlay.id);7857 inlay_ids.push(inlay.id);
7858 inlays.push(inlay);7858 inlays.push(inlay);
crates/editor/src/git/blame.rs+3−2
@@ -1115,18 +1115,19 @@ mod tests { · 1114 unmodified lines
1115 1115
1116 let fs = FakeFs::new(cx.executor());1116 let fs = FakeFs::new(cx.executor());
1117 let buffer_initial_text_len = rng.random_range(5..15);1117 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(
1119 RandomCharIter::new(&mut rng)1119 RandomCharIter::new(&mut rng)
1120 .take(buffer_initial_text_len)1120 .take(buffer_initial_text_len)
1121 .collect::<String>()1121 .collect::<String>()
1122 .as_str(),1122 .as_str(),
1123 cx.background_executor(),
1123 );1124 );
1124 1125
1125 let mut newline_ixs = (0..buffer_initial_text_len).choose_multiple(&mut rng, 5);1126 let mut newline_ixs = (0..buffer_initial_text_len).choose_multiple(&mut rng, 5);
1126 newline_ixs.sort_unstable();1127 newline_ixs.sort_unstable();
1127 for newline_ix in newline_ixs.into_iter().rev() {1128 for newline_ix in newline_ixs.into_iter().rev() {
1128 let newline_ix = buffer_initial_text.clip_offset(newline_ix, Bias::Right);1129 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());
1130 }1131 }
1131 log::info!("initial buffer text: {:?}", buffer_initial_text);1132 log::info!("initial buffer text: {:?}", buffer_initial_text);
1132 1133
crates/editor/src/inlays.rs+9−9
@@ -59,10 +59,10 @@ impl Inlay { · 58 unmodified lines
59 pub fn hint(id: InlayId, position: Anchor, hint: &InlayHint) -> Self {59 pub fn hint(id: InlayId, position: Anchor, hint: &InlayHint) -> Self {
60 let mut text = hint.text();60 let mut text = hint.text();
61 if hint.padding_right && text.reversed_chars_at(text.len()).next() != Some(' ') {61 if hint.padding_right && text.reversed_chars_at(text.len()).next() != Some(' ') {
62 text.push(" ");62 text.push_small(" ");
63 }63 }
64 if hint.padding_left && text.chars_at(0).next() != Some(' ') {64 if hint.padding_left && text.chars_at(0).next() != Some(' ') {
65 text.push_front(" ");65 text.push_front_small(" ");
66 }66 }
67 Self {67 Self {
68 id,68 id,
@@ -72,11 +72,11 @@ impl Inlay { · 3 unmodified lines
72 }72 }
73 73
74 #[cfg(any(test, feature = "test-support"))]74 #[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 {
76 Self {76 Self {
77 id: InlayId::Hint(id),77 id: InlayId::Hint(id),
78 position,78 position,
79 content: InlayContent::Text(text.into()),79 content: InlayContent::Text(text),
80 }80 }
81 }81 }
82 82
@@ -88,19 +88,19 @@ impl Inlay { · 5 unmodified lines
88 }88 }
89 }89 }
90 90
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 {
92 Self {92 Self {
93 id: InlayId::EditPrediction(id),93 id: InlayId::EditPrediction(id),
94 position,94 position,
95 content: InlayContent::Text(text.into()),95 content: InlayContent::Text(text),
96 }96 }
97 }97 }
98 98
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 {
100 Self {100 Self {
101 id: InlayId::DebuggerValue(id),101 id: InlayId::DebuggerValue(id),
102 position,102 position,
103 content: InlayContent::Text(text.into()),103 content: InlayContent::Text(text),
104 }104 }
105 }105 }
106 106
@@ -108,7 +108,7 @@ impl Inlay { · 1 unmodified line
108 static COLOR_TEXT: OnceLock<Rope> = OnceLock::new();108 static COLOR_TEXT: OnceLock<Rope> = OnceLock::new();
109 match &self.content {109 match &self.content {
110 InlayContent::Text(text) => text,110 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("◼")),
112 }112 }
113 }113 }
114 114
crates/editor/src/movement.rs+5−4
@@ -878,6 +878,7 @@ mod tests { · 877 unmodified lines
878 use gpui::{AppContext as _, font, px};878 use gpui::{AppContext as _, font, px};
879 use language::Capability;879 use language::Capability;
880 use project::{Project, project_settings::DiagnosticSeverity};880 use project::{Project, project_settings::DiagnosticSeverity};
881 use rope::Rope;
881 use settings::SettingsStore;882 use settings::SettingsStore;
882 use util::post_inc;883 use util::post_inc;
883 884
@@ -1024,22 +1025,22 @@ mod tests { · 140 unmodified lines
1024 Inlay::edit_prediction(1025 Inlay::edit_prediction(
1025 post_inc(&mut id),1026 post_inc(&mut id),
1026 buffer_snapshot.anchor_before(offset),1027 buffer_snapshot.anchor_before(offset),
1027 "test",1028 Rope::from_str_small("test"),
1028 ),1029 ),
1029 Inlay::edit_prediction(1030 Inlay::edit_prediction(
1030 post_inc(&mut id),1031 post_inc(&mut id),
1031 buffer_snapshot.anchor_after(offset),1032 buffer_snapshot.anchor_after(offset),
1032 "test",1033 Rope::from_str_small("test"),
1033 ),1034 ),
1034 Inlay::mock_hint(1035 Inlay::mock_hint(
1035 post_inc(&mut id),1036 post_inc(&mut id),
1036 buffer_snapshot.anchor_before(offset),1037 buffer_snapshot.anchor_before(offset),
1037 "test",1038 Rope::from_str_small("test"),
1038 ),1039 ),
1039 Inlay::mock_hint(1040 Inlay::mock_hint(
1040 post_inc(&mut id),1041 post_inc(&mut id),
1041 buffer_snapshot.anchor_after(offset),1042 buffer_snapshot.anchor_after(offset),
1042 "test",1043 Rope::from_str_small("test"),
1043 ),1044 ),
1044 ]1045 ]
1045 })1046 })
crates/editor/src/signature_help.rs+1−1
@@ -193,7 +193,7 @@ impl Editor { · 192 unmodified lines
193 193
194 if let Some(language) = language {194 if let Some(language) = language {
195 for signature in &mut signature_help.signatures {195 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());
197 let highlights = language197 let highlights = language
198 .highlight_text(&text, 0..signature.label.len())198 .highlight_text(&text, 0..signature.label.len())
199 .into_iter()199 .into_iter()
crates/extension_host/src/extension_host.rs+10−5
@@ -1468,6 +1468,7 @@ impl ExtensionStore { · 1467 unmodified lines
1468 let extensions_dir = self.installed_dir.clone();1468 let extensions_dir = self.installed_dir.clone();
1469 let index_path = self.index_path.clone();1469 let index_path = self.index_path.clone();
1470 let proxy = self.proxy.clone();1470 let proxy = self.proxy.clone();
1471 let executor = cx.background_executor().clone();
1471 cx.background_spawn(async move {1472 cx.background_spawn(async move {
1472 let start_time = Instant::now();1473 let start_time = Instant::now();
1473 let mut index = ExtensionIndex::default();1474 let mut index = ExtensionIndex::default();
@@ -1501,10 +1502,14 @@ impl ExtensionStore { · 27 unmodified lines
1501 }1502 }
1502 1503
1503 if let Ok(index_json) = serde_json::to_string_pretty(&index) {1504 if let Ok(index_json) = serde_json::to_string_pretty(&index) {
1504 fs.save(&index_path, &index_json.as_str().into(), Default::default())1505 fs.save(
1505 .await1506 &index_path,
1506 .context("failed to save extension index")1507 &Rope::from_str(&index_json, &executor),
1507 .log_err();1508 Default::default(),
1509 )
1510 .await
1511 .context("failed to save extension index")
1512 .log_err();
1508 }1513 }
1509 1514
1510 log::info!("rebuilt extension index in {:?}", start_time.elapsed());1515 log::info!("rebuilt extension index in {:?}", start_time.elapsed());
@@ -1671,7 +1676,7 @@ impl ExtensionStore { · 160 unmodified lines
1671 let manifest_toml = toml::to_string(&loaded_extension.manifest)?;1676 let manifest_toml = toml::to_string(&loaded_extension.manifest)?;
1672 fs.save(1677 fs.save(
1673 &tmp_dir.join(EXTENSION_TOML),1678 &tmp_dir.join(EXTENSION_TOML),
1674 &Rope::from(manifest_toml),1679 &Rope::from_str_small(&manifest_toml),
1675 language::LineEnding::Unix,1680 language::LineEnding::Unix,
1676 )1681 )
1677 .await?;1682 .await?;
crates/git_ui/src/commit_view.rs+6−3
@@ -170,7 +170,10 @@ impl CommitView { · 169 unmodified lines
170 ReplicaId::LOCAL,170 ReplicaId::LOCAL,
171 cx.entity_id().as_non_zero_u64().into(),171 cx.entity_id().as_non_zero_u64().into(),
172 LineEnding::default(),172 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 ),
174 );177 );
175 metadata_buffer_id = Some(buffer.remote_id());178 metadata_buffer_id = Some(buffer.remote_id());
176 Buffer::build(buffer, Some(file.clone()), Capability::ReadWrite)179 Buffer::build(buffer, Some(file.clone()), Capability::ReadWrite)
@@ -336,7 +339,7 @@ async fn build_buffer( · 159 unmodified lines
336) -> Result<Entity<Buffer>> {339) -> Result<Entity<Buffer>> {
337 let line_ending = LineEnding::detect(&text);340 let line_ending = LineEnding::detect(&text);
338 LineEnding::normalize(&mut text);341 LineEnding::normalize(&mut text);
339 let text = Rope::from(text);342 let text = Rope::from_str(&text, cx.background_executor());
340 let language = cx.update(|cx| language_registry.language_for_file(&blob, Some(&text), cx))?;343 let language = cx.update(|cx| language_registry.language_for_file(&blob, Some(&text), cx))?;
341 let language = if let Some(language) = language {344 let language = if let Some(language) = language {
342 language_registry345 language_registry
@@ -376,7 +379,7 @@ async fn build_buffer_diff( · 33 unmodified lines
376 let base_buffer = cx379 let base_buffer = cx
377 .update(|cx| {380 .update(|cx| {
378 Buffer::build_snapshot(381 Buffer::build_snapshot(
379 old_text.as_deref().unwrap_or("").into(),382 Rope::from_str(old_text.as_deref().unwrap_or(""), cx.background_executor()),
380 buffer.language().cloned(),383 buffer.language().cloned(),
381 Some(language_registry.clone()),384 Some(language_registry.clone()),
382 cx,385 cx,
crates/git_ui/src/file_diff_view.rs+5−6
@@ -359,6 +359,7 @@ mod tests { · 358 unmodified lines
359 use super::*;359 use super::*;
360 use editor::test::editor_test_context::assert_state_with_diff;360 use editor::test::editor_test_context::assert_state_with_diff;
361 use gpui::TestAppContext;361 use gpui::TestAppContext;
362 use language::Rope;
362 use project::{FakeFs, Fs, Project};363 use project::{FakeFs, Fs, Project};
363 use settings::SettingsStore;364 use settings::SettingsStore;
364 use std::path::PathBuf;365 use std::path::PathBuf;
@@ -429,7 +430,7 @@ mod tests { · 64 unmodified lines
429 // Modify the new file on disk430 // Modify the new file on disk
430 fs.save(431 fs.save(
431 path!("/test/new_file.txt").as_ref(),432 path!("/test/new_file.txt").as_ref(),
432 &unindent(433 &Rope::from_str_small(&unindent(
433 "434 "
434 new line 1435 new line 1
435 line 2436 line 2
@@ -437,8 +438,7 @@ mod tests { · 1 unmodified line
437 line 4438 line 4
438 new line 5439 new line 5
439 ",440 ",
440 )441 )),
441 .into(),
442 Default::default(),442 Default::default(),
443 )443 )
444 .await444 .await
@@ -465,15 +465,14 @@ mod tests { · 20 unmodified lines
465 // Modify the old file on disk465 // Modify the old file on disk
466 fs.save(466 fs.save(
467 path!("/test/old_file.txt").as_ref(),467 path!("/test/old_file.txt").as_ref(),
468 &unindent(468 &Rope::from_str_small(&unindent(
469 "469 "
470 new line 1470 new line 1
471 line 2471 line 2
472 old line 3472 old line 3
473 line 4473 line 4
474 ",474 ",
475 )475 )),
476 .into(),
477 Default::default(),476 Default::default(),
478 )477 )
479 .await478 .await
crates/gpui/src/app/async_context.rs+13−0
@@ -260,6 +260,19 @@ impl AsyncApp { · 259 unmodified lines
260 }260 }
261}261}
262 262
263impl 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
263/// A cloneable, owned handle to the application context,276/// A cloneable, owned handle to the application context,
264/// composed with the window associated with the current task.277/// composed with the window associated with the current task.
265#[derive(Clone, Deref, DerefMut)]278#[derive(Clone, Deref, DerefMut)]
crates/gpui/src/app/test_context.rs+5−0
@@ -393,6 +393,11 @@ impl TestAppContext { · 392 unmodified lines
393 }393 }
394 }394 }
395 395
396 /// Returns the background executor for this context.
397 pub fn background_executor(&self) -> &BackgroundExecutor {
398 &self.background_executor
399 }
400
396 /// Wait until there are no more pending tasks.401 /// Wait until there are no more pending tasks.
397 pub fn run_until_parked(&mut self) {402 pub fn run_until_parked(&mut self) {
398 self.background_executor.run_until_parked()403 self.background_executor.run_until_parked()
crates/gpui/src/executor.rs+1−1
@@ -342,7 +342,7 @@ impl BackgroundExecutor { · 341 unmodified lines
342 /// for all of them to complete before returning.342 /// for all of them to complete before returning.
343 pub async fn scoped<'scope, F>(&self, scheduler: F)343 pub async fn scoped<'scope, F>(&self, scheduler: F)
344 where344 where
345 F: FnOnce(&mut Scope<'scope>),345 F: for<'a> FnOnce(&'a mut Scope<'scope>),
346 {346 {
347 let mut scope = Scope::new(self.clone());347 let mut scope = Scope::new(self.clone());
348 (scheduler)(&mut scope);348 (scheduler)(&mut scope);
crates/keymap_editor/src/keymap_editor.rs+2−2
@@ -22,7 +22,7 @@ use gpui::{ · 21 unmodified lines
22 ScrollWheelEvent, Stateful, StyledText, Subscription, Task, TextStyleRefinement, WeakEntity,22 ScrollWheelEvent, Stateful, StyledText, Subscription, Task, TextStyleRefinement, WeakEntity,
23 actions, anchored, deferred, div,23 actions, anchored, deferred, div,
24};24};
25use language::{Language, LanguageConfig, ToOffset as _};25use language::{Language, LanguageConfig, Rope, ToOffset as _};
26use notifications::status_toast::{StatusToast, ToastIcon};26use notifications::status_toast::{StatusToast, ToastIcon};
27use project::{CompletionDisplayOptions, Project};27use project::{CompletionDisplayOptions, Project};
28use settings::{28use settings::{
@@ -2119,7 +2119,7 @@ impl RenderOnce for SyntaxHighlightedText { · 2090 unmodified lines
2119 2119
2120 let highlights = self2120 let highlights = self
2121 .language2121 .language
2122 .highlight_text(&text.as_ref().into(), 0..text.len());2122 .highlight_text(&Rope::from_str_small(text.as_ref()), 0..text.len());
2123 let mut runs = Vec::with_capacity(highlights.len());2123 let mut runs = Vec::with_capacity(highlights.len());
2124 let mut offset = 0;2124 let mut offset = 0;
2125 2125
crates/language/src/buffer.rs+14−7
@@ -24,8 +24,8 @@ use collections::HashMap; · 23 unmodified lines
24use fs::MTime;24use fs::MTime;
25use futures::channel::oneshot;25use futures::channel::oneshot;
26use gpui::{26use gpui::{
27 App, AppContext as _, Context, Entity, EventEmitter, HighlightStyle, SharedString, StyledText,27 App, AppContext as _, BackgroundExecutor, Context, Entity, EventEmitter, HighlightStyle,
28 Task, TaskLabel, TextStyle,28 SharedString, StyledText, Task, TaskLabel, TextStyle,
29};29};
30 30
31use lsp::{LanguageServerId, NumberOrString};31use lsp::{LanguageServerId, NumberOrString};
@@ -832,6 +832,7 @@ impl Buffer { · 800 unmodified lines
832 ReplicaId::LOCAL,832 ReplicaId::LOCAL,
833 cx.entity_id().as_non_zero_u64().into(),833 cx.entity_id().as_non_zero_u64().into(),
834 base_text.into(),834 base_text.into(),
835 &cx.background_executor(),
835 ),836 ),
836 None,837 None,
837 Capability::ReadWrite,838 Capability::ReadWrite,
@@ -862,9 +863,10 @@ impl Buffer { · 24 unmodified lines
862 replica_id: ReplicaId,863 replica_id: ReplicaId,
863 capability: Capability,864 capability: Capability,
864 base_text: impl Into<String>,865 base_text: impl Into<String>,
866 cx: &BackgroundExecutor,
865 ) -> Self {867 ) -> Self {
866 Self::build(868 Self::build(
867 TextBuffer::new(replica_id, remote_id, base_text.into()),869 TextBuffer::new(replica_id, remote_id, base_text.into(), cx),
868 None,870 None,
869 capability,871 capability,
870 )872 )
@@ -877,9 +879,10 @@ impl Buffer { · 6 unmodified lines
877 capability: Capability,879 capability: Capability,
878 message: proto::BufferState,880 message: proto::BufferState,
879 file: Option<Arc<dyn File>>,881 file: Option<Arc<dyn File>>,
882 cx: &BackgroundExecutor,
880 ) -> Result<Self> {883 ) -> Result<Self> {
881 let buffer_id = BufferId::new(message.id).context("Could not deserialize buffer_id")?;884 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);
883 let mut this = Self::build(buffer, file, capability);886 let mut this = Self::build(buffer, file, capability);
884 this.text.set_line_ending(proto::deserialize_line_ending(887 this.text.set_line_ending(proto::deserialize_line_ending(
885 rpc::proto::LineEnding::from_i32(message.line_ending).context("missing line_ending")?,888 rpc::proto::LineEnding::from_i32(message.line_ending).context("missing line_ending")?,
@@ -1138,13 +1141,14 @@ impl Buffer { · 252 unmodified lines
1138 let old_snapshot = self.text.snapshot();1141 let old_snapshot = self.text.snapshot();
1139 let mut branch_buffer = self.text.branch();1142 let mut branch_buffer = self.text.branch();
1140 let mut syntax_snapshot = self.syntax_map.lock().snapshot();1143 let mut syntax_snapshot = self.syntax_map.lock().snapshot();
1144 let executor = cx.background_executor().clone();
1141 cx.background_spawn(async move {1145 cx.background_spawn(async move {
1142 if !edits.is_empty() {1146 if !edits.is_empty() {
1143 if let Some(language) = language.clone() {1147 if let Some(language) = language.clone() {
1144 syntax_snapshot.reparse(&old_snapshot, registry.clone(), language);1148 syntax_snapshot.reparse(&old_snapshot, registry.clone(), language);
1145 }1149 }
1146 1150
1147 branch_buffer.edit(edits.iter().cloned());1151 branch_buffer.edit(edits.iter().cloned(), &executor);
1148 let snapshot = branch_buffer.snapshot();1152 let snapshot = branch_buffer.snapshot();
1149 syntax_snapshot.interpolate(&snapshot);1153 syntax_snapshot.interpolate(&snapshot);
1150 1154
@@ -2361,7 +2365,9 @@ impl Buffer { · 1210 unmodified lines
2361 let autoindent_request = autoindent_mode2365 let autoindent_request = autoindent_mode
2362 .and_then(|mode| self.language.as_ref().map(|_| (self.snapshot(), mode)));2366 .and_then(|mode| self.language.as_ref().map(|_| (self.snapshot(), mode)));
2363 2367
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());
2365 let edit_id = edit_operation.timestamp();2371 let edit_id = edit_operation.timestamp();
2366 2372
2367 if let Some((before_edit, mode)) = autoindent_request {2373 if let Some((before_edit, mode)) = autoindent_request {
@@ -2592,7 +2598,8 @@ impl Buffer { · 224 unmodified lines
2592 for operation in buffer_ops.iter() {2598 for operation in buffer_ops.iter() {
2593 self.send_operation(Operation::Buffer(operation.clone()), false, cx);2599 self.send_operation(Operation::Buffer(operation.clone()), false, cx);
2594 }2600 }
2595 self.text.apply_ops(buffer_ops);2601 self.text
2602 .apply_ops(buffer_ops, Some(cx.background_executor()));
2596 self.deferred_ops.insert(deferred_ops);2603 self.deferred_ops.insert(deferred_ops);
2597 self.flush_deferred_ops(cx);2604 self.flush_deferred_ops(cx);
2598 self.did_edit(&old_version, was_dirty, cx);2605 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
75 Capability::ReadWrite,75 Capability::ReadWrite,
76 base.read(cx).to_proto(cx),76 base.read(cx).to_proto(cx),
77 None,77 None,
78 cx.background_executor(),
78 )79 )
79 .unwrap()80 .unwrap()
80 });81 });
@@ -255,14 +256,18 @@ async fn test_first_line_pattern(cx: &mut TestAppContext) { · 174 unmodified lines
255 .is_none()256 .is_none()
256 );257 );
257 assert!(258 assert!(
258 cx.read(|cx| languages.language_for_file(&file("the/script"), Some(&"nothing".into()), cx))259 cx.read(|cx| languages.language_for_file(
259 .is_none()260 &file("the/script"),
261 Some(&Rope::from_str("nothing", cx.background_executor())),
262 cx
263 ))
264 .is_none()
260 );265 );
261 266
262 assert_eq!(267 assert_eq!(
263 cx.read(|cx| languages.language_for_file(268 cx.read(|cx| languages.language_for_file(
264 &file("the/script"),269 &file("the/script"),
265 Some(&"#!/bin/env node".into()),270 Some(&Rope::from_str("#!/bin/env node", cx.background_executor())),
266 cx271 cx
267 ))272 ))
268 .unwrap()273 .unwrap()
@@ -406,6 +411,7 @@ fn test_edit_events(cx: &mut gpui::App) { · 137 unmodified lines
406 ReplicaId::new(1),411 ReplicaId::new(1),
407 Capability::ReadWrite,412 Capability::ReadWrite,
408 "abcdef",413 "abcdef",
414 cx.background_executor(),
409 )415 )
410 });416 });
411 let buffer1_ops = Arc::new(Mutex::new(Vec::new()));417 let buffer1_ops = Arc::new(Mutex::new(Vec::new()));
@@ -2781,8 +2787,14 @@ fn test_serialization(cx: &mut gpui::App) { · 2369 unmodified lines
2781 .background_executor()2787 .background_executor()
2782 .block(buffer1.read(cx).serialize_ops(None, cx));2788 .block(buffer1.read(cx).serialize_ops(None, cx));
2783 let buffer2 = cx.new(|cx| {2789 let buffer2 = cx.new(|cx| {
2784 let mut buffer =2790 let mut buffer = Buffer::from_proto(
2785 Buffer::from_proto(ReplicaId::new(1), Capability::ReadWrite, state, None).unwrap();2791 ReplicaId::new(1),
2792 Capability::ReadWrite,
2793 state,
2794 None,
2795 cx.background_executor(),
2796 )
2797 .unwrap();
2786 buffer.apply_ops(2798 buffer.apply_ops(
2787 ops.into_iter()2799 ops.into_iter()
2788 .map(|op| proto::deserialize_operation(op).unwrap()),2800 .map(|op| proto::deserialize_operation(op).unwrap()),
@@ -2806,6 +2818,7 @@ fn test_branch_and_merge(cx: &mut TestAppContext) { · 17 unmodified lines
2806 Capability::ReadWrite,2818 Capability::ReadWrite,
2807 base.read(cx).to_proto(cx),2819 base.read(cx).to_proto(cx),
2808 None,2820 None,
2821 cx.background_executor(),
2809 )2822 )
2810 .unwrap()2823 .unwrap()
2811 });2824 });
@@ -3120,9 +3133,14 @@ fn test_random_collaboration(cx: &mut App, mut rng: StdRng) { · 308 unmodified lines
3120 let ops = cx3133 let ops = cx
3121 .background_executor()3134 .background_executor()
3122 .block(base_buffer.read(cx).serialize_ops(None, cx));3135 .block(base_buffer.read(cx).serialize_ops(None, cx));
3123 let mut buffer =3136 let mut buffer = Buffer::from_proto(
3124 Buffer::from_proto(ReplicaId::new(i as u16), Capability::ReadWrite, state, None)3137 ReplicaId::new(i as u16),
3125 .unwrap();3138 Capability::ReadWrite,
3139 state,
3140 None,
3141 cx.background_executor(),
3142 )
3143 .unwrap();
3126 buffer.apply_ops(3144 buffer.apply_ops(
3127 ops.into_iter()3145 ops.into_iter()
3128 .map(|op| proto::deserialize_operation(op).unwrap()),3146 .map(|op| proto::deserialize_operation(op).unwrap()),
@@ -3251,6 +3269,7 @@ fn test_random_collaboration(cx: &mut App, mut rng: StdRng) { · 122 unmodified lines
3251 Capability::ReadWrite,3269 Capability::ReadWrite,
3252 old_buffer_state,3270 old_buffer_state,
3253 None,3271 None,
3272 cx.background_executor(),
3254 )3273 )
3255 .unwrap();3274 .unwrap();
3256 new_buffer.apply_ops(3275 new_buffer.apply_ops(
@@ -3414,7 +3433,7 @@ fn test_contiguous_ranges() { · 157 unmodified lines
3414}3433}
3415 3434
3416#[gpui::test(iterations = 500)]3435#[gpui::test(iterations = 500)]
3417fn test_trailing_whitespace_ranges(mut rng: StdRng) {3436fn test_trailing_whitespace_ranges(mut rng: StdRng, cx: &mut TestAppContext) {
3418 // Generate a random multi-line string containing3437 // Generate a random multi-line string containing
3419 // some lines with trailing whitespace.3438 // some lines with trailing whitespace.
3420 let mut text = String::new();3439 let mut text = String::new();
@@ -3438,7 +3457,7 @@ fn test_trailing_whitespace_ranges(mut rng: StdRng) { · 17 unmodified lines
3438 _ => {}3457 _ => {}
3439 }3458 }
3440 3459
3441 let rope = Rope::from(text.as_str());3460 let rope = Rope::from_str(text.as_str(), cx.background_executor());
3442 let actual_ranges = trailing_whitespace_ranges(&rope);3461 let actual_ranges = trailing_whitespace_ranges(&rope);
3443 let expected_ranges = TRAILING_WHITESPACE_REGEX3462 let expected_ranges = TRAILING_WHITESPACE_REGEX
3444 .find_iter(&text)3463 .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
100 }100 }
101 "#101 "#
102 .unindent(),102 .unindent(),
103 cx.background_executor(),
103 );104 );
104 105
105 let mut syntax_map = SyntaxMap::new(&buffer);106 let mut syntax_map = SyntaxMap::new(&buffer);
@@ -147,7 +148,7 @@ fn test_syntax_map_layers_for_range(cx: &mut App) { · 41 unmodified lines
147 148
148 // Replace a vec! macro invocation with a plain slice, removing a syntactic layer.149 // Replace a vec! macro invocation with a plain slice, removing a syntactic layer.
149 let macro_name_range = range_for_text(&buffer, "vec!");150 let macro_name_range = range_for_text(&buffer, "vec!");
150 buffer.edit([(macro_name_range, "&")]);151 buffer.edit([(macro_name_range, "&")], cx.background_executor());
151 syntax_map.interpolate(&buffer);152 syntax_map.interpolate(&buffer);
152 syntax_map.reparse(language.clone(), &buffer);153 syntax_map.reparse(language.clone(), &buffer);
153 154
@@ -199,6 +200,7 @@ fn test_dynamic_language_injection(cx: &mut App) { · 45 unmodified lines
199 ```200 ```
200 "#201 "#
201 .unindent(),202 .unindent(),
203 cx.background_executor(),
202 );204 );
203 205
204 let mut syntax_map = SyntaxMap::new(&buffer);206 let mut syntax_map = SyntaxMap::new(&buffer);
@@ -218,7 +220,10 @@ fn test_dynamic_language_injection(cx: &mut App) { · 13 unmodified lines
218 220
219 // Replace `rs` with a path to ending in `.rb` in code block.221 // Replace `rs` with a path to ending in `.rb` in code block.
220 let macro_name_range = range_for_text(&buffer, "rs");222 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 );
222 syntax_map.interpolate(&buffer);227 syntax_map.interpolate(&buffer);
223 syntax_map.reparse(markdown.clone(), &buffer);228 syntax_map.reparse(markdown.clone(), &buffer);
224 syntax_map.reparse(markdown_inline.clone(), &buffer);229 syntax_map.reparse(markdown_inline.clone(), &buffer);
@@ -235,7 +240,7 @@ fn test_dynamic_language_injection(cx: &mut App) { · 10 unmodified lines
235 240
236 // Replace Ruby with a language that hasn't been loaded yet.241 // Replace Ruby with a language that hasn't been loaded yet.
237 let macro_name_range = range_for_text(&buffer, "foo/bar/baz.rb");242 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());
239 syntax_map.interpolate(&buffer);244 syntax_map.interpolate(&buffer);
240 syntax_map.reparse(markdown.clone(), &buffer);245 syntax_map.reparse(markdown.clone(), &buffer);
241 syntax_map.reparse(markdown_inline.clone(), &buffer);246 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
811 .unindent();816 .unindent();
812 817
813 let registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));818 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 );
815 825
816 let mut syntax_map = SyntaxMap::new(&buffer);826 let mut syntax_map = SyntaxMap::new(&buffer);
817 syntax_map.set_language_registry(registry.clone());827 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
859 .unindent();869 .unindent();
860 870
861 log::info!("editing");871 log::info!("editing");
862 buffer.edit_via_marked_text(&text);872 buffer.edit_via_marked_text(&text, cx.background_executor());
863 syntax_map.interpolate(&buffer);873 syntax_map.interpolate(&buffer);
864 syntax_map.reparse(language, &buffer);874 syntax_map.reparse(language, &buffer);
865 875
@@ -903,7 +913,7 @@ fn test_random_syntax_map_edits_rust_macros(rng: StdRng, cx: &mut App) { · 37 unmodified lines
903 let language = Arc::new(rust_lang());913 let language = Arc::new(rust_lang());
904 registry.add(language.clone());914 registry.add(language.clone());
905 915
906 test_random_edits(text, registry, language, rng);916 test_random_edits(text, registry, language, rng, cx);
907}917}
908 918
909#[gpui::test(iterations = 50)]919#[gpui::test(iterations = 50)]
@@ -932,7 +942,7 @@ fn test_random_syntax_map_edits_with_erb(rng: StdRng, cx: &mut App) { · 22 unmodified lines
932 registry.add(Arc::new(ruby_lang()));942 registry.add(Arc::new(ruby_lang()));
933 registry.add(Arc::new(html_lang()));943 registry.add(Arc::new(html_lang()));
934 944
935 test_random_edits(text, registry, language, rng);945 test_random_edits(text, registry, language, rng, cx);
936}946}
937 947
938#[gpui::test(iterations = 50)]948#[gpui::test(iterations = 50)]
@@ -965,7 +975,7 @@ fn test_random_syntax_map_edits_with_heex(rng: StdRng, cx: &mut App) { · 26 unmodified lines
965 registry.add(Arc::new(heex_lang()));975 registry.add(Arc::new(heex_lang()));
966 registry.add(Arc::new(html_lang()));976 registry.add(Arc::new(html_lang()));
967 977
968 test_random_edits(text, registry, language, rng);978 test_random_edits(text, registry, language, rng, cx);
969}979}
970 980
971fn test_random_edits(981fn test_random_edits(
@@ -973,12 +983,18 @@ fn test_random_edits( · 1 unmodified line
973 registry: Arc<LanguageRegistry>,983 registry: Arc<LanguageRegistry>,
974 language: Arc<Language>,984 language: Arc<Language>,
975 mut rng: StdRng,985 mut rng: StdRng,
986 cx: &mut App,
976) {987) {
977 let operations = env::var("OPERATIONS")988 let operations = env::var("OPERATIONS")
978 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))989 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
979 .unwrap_or(10);990 .unwrap_or(10);
980 991
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 );
982 998
983 let mut syntax_map = SyntaxMap::new(&buffer);999 let mut syntax_map = SyntaxMap::new(&buffer);
984 syntax_map.set_language_registry(registry.clone());1000 syntax_map.set_language_registry(registry.clone());
@@ -993,7 +1009,7 @@ fn test_random_edits( · 8 unmodified lines
993 let prev_buffer = buffer.snapshot();1009 let prev_buffer = buffer.snapshot();
994 let prev_syntax_map = syntax_map.snapshot();1010 let prev_syntax_map = syntax_map.snapshot();
995 1011
996 buffer.randomly_edit(&mut rng, 3);1012 buffer.randomly_edit(&mut rng, 3, cx.background_executor());
997 log::info!("text:\n{}", buffer.text());1013 log::info!("text:\n{}", buffer.text());
998 1014
999 syntax_map.interpolate(&buffer);1015 syntax_map.interpolate(&buffer);
@@ -1159,7 +1175,12 @@ fn test_edit_sequence(language_name: &str, steps: &[&str], cx: &mut App) -> (Buf · 159 unmodified lines
1159 .now_or_never()1175 .now_or_never()
1160 .unwrap()1176 .unwrap()
1161 .unwrap();1177 .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 );
1163 1184
1164 let mut mutated_syntax_map = SyntaxMap::new(&buffer);1185 let mut mutated_syntax_map = SyntaxMap::new(&buffer);
1165 mutated_syntax_map.set_language_registry(registry.clone());1186 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
1168 for (i, marked_string) in steps.iter().enumerate() {1189 for (i, marked_string) in steps.iter().enumerate() {
1169 let marked_string = marked_string.unindent();1190 let marked_string = marked_string.unindent();
1170 log::info!("incremental parse {i}: {marked_string:?}");1191 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());
1172 1193
1173 // Reparse the syntax map1194 // Reparse the syntax map
1174 mutated_syntax_map.interpolate(&buffer);1195 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
11use gpui::{App, AppContext, AsyncApp, Task};11use gpui::{App, AppContext, AsyncApp, Task};
12use language::{12use language::{
13 BinaryStatus, CodeLabel, DynLspInstaller, HighlightId, Language, LanguageName, LspAdapter,13 BinaryStatus, CodeLabel, DynLspInstaller, HighlightId, Language, LanguageName, LspAdapter,
14 LspAdapterDelegate, Toolchain,14 LspAdapterDelegate, Rope, Toolchain,
15};15};
16use lsp::{16use lsp::{
17 CodeActionKind, LanguageServerBinary, LanguageServerBinaryOptions, LanguageServerName,17 CodeActionKind, LanguageServerBinary, LanguageServerBinaryOptions, LanguageServerName,
@@ -403,7 +403,10 @@ fn labels_from_extension( · 385 unmodified lines
403 let runs = if label.code.is_empty() {403 let runs = if label.code.is_empty() {
404 Vec::new()404 Vec::new()
405 } else {405 } 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 )
407 };410 };
408 build_code_label(&label, &runs, language)411 build_code_label(&label, &runs, language)
409 })412 })
crates/languages/src/c.rs+6−4
@@ -189,7 +189,7 @@ impl super::LspAdapter for CLspAdapter { · 188 unmodified lines
189 Some(lsp::CompletionItemKind::FIELD) if completion.detail.is_some() => {189 Some(lsp::CompletionItemKind::FIELD) if completion.detail.is_some() => {
190 let detail = completion.detail.as_ref().unwrap();190 let detail = completion.detail.as_ref().unwrap();
191 let text = format!("{} {}", detail, label);191 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());
193 let runs = language.highlight_text(&source, 11..11 + text.len());193 let runs = language.highlight_text(&source, 11..11 + text.len());
194 let filter_range = completion194 let filter_range = completion
195 .filter_text195 .filter_text
@@ -206,7 +206,8 @@ impl super::LspAdapter for CLspAdapter { · 10 unmodified lines
206 {206 {
207 let detail = completion.detail.as_ref().unwrap();207 let detail = completion.detail.as_ref().unwrap();
208 let text = format!("{} {}", detail, label);208 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());
210 let filter_range = completion211 let filter_range = completion
211 .filter_text212 .filter_text
212 .as_deref()213 .as_deref()
@@ -222,7 +223,8 @@ impl super::LspAdapter for CLspAdapter { · 9 unmodified lines
222 {223 {
223 let detail = completion.detail.as_ref().unwrap();224 let detail = completion.detail.as_ref().unwrap();
224 let text = format!("{} {}", detail, label);225 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());
226 let filter_range = completion228 let filter_range = completion
227 .filter_text229 .filter_text
228 .as_deref()230 .as_deref()
@@ -326,7 +328,7 @@ impl super::LspAdapter for CLspAdapter { · 97 unmodified lines
326 Some(CodeLabel::new(328 Some(CodeLabel::new(
327 text[display_range.clone()].to_string(),329 text[display_range.clone()].to_string(),
328 filter_range,330 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),
330 ))332 ))
331 }333 }
332 334
crates/languages/src/go.rs+15−9
@@ -221,7 +221,7 @@ impl LspAdapter for GoLspAdapter { · 220 unmodified lines
221 match completion.kind.zip(completion.detail.as_ref()) {221 match completion.kind.zip(completion.detail.as_ref()) {
222 Some((lsp::CompletionItemKind::MODULE, detail)) => {222 Some((lsp::CompletionItemKind::MODULE, detail)) => {
223 let text = format!("{label} {detail}");223 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());
225 let runs = language.highlight_text(&source, 7..7 + text[name_offset..].len());225 let runs = language.highlight_text(&source, 7..7 + text[name_offset..].len());
226 let filter_range = completion226 let filter_range = completion
227 .filter_text227 .filter_text
@@ -238,8 +238,9 @@ impl LspAdapter for GoLspAdapter { · 10 unmodified lines
238 detail,238 detail,
239 )) => {239 )) => {
240 let text = format!("{label} {detail}");240 let text = format!("{label} {detail}");
241 let source =241 let source = Rope::from_str_small(
242 Rope::from(format!("var {} {}", &text[name_offset..], detail).as_str());242 format!("var {} {}", &text[name_offset..], detail).as_str(),
243 );
243 let runs = adjust_runs(244 let runs = adjust_runs(
244 name_offset,245 name_offset,
245 language.highlight_text(&source, 4..4 + text[name_offset..].len()),246 language.highlight_text(&source, 4..4 + text[name_offset..].len()),
@@ -256,7 +257,8 @@ impl LspAdapter for GoLspAdapter { · 10 unmodified lines
256 }257 }
257 Some((lsp::CompletionItemKind::STRUCT, _)) => {258 Some((lsp::CompletionItemKind::STRUCT, _)) => {
258 let text = format!("{label} struct {{}}");259 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());
260 let runs = adjust_runs(262 let runs = adjust_runs(
261 name_offset,263 name_offset,
262 language.highlight_text(&source, 5..5 + text[name_offset..].len()),264 language.highlight_text(&source, 5..5 + text[name_offset..].len()),
@@ -273,7 +275,8 @@ impl LspAdapter for GoLspAdapter { · 10 unmodified lines
273 }275 }
274 Some((lsp::CompletionItemKind::INTERFACE, _)) => {276 Some((lsp::CompletionItemKind::INTERFACE, _)) => {
275 let text = format!("{label} interface {{}}");277 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());
277 let runs = adjust_runs(280 let runs = adjust_runs(
278 name_offset,281 name_offset,
279 language.highlight_text(&source, 5..5 + text[name_offset..].len()),282 language.highlight_text(&source, 5..5 + text[name_offset..].len()),
@@ -290,8 +293,9 @@ impl LspAdapter for GoLspAdapter { · 10 unmodified lines
290 }293 }
291 Some((lsp::CompletionItemKind::FIELD, detail)) => {294 Some((lsp::CompletionItemKind::FIELD, detail)) => {
292 let text = format!("{label} {detail}");295 let text = format!("{label} {detail}");
293 let source =296 let source = Rope::from_str_small(
294 Rope::from(format!("type T struct {{ {} }}", &text[name_offset..]).as_str());297 format!("type T struct {{ {} }}", &text[name_offset..]).as_str(),
298 );
295 let runs = adjust_runs(299 let runs = adjust_runs(
296 name_offset,300 name_offset,
297 language.highlight_text(&source, 16..16 + text[name_offset..].len()),301 language.highlight_text(&source, 16..16 + text[name_offset..].len()),
@@ -309,7 +313,9 @@ impl LspAdapter for GoLspAdapter { · 11 unmodified lines
309 Some((lsp::CompletionItemKind::FUNCTION | lsp::CompletionItemKind::METHOD, detail)) => {313 Some((lsp::CompletionItemKind::FUNCTION | lsp::CompletionItemKind::METHOD, detail)) => {
310 if let Some(signature) = detail.strip_prefix("func") {314 if let Some(signature) = detail.strip_prefix("func") {
311 let text = format!("{label}{signature}");315 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 );
313 let runs = adjust_runs(319 let runs = adjust_runs(
314 name_offset,320 name_offset,
315 language.highlight_text(&source, 5..5 + text[name_offset..].len()),321 language.highlight_text(&source, 5..5 + text[name_offset..].len()),
@@ -385,7 +391,7 @@ impl LspAdapter for GoLspAdapter { · 69 unmodified lines
385 Some(CodeLabel::new(391 Some(CodeLabel::new(
386 text[display_range.clone()].to_string(),392 text[display_range.clone()].to_string(),
387 filter_range,393 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),
389 ))395 ))
390 }396 }
391 397
crates/languages/src/python.rs+4−3
@@ -19,6 +19,7 @@ use pet_core::python_environment::{PythonEnvironment, PythonEnvironmentKind}; · 18 unmodified lines
19use pet_virtualenv::is_virtualenv_dir;19use pet_virtualenv::is_virtualenv_dir;
20use project::Fs;20use project::Fs;
21use project::lsp_store::language_server_settings;21use project::lsp_store::language_server_settings;
22use rope::Rope;
22use serde::{Deserialize, Serialize};23use serde::{Deserialize, Serialize};
23use serde_json::{Value, json};24use serde_json::{Value, json};
24use smol::lock::OnceCell;25use smol::lock::OnceCell;
@@ -466,7 +467,7 @@ impl LspAdapter for PyrightLspAdapter { · 441 unmodified lines
466 Some(language::CodeLabel::new(467 Some(language::CodeLabel::new(
467 text[display_range.clone()].to_string(),468 text[display_range.clone()].to_string(),
468 filter_range,469 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),
470 ))471 ))
471 }472 }
472 473
@@ -1511,7 +1512,7 @@ impl LspAdapter for PyLspAdapter { · 1038 unmodified lines
1511 Some(language::CodeLabel::new(1512 Some(language::CodeLabel::new(
1512 text[display_range.clone()].to_string(),1513 text[display_range.clone()].to_string(),
1513 filter_range,1514 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),
1515 ))1516 ))
1516 }1517 }
1517 1518
@@ -1800,7 +1801,7 @@ impl LspAdapter for BasedPyrightLspAdapter { · 282 unmodified lines
1800 Some(language::CodeLabel::new(1801 Some(language::CodeLabel::new(
1801 text[display_range.clone()].to_string(),1802 text[display_range.clone()].to_string(),
1802 filter_range,1803 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),
1804 ))1805 ))
1805 }1806 }
1806 1807
crates/languages/src/rust.rs+9−6
@@ -252,7 +252,7 @@ impl LspAdapter for RustLspAdapter { · 251 unmodified lines
252 let name = &completion.label;252 let name = &completion.label;
253 let text = format!("{name}: {signature}");253 let text = format!("{name}: {signature}");
254 let prefix = "struct S { ";254 let prefix = "struct S { ";
255 let source = Rope::from_iter([prefix, &text, " }"]);255 let source = Rope::from_iter_small([prefix, &text, " }"]);
256 let runs =256 let runs =
257 language.highlight_text(&source, prefix.len()..prefix.len() + text.len());257 language.highlight_text(&source, prefix.len()..prefix.len() + text.len());
258 mk_label(text, &|| 0..completion.label.len(), runs)258 mk_label(text, &|| 0..completion.label.len(), runs)
@@ -264,7 +264,7 @@ impl LspAdapter for RustLspAdapter { · 5 unmodified lines
264 let name = &completion.label;264 let name = &completion.label;
265 let text = format!("{name}: {signature}",);265 let text = format!("{name}: {signature}",);
266 let prefix = "let ";266 let prefix = "let ";
267 let source = Rope::from_iter([prefix, &text, " = ();"]);267 let source = Rope::from_iter_small([prefix, &text, " = ();"]);
268 let runs =268 let runs =
269 language.highlight_text(&source, prefix.len()..prefix.len() + text.len());269 language.highlight_text(&source, prefix.len()..prefix.len() + text.len());
270 mk_label(text, &|| 0..completion.label.len(), runs)270 mk_label(text, &|| 0..completion.label.len(), runs)
@@ -302,7 +302,7 @@ impl LspAdapter for RustLspAdapter { · 31 unmodified lines
302 .filter(|it| it.contains(&label))302 .filter(|it| it.contains(&label))
303 .and_then(|it| Some((it, FULL_SIGNATURE_REGEX.find(it)?)))303 .and_then(|it| Some((it, FULL_SIGNATURE_REGEX.find(it)?)))
304 {304 {
305 let source = Rope::from(function_signature);305 let source = Rope::from_str_small(function_signature);
306 let runs = language.highlight_text(&source, 0..function_signature.len());306 let runs = language.highlight_text(&source, 0..function_signature.len());
307 mk_label(307 mk_label(
308 function_signature.to_owned(),308 function_signature.to_owned(),
@@ -311,7 +311,7 @@ impl LspAdapter for RustLspAdapter { · 2 unmodified lines
311 )311 )
312 } else if let Some((prefix, suffix)) = fn_prefixed {312 } else if let Some((prefix, suffix)) = fn_prefixed {
313 let text = format!("{label}{suffix}");313 let text = format!("{label}{suffix}");
314 let source = Rope::from_iter([prefix, " ", &text, " {}"]);314 let source = Rope::from_iter_small([prefix, " ", &text, " {}"]);
315 let run_start = prefix.len() + 1;315 let run_start = prefix.len() + 1;
316 let runs = language.highlight_text(&source, run_start..run_start + text.len());316 let runs = language.highlight_text(&source, run_start..run_start + text.len());
317 mk_label(text, &|| 0..label.len(), runs)317 mk_label(text, &|| 0..label.len(), runs)
@@ -322,7 +322,7 @@ impl LspAdapter for RustLspAdapter { · 4 unmodified lines
322 {322 {
323 let text = completion.label.clone();323 let text = completion.label.clone();
324 let len = text.len();324 let len = text.len();
325 let source = Rope::from(text.as_str());325 let source = Rope::from_str_small(text.as_str());
326 let runs = language.highlight_text(&source, 0..len);326 let runs = language.highlight_text(&source, 0..len);
327 mk_label(text, &|| 0..completion.label.len(), runs)327 mk_label(text, &|| 0..completion.label.len(), runs)
328 } else if detail_left.is_none() {328 } else if detail_left.is_none() {
@@ -399,7 +399,10 @@ impl LspAdapter for RustLspAdapter { · 70 unmodified lines
399 Some(CodeLabel::new(399 Some(CodeLabel::new(
400 format!("{prefix}{name}"),400 format!("{prefix}{name}"),
401 filter_range,401 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 ),
403 ))406 ))
404 }407 }
405 408
crates/markdown/src/markdown.rs+3−1
@@ -1558,7 +1558,9 @@ impl MarkdownElementBuilder { · 1557 unmodified lines
1558 1558
1559 if let Some(Some(language)) = self.code_block_stack.last() {1559 if let Some(Some(language)) = self.code_block_stack.last() {
1560 let mut offset = 0;1560 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 {
1562 if range.start > offset {1564 if range.start > offset {
1563 self.pending_line1565 self.pending_line
1564 .runs1566 .runs
crates/markdown_preview/src/markdown_parser.rs+1−1
@@ -779,7 +779,7 @@ impl<'a> MarkdownParser<'a> { · 778 unmodified lines
779 779
780 let highlights = if let Some(language) = &language {780 let highlights = if let Some(language) = &language {
781 if let Some(registry) = &self.language_registry {781 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());
783 registry783 registry
784 .language_for_name_or_extension(language)784 .language_for_name_or_extension(language)
785 .await785 .await
crates/multi_buffer/src/multi_buffer_tests.rs+19−14
@@ -1,6 +1,6 @@
1use super::*;1use super::*;
2use buffer_diff::{DiffHunkStatus, DiffHunkStatusKind};2use buffer_diff::{DiffHunkStatus, DiffHunkStatusKind};
3use gpui::{App, TestAppContext};3use gpui::{App, BackgroundExecutor, TestAppContext};
4use indoc::indoc;4use indoc::indoc;
5use language::{Buffer, Rope};5use language::{Buffer, Rope};
6use parking_lot::RwLock;6use parking_lot::RwLock;
@@ -79,9 +79,14 @@ fn test_remote(cx: &mut App) { · 72 unmodified lines
79 let ops = cx79 let ops = cx
80 .background_executor()80 .background_executor()
81 .block(host_buffer.read(cx).serialize_ops(None, cx));81 .block(host_buffer.read(cx).serialize_ops(None, cx));
82 let mut buffer =82 let mut buffer = Buffer::from_proto(
83 Buffer::from_proto(ReplicaId::REMOTE_SERVER, Capability::ReadWrite, state, None)83 ReplicaId::REMOTE_SERVER,
84 .unwrap();84 Capability::ReadWrite,
85 state,
86 None,
87 cx.background_executor(),
88 )
89 .unwrap();
85 buffer.apply_ops(90 buffer.apply_ops(
86 ops.into_iter()91 ops.into_iter()
87 .map(|op| language::proto::deserialize_operation(op).unwrap()),92 .map(|op| language::proto::deserialize_operation(op).unwrap()),
@@ -1224,7 +1229,7 @@ fn test_basic_diff_hunks(cx: &mut TestAppContext) { · 1136 unmodified lines
1224 assert_chunks_in_ranges(&snapshot);1229 assert_chunks_in_ranges(&snapshot);
1225 assert_consistent_line_numbers(&snapshot);1230 assert_consistent_line_numbers(&snapshot);
1226 assert_position_translation(&snapshot);1231 assert_position_translation(&snapshot);
1227 assert_line_indents(&snapshot);1232 assert_line_indents(&snapshot, cx.background_executor());
1228 1233
1229 multibuffer.update(cx, |multibuffer, cx| {1234 multibuffer.update(cx, |multibuffer, cx| {
1230 multibuffer.collapse_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)1235 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
1248 assert_chunks_in_ranges(&snapshot);1253 assert_chunks_in_ranges(&snapshot);
1249 assert_consistent_line_numbers(&snapshot);1254 assert_consistent_line_numbers(&snapshot);
1250 assert_position_translation(&snapshot);1255 assert_position_translation(&snapshot);
1251 assert_line_indents(&snapshot);1256 assert_line_indents(&snapshot, cx.background_executor());
1252 1257
1253 // Expand the first diff hunk1258 // Expand the first diff hunk
1254 multibuffer.update(cx, |multibuffer, cx| {1259 multibuffer.update(cx, |multibuffer, cx| {
@@ -1300,7 +1305,7 @@ fn test_basic_diff_hunks(cx: &mut TestAppContext) { · 45 unmodified lines
1300 assert_chunks_in_ranges(&snapshot);1305 assert_chunks_in_ranges(&snapshot);
1301 assert_consistent_line_numbers(&snapshot);1306 assert_consistent_line_numbers(&snapshot);
1302 assert_position_translation(&snapshot);1307 assert_position_translation(&snapshot);
1303 assert_line_indents(&snapshot);1308 assert_line_indents(&snapshot, cx.background_executor());
1304 1309
1305 // Edit the buffer before the first hunk1310 // Edit the buffer before the first hunk
1306 buffer.update(cx, |buffer, cx| {1311 buffer.update(cx, |buffer, cx| {
@@ -1342,7 +1347,7 @@ fn test_basic_diff_hunks(cx: &mut TestAppContext) { · 35 unmodified lines
1342 assert_chunks_in_ranges(&snapshot);1347 assert_chunks_in_ranges(&snapshot);
1343 assert_consistent_line_numbers(&snapshot);1348 assert_consistent_line_numbers(&snapshot);
1344 assert_position_translation(&snapshot);1349 assert_position_translation(&snapshot);
1345 assert_line_indents(&snapshot);1350 assert_line_indents(&snapshot, cx.background_executor());
1346 1351
1347 // Recalculate the diff, changing the first diff hunk.1352 // Recalculate the diff, changing the first diff hunk.
1348 diff.update(cx, |diff, cx| {1353 diff.update(cx, |diff, cx| {
@@ -2067,7 +2072,7 @@ fn test_diff_hunks_with_multiple_excerpts(cx: &mut TestAppContext) { · 718 unmodified lines
2067 }2072 }
2068 2073
2069 assert_position_translation(&snapshot);2074 assert_position_translation(&snapshot);
2070 assert_line_indents(&snapshot);2075 assert_line_indents(&snapshot, cx.background_executor());
2071 2076
2072 assert_eq!(2077 assert_eq!(
2073 snapshot2078 snapshot
@@ -2118,7 +2123,7 @@ fn test_diff_hunks_with_multiple_excerpts(cx: &mut TestAppContext) { · 44 unmodified lines
2118 ),2123 ),
2119 );2124 );
2120 2125
2121 assert_line_indents(&snapshot);2126 assert_line_indents(&snapshot, cx.background_executor());
2122}2127}
2123 2128
2124/// A naive implementation of a multi-buffer that does not maintain2129/// 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
2888 );2893 );
2889 }2894 }
2890 2895
2891 let text_rope = Rope::from(expected_text.as_str());2896 let text_rope = Rope::from_str(expected_text.as_str(), cx.background_executor());
2892 for _ in 0..10 {2897 for _ in 0..10 {
2893 let end_ix = text_rope.clip_offset(rng.random_range(0..=text_rope.len()), Bias::Right);2898 let end_ix = text_rope.clip_offset(rng.random_range(0..=text_rope.len()), Bias::Right);
2894 let start_ix = text_rope.clip_offset(rng.random_range(0..=end_ix), Bias::Left);2899 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
3512 3517
3513#[track_caller]3518#[track_caller]
3514fn assert_position_translation(snapshot: &MultiBufferSnapshot) {3519fn assert_position_translation(snapshot: &MultiBufferSnapshot) {
3515 let text = Rope::from(snapshot.text());3520 let text = Rope::from_str_small(&snapshot.text());
3516 3521
3517 let mut left_anchors = Vec::new();3522 let mut left_anchors = Vec::new();
3518 let mut right_anchors = Vec::new();3523 let mut right_anchors = Vec::new();
@@ -3636,10 +3641,10 @@ fn assert_position_translation(snapshot: &MultiBufferSnapshot) { · 117 unmodified lines
3636 }3641 }
3637}3642}
3638 3643
3639fn assert_line_indents(snapshot: &MultiBufferSnapshot) {3644fn assert_line_indents(snapshot: &MultiBufferSnapshot, executor: &BackgroundExecutor) {
3640 let max_row = snapshot.max_point().row;3645 let max_row = snapshot.max_point().row;
3641 let buffer_id = snapshot.excerpts().next().unwrap().1.remote_id();3646 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);
3643 let mut line_indents = text3648 let mut line_indents = text
3644 .line_indents_in_row_range(0..max_row + 1)3649 .line_indents_in_row_range(0..max_row + 1)
3645 .collect::<Vec<_>>();3650 .collect::<Vec<_>>();
crates/project/src/buffer_store.rs+15−3
@@ -180,7 +180,13 @@ impl RemoteBufferStore { · 179 unmodified lines
180 buffer_file = Some(Arc::new(File::from_proto(file, worktree, cx)?)180 buffer_file = Some(Arc::new(File::from_proto(file, worktree, cx)?)
181 as Arc<dyn language::File>);181 as Arc<dyn language::File>);
182 }182 }
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 )
184 });190 });
185 191
186 match buffer_result {192 match buffer_result {
@@ -628,9 +634,10 @@ impl LocalBufferStore { · 441 unmodified lines
628 Ok(loaded) => {634 Ok(loaded) => {
629 let reservation = cx.reserve_entity::<Buffer>()?;635 let reservation = cx.reserve_entity::<Buffer>()?;
630 let buffer_id = BufferId::from(reservation.entity_id().as_non_zero_u64());636 let buffer_id = BufferId::from(reservation.entity_id().as_non_zero_u64());
637 let executor = cx.background_executor().clone();
631 let text_buffer = cx638 let text_buffer = cx
632 .background_spawn(async move {639 .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)
634 })641 })
635 .await;642 .await;
636 cx.insert_entity(reservation, |_| {643 cx.insert_entity(reservation, |_| {
@@ -639,7 +646,12 @@ impl LocalBufferStore { · 2 unmodified lines
639 }646 }
640 Err(error) if is_not_found_error(&error) => cx.new(|cx| {647 Err(error) if is_not_found_error(&error) => cx.new(|cx| {
641 let buffer_id = BufferId::from(cx.entity_id().as_non_zero_u64());648 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 );
643 Buffer::build(655 Buffer::build(
644 text_buffer,656 text_buffer,
645 Some(Arc::new(File {657 Some(Arc::new(File {
crates/project/src/git_store/conflict_set.rs+32−12
@@ -276,8 +276,8 @@ mod tests { · 275 unmodified lines
276 use util::{path, rel_path::rel_path};276 use util::{path, rel_path::rel_path};
277 use worktree::WorktreeSettings;277 use worktree::WorktreeSettings;
278 278
279 #[test]279 #[gpui::test]
280 fn test_parse_conflicts_in_buffer() {280 fn test_parse_conflicts_in_buffer(cx: &mut TestAppContext) {
281 // Create a buffer with conflict markers281 // Create a buffer with conflict markers
282 let test_content = r#"282 let test_content = r#"
283 This is some text before the conflict.283 This is some text before the conflict.
@@ -299,7 +299,12 @@ mod tests { · 15 unmodified lines
299 .unindent();299 .unindent();
300 300
301 let buffer_id = BufferId::new(1).unwrap();301 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 );
303 let snapshot = buffer.snapshot();308 let snapshot = buffer.snapshot();
304 309
305 let conflict_snapshot = ConflictSet::parse(&snapshot);310 let conflict_snapshot = ConflictSet::parse(&snapshot);
@@ -355,8 +360,8 @@ mod tests { · 49 unmodified lines
355 assert_eq!(conflicts_in_range.len(), 0);360 assert_eq!(conflicts_in_range.len(), 0);
356 }361 }
357 362
358 #[test]363 #[gpui::test]
359 fn test_nested_conflict_markers() {364 fn test_nested_conflict_markers(cx: &mut TestAppContext) {
360 // Create a buffer with nested conflict markers365 // Create a buffer with nested conflict markers
361 let test_content = r#"366 let test_content = r#"
362 This is some text before the conflict.367 This is some text before the conflict.
@@ -374,7 +379,12 @@ mod tests { · 11 unmodified lines
374 .unindent();379 .unindent();
375 380
376 let buffer_id = BufferId::new(1).unwrap();381 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 );
378 let snapshot = buffer.snapshot();388 let snapshot = buffer.snapshot();
379 389
380 let conflict_snapshot = ConflictSet::parse(&snapshot);390 let conflict_snapshot = ConflictSet::parse(&snapshot);
@@ -396,8 +406,8 @@ mod tests { · 15 unmodified lines
396 assert_eq!(their_text, "This is their version in a nested conflict\n");406 assert_eq!(their_text, "This is their version in a nested conflict\n");
397 }407 }
398 408
399 #[test]409 #[gpui::test]
400 fn test_conflict_markers_at_eof() {410 fn test_conflict_markers_at_eof(cx: &mut TestAppContext) {
401 let test_content = r#"411 let test_content = r#"
402 <<<<<<< ours412 <<<<<<< ours
403 =======413 =======
@@ -405,15 +415,20 @@ mod tests { · 1 unmodified line
405 >>>>>>> "#415 >>>>>>> "#
406 .unindent();416 .unindent();
407 let buffer_id = BufferId::new(1).unwrap();417 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 );
409 let snapshot = buffer.snapshot();424 let snapshot = buffer.snapshot();
410 425
411 let conflict_snapshot = ConflictSet::parse(&snapshot);426 let conflict_snapshot = ConflictSet::parse(&snapshot);
412 assert_eq!(conflict_snapshot.conflicts.len(), 1);427 assert_eq!(conflict_snapshot.conflicts.len(), 1);
413 }428 }
414 429
415 #[test]430 #[gpui::test]
416 fn test_conflicts_in_range() {431 fn test_conflicts_in_range(cx: &mut TestAppContext) {
417 // Create a buffer with conflict markers432 // Create a buffer with conflict markers
418 let test_content = r#"433 let test_content = r#"
419 one434 one
@@ -447,7 +462,12 @@ mod tests { · 27 unmodified lines
447 .unindent();462 .unindent();
448 463
449 let buffer_id = BufferId::new(1).unwrap();464 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 );
451 let snapshot = buffer.snapshot();471 let snapshot = buffer.snapshot();
452 472
453 let conflict_snapshot = ConflictSet::parse(&snapshot);473 let conflict_snapshot = ConflictSet::parse(&snapshot);
crates/project/src/prettier_store.rs+91−48
@@ -13,7 +13,9 @@ use futures::{ · 12 unmodified lines
13 future::{self, Shared},13 future::{self, Shared},
14 stream::FuturesUnordered,14 stream::FuturesUnordered,
15};15};
16use gpui::{AppContext as _, AsyncApp, Context, Entity, EventEmitter, Task, WeakEntity};16use gpui::{
17 AppContext as _, AsyncApp, BackgroundExecutor, Context, Entity, EventEmitter, Task, WeakEntity,
18};
17use language::{19use language::{
18 Buffer, LanguageRegistry, LocalFile,20 Buffer, LanguageRegistry, LocalFile,
19 language_settings::{Formatter, LanguageSettings},21 language_settings::{Formatter, LanguageSettings},
@@ -558,99 +560,137 @@ impl PrettierStore { · 538 unmodified lines
558 let plugins_to_install = new_plugins.clone();560 let plugins_to_install = new_plugins.clone();
559 let fs = Arc::clone(&self.fs);561 let fs = Arc::clone(&self.fs);
560 let new_installation_task = cx562 let new_installation_task = cx
561 .spawn(async move |prettier_store, cx| {563 .spawn(async move |prettier_store, cx| {
562 cx.background_executor().timer(Duration::from_millis(30)).await;564 cx.background_executor()
565 .timer(Duration::from_millis(30))
566 .await;
563 let location_data = prettier_store.update(cx, |prettier_store, cx| {567 let location_data = prettier_store.update(cx, |prettier_store, cx| {
564 worktree.and_then(|worktree_id| {568 worktree
565 prettier_store.worktree_store569 .and_then(|worktree_id| {
566 .read(cx)570 prettier_store
567 .worktree_for_id(worktree_id, cx)571 .worktree_store
568 .map(|worktree| worktree.read(cx).abs_path())572 .read(cx)
569 }).map(|locate_from| {573 .worktree_for_id(worktree_id, cx)
570 let installed_prettiers = prettier_store.prettier_instances.keys().cloned().collect();574 .map(|worktree| worktree.read(cx).abs_path())
571 (locate_from, installed_prettiers)575 })
572 })576 .map(|locate_from| {
577 let installed_prettiers =
578 prettier_store.prettier_instances.keys().cloned().collect();
579 (locate_from, installed_prettiers)
580 })
573 })?;581 })?;
574 let locate_prettier_installation = match location_data {582 let locate_prettier_installation = match location_data {
575 Some((locate_from, installed_prettiers)) => Prettier::locate_prettier_installation(583 Some((locate_from, installed_prettiers)) => {
576 fs.as_ref(),584 Prettier::locate_prettier_installation(
577 &installed_prettiers,585 fs.as_ref(),
578 locate_from.as_ref(),586 &installed_prettiers,
579 )587 locate_from.as_ref(),
580 .await588 )
581 .context("locate prettier installation").map_err(Arc::new)?,589 .await
590 .context("locate prettier installation")
591 .map_err(Arc::new)?
592 }
582 None => ControlFlow::Continue(None),593 None => ControlFlow::Continue(None),
583 };594 };
584 595
585 match locate_prettier_installation596 match locate_prettier_installation {
586 {
587 ControlFlow::Break(()) => return Ok(()),597 ControlFlow::Break(()) => return Ok(()),
588 ControlFlow::Continue(prettier_path) => {598 ControlFlow::Continue(prettier_path) => {
589 if prettier_path.is_some() {599 if prettier_path.is_some() {
590 new_plugins.clear();600 new_plugins.clear();
591 }601 }
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;
593 if let Some(previous_installation_task) = previous_installation_task604 if let Some(previous_installation_task) = previous_installation_task
594 && let Err(e) = previous_installation_task.await {605 && let Err(e) = previous_installation_task.await
595 log::error!("Failed to install default prettier: {e:#}");606 {
596 prettier_store.update(cx, |prettier_store, _| {607 log::error!("Failed to install default prettier: {e:#}");
597 if let PrettierInstallation::NotInstalled { attempts, not_installed_plugins, .. } = &mut prettier_store.default_prettier.prettier {608 prettier_store.update(cx, |prettier_store, _| {
598 *attempts += 1;609 if let PrettierInstallation::NotInstalled {
599 new_plugins.extend(not_installed_plugins.iter().cloned());610 attempts,
600 installation_attempt = *attempts;611 not_installed_plugins,
601 needs_install = true;612 ..
602 };613 } = &mut prettier_store.default_prettier.prettier
603 })?;614 {
604 };615 *attempts += 1;
616 new_plugins.extend(not_installed_plugins.iter().cloned());
617 installation_attempt = *attempts;
618 needs_install = true;
619 };
620 })?;
621 };
605 if installation_attempt > prettier::FAIL_THRESHOLD {622 if installation_attempt > prettier::FAIL_THRESHOLD {
606 prettier_store.update(cx, |prettier_store, _| {623 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 {
608 *installation_task = None;629 *installation_task = None;
609 };630 };
610 })?;631 })?;
611 log::warn!(632 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",
613 );635 );
614 return Ok(());636 return Ok(());
615 }637 }
616 prettier_store.update(cx, |prettier_store, _| {638 prettier_store.update(cx, |prettier_store, _| {
617 new_plugins.retain(|plugin| {639 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)
619 });644 });
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 {
621 not_installed_plugins.retain(|plugin| {650 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)
623 });655 });
624 not_installed_plugins.extend(new_plugins.iter().cloned());656 not_installed_plugins.extend(new_plugins.iter().cloned());
625 }657 }
626 needs_install |= !new_plugins.is_empty();658 needs_install |= !new_plugins.is_empty();
627 })?;659 })?;
628 if needs_install {660 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 );
630 let installed_plugins = new_plugins.clone();664 let installed_plugins = new_plugins.clone();
665 let executor = cx.background_executor().clone();
631 cx.background_spawn(async move {666 cx.background_spawn(async move {
632 install_prettier_packages(fs.as_ref(), new_plugins, node).await?;667 install_prettier_packages(fs.as_ref(), new_plugins, node).await?;
633 // Save the server file last, so the reinstall need could be determined by the absence of the file.668 // 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?;
635 anyhow::Ok(())670 anyhow::Ok(())
636 })671 })
637 .await672 .await
638 .context("prettier & plugins install")673 .context("prettier & plugins install")
639 .map_err(Arc::new)?;674 .map_err(Arc::new)?;
640 log::info!("Initialized default prettier with plugins: {installed_plugins:?}");675 log::info!(
676 "Initialized default prettier with plugins: {installed_plugins:?}"
677 );
641 prettier_store.update(cx, |prettier_store, _| {678 prettier_store.update(cx, |prettier_store, _| {
642 prettier_store.default_prettier.prettier =679 prettier_store.default_prettier.prettier =
643 PrettierInstallation::Installed(PrettierInstance {680 PrettierInstallation::Installed(PrettierInstance {
644 attempt: 0,681 attempt: 0,
645 prettier: None,682 prettier: None,
646 });683 });
647 prettier_store.default_prettier684 prettier_store
685 .default_prettier
648 .installed_plugins686 .installed_plugins
649 .extend(installed_plugins);687 .extend(installed_plugins);
650 })?;688 })?;
651 } else {689 } else {
652 prettier_store.update(cx, |prettier_store, _| {690 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 {
654 prettier_store.default_prettier.prettier =694 prettier_store.default_prettier.prettier =
655 PrettierInstallation::Installed(PrettierInstance {695 PrettierInstallation::Installed(PrettierInstance {
656 attempt: 0,696 attempt: 0,
@@ -936,11 +976,14 @@ async fn install_prettier_packages( · 279 unmodified lines
936 anyhow::Ok(())976 anyhow::Ok(())
937}977}
938 978
939async fn save_prettier_server_file(fs: &dyn Fs) -> anyhow::Result<()> {979async fn save_prettier_server_file(
980 fs: &dyn Fs,
981 executor: &BackgroundExecutor,
982) -> anyhow::Result<()> {
940 let prettier_wrapper_path = default_prettier_dir().join(prettier::PRETTIER_SERVER_FILE);983 let prettier_wrapper_path = default_prettier_dir().join(prettier::PRETTIER_SERVER_FILE);
941 fs.save(984 fs.save(
942 &prettier_wrapper_path,985 &prettier_wrapper_path,
943 &text::Rope::from(prettier::PRETTIER_SERVER_JS),986 &text::Rope::from_str(prettier::PRETTIER_SERVER_JS, executor),
944 text::LineEnding::Unix,987 text::LineEnding::Unix,
945 )988 )
946 .await989 .await
crates/project/src/project.rs+10−3
@@ -712,8 +712,10 @@ pub enum ResolveState { · 711 unmodified lines
712impl InlayHint {712impl InlayHint {
713 pub fn text(&self) -> Rope {713 pub fn text(&self) -> Rope {
714 match &self.label {714 match &self.label {
715 InlayHintLabel::String(s) => Rope::from(s),715 InlayHintLabel::String(s) => Rope::from_str_small(s),
716 InlayHintLabel::LabelParts(parts) => parts.iter().map(|part| &*part.value).collect(),716 InlayHintLabel::LabelParts(parts) => {
717 Rope::from_iter_small(parts.iter().map(|part| &*part.value))
718 }
717 }719 }
718 }720 }
719}721}
@@ -5402,7 +5404,12 @@ impl Project { · 4682 unmodified lines
5402 worktree5404 worktree
5403 .update(cx, |worktree, cx| {5405 .update(cx, |worktree, cx| {
5404 let line_ending = text::LineEnding::detect(&new_text);5406 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 )
5406 })?5413 })?
5407 .await5414 .await
5408 .context("Failed to write settings file")?;5415 .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
1461 .unwrap();1461 .unwrap();
1462 fs.save(1462 fs.save(
1463 path!("/the-root/Cargo.lock").as_ref(),1463 path!("/the-root/Cargo.lock").as_ref(),
1464 &"".into(),1464 &Rope::default(),
1465 Default::default(),1465 Default::default(),
1466 )1466 )
1467 .await1467 .await
1468 .unwrap();1468 .unwrap();
1469 fs.save(1469 fs.save(
1470 path!("/the-stdlib/LICENSE").as_ref(),1470 path!("/the-stdlib/LICENSE").as_ref(),
1471 &"".into(),1471 &Rope::default(),
1472 Default::default(),1472 Default::default(),
1473 )1473 )
1474 .await1474 .await
1475 .unwrap();1475 .unwrap();
1476 fs.save(1476 fs.save(
1477 path!("/the/stdlib/src/string.rs").as_ref(),1477 path!("/the/stdlib/src/string.rs").as_ref(),
1478 &"".into(),1478 &Rope::default(),
1479 Default::default(),1479 Default::default(),
1480 )1480 )
1481 .await1481 .await
@@ -4072,7 +4072,7 @@ async fn test_file_changes_multiple_times_on_disk(cx: &mut gpui::TestAppContext) · 2590 unmodified lines
4072 // to be detected by the worktree, so that the buffer starts reloading.4072 // to be detected by the worktree, so that the buffer starts reloading.
4073 fs.save(4073 fs.save(
4074 path!("/dir/file1").as_ref(),4074 path!("/dir/file1").as_ref(),
4075 &"the first contents".into(),4075 &Rope::from_str("the first contents", cx.background_executor()),
4076 Default::default(),4076 Default::default(),
4077 )4077 )
4078 .await4078 .await
@@ -4083,7 +4083,7 @@ async fn test_file_changes_multiple_times_on_disk(cx: &mut gpui::TestAppContext) · 4 unmodified lines
4083 // previous file change may still be in progress.4083 // previous file change may still be in progress.
4084 fs.save(4084 fs.save(
4085 path!("/dir/file1").as_ref(),4085 path!("/dir/file1").as_ref(),
4086 &"the second contents".into(),4086 &Rope::from_str("the second contents", cx.background_executor()),
4087 Default::default(),4087 Default::default(),
4088 )4088 )
4089 .await4089 .await
@@ -4127,7 +4127,7 @@ async fn test_edit_buffer_while_it_reloads(cx: &mut gpui::TestAppContext) { · 37 unmodified lines
4127 // to be detected by the worktree, so that the buffer starts reloading.4127 // to be detected by the worktree, so that the buffer starts reloading.
4128 fs.save(4128 fs.save(
4129 path!("/dir/file1").as_ref(),4129 path!("/dir/file1").as_ref(),
4130 &"the first contents".into(),4130 &Rope::from_str("the first contents", cx.background_executor()),
4131 Default::default(),4131 Default::default(),
4132 )4132 )
4133 .await4133 .await
@@ -4805,7 +4805,7 @@ async fn test_buffer_file_changes_on_disk(cx: &mut gpui::TestAppContext) { · 671 unmodified lines
4805 marked_text_offsets("oneˇ\nthree ˇFOURˇ five\nsixtyˇ seven\n");4805 marked_text_offsets("oneˇ\nthree ˇFOURˇ five\nsixtyˇ seven\n");
4806 fs.save(4806 fs.save(
4807 path!("/dir/the-file").as_ref(),4807 path!("/dir/the-file").as_ref(),
4808 &new_contents.as_str().into(),4808 &Rope::from_str(new_contents.as_str(), cx.background_executor()),
4809 LineEnding::Unix,4809 LineEnding::Unix,
4810 )4810 )
4811 .await4811 .await
@@ -4837,7 +4837,7 @@ async fn test_buffer_file_changes_on_disk(cx: &mut gpui::TestAppContext) { · 25 unmodified lines
4837 // Change the file on disk again, adding blank lines to the beginning.4837 // Change the file on disk again, adding blank lines to the beginning.
4838 fs.save(4838 fs.save(
4839 path!("/dir/the-file").as_ref(),4839 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()),
4841 LineEnding::Unix,4841 LineEnding::Unix,
4842 )4842 )
4843 .await4843 .await
@@ -4889,7 +4889,7 @@ async fn test_buffer_line_endings(cx: &mut gpui::TestAppContext) { · 45 unmodified lines
4889 // state updates correctly.4889 // state updates correctly.
4890 fs.save(4890 fs.save(
4891 path!("/dir/file1").as_ref(),4891 path!("/dir/file1").as_ref(),
4892 &"aaa\nb\nc\n".into(),4892 &Rope::from_str("aaa\nb\nc\n", cx.background_executor()),
4893 LineEnding::Windows,4893 LineEnding::Windows,
4894 )4894 )
4895 .await4895 .await
crates/remote_server/src/remote_editing_tests.rs+4−4
@@ -13,7 +13,7 @@ use fs::{FakeFs, Fs}; · 12 unmodified lines
13use gpui::{AppContext as _, Entity, SemanticVersion, SharedString, TestAppContext};13use gpui::{AppContext as _, Entity, SemanticVersion, SharedString, TestAppContext};
14use http_client::{BlockedHttpClient, FakeHttpClient};14use http_client::{BlockedHttpClient, FakeHttpClient};
15use language::{15use language::{
16 Buffer, FakeLspAdapter, LanguageConfig, LanguageMatcher, LanguageRegistry, LineEnding,16 Buffer, FakeLspAdapter, LanguageConfig, LanguageMatcher, LanguageRegistry, LineEnding, Rope,
17 language_settings::{AllLanguageSettings, language_settings},17 language_settings::{AllLanguageSettings, language_settings},
18};18};
19use lsp::{CompletionContext, CompletionResponse, CompletionTriggerKind, LanguageServerName};19use 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
120 // sees the new file.120 // sees the new file.
121 fs.save(121 fs.save(
122 path!("/code/project1/src/main.rs").as_ref(),122 path!("/code/project1/src/main.rs").as_ref(),
123 &"fn main() {}".into(),123 &Rope::from_str_small("fn main() {}"),
124 Default::default(),124 Default::default(),
125 )125 )
126 .await126 .await
@@ -766,7 +766,7 @@ async fn test_remote_reload(cx: &mut TestAppContext, server_cx: &mut TestAppCont · 639 unmodified lines
766 766
767 fs.save(767 fs.save(
768 &PathBuf::from(path!("/code/project1/src/lib.rs")),768 &PathBuf::from(path!("/code/project1/src/lib.rs")),
769 &("bangles".to_string().into()),769 &Rope::from_str_small("bangles"),
770 LineEnding::Unix,770 LineEnding::Unix,
771 )771 )
772 .await772 .await
@@ -781,7 +781,7 @@ async fn test_remote_reload(cx: &mut TestAppContext, server_cx: &mut TestAppCont · 8 unmodified lines
781 781
782 fs.save(782 fs.save(
783 &PathBuf::from(path!("/code/project1/src/lib.rs")),783 &PathBuf::from(path!("/code/project1/src/lib.rs")),
784 &("bloop".to_string().into()),784 &Rope::from_str_small("bloop"),
785 LineEnding::Unix,785 LineEnding::Unix,
786 )786 )
787 .await787 .await
crates/rich_text/src/rich_text.rs+12−5
@@ -1,9 +1,10 @@
1use futures::FutureExt;1use futures::FutureExt;
2use gpui::{2use gpui::{
3 AnyElement, AnyView, App, ElementId, FontStyle, FontWeight, HighlightStyle, InteractiveText,3 AnyElement, AnyView, App, BackgroundExecutor, ElementId, FontStyle, FontWeight, HighlightStyle,
4 IntoElement, SharedString, StrikethroughStyle, StyledText, UnderlineStyle, Window,4 InteractiveText, IntoElement, SharedString, StrikethroughStyle, StyledText, UnderlineStyle,
5 Window,
5};6};
6use language::{HighlightId, Language, LanguageRegistry};7use language::{HighlightId, Language, LanguageRegistry, Rope};
7use std::{ops::Range, sync::Arc};8use std::{ops::Range, sync::Arc};
8use theme::ActiveTheme;9use theme::ActiveTheme;
9use ui::LinkPreview;10use ui::LinkPreview;
@@ -56,6 +57,7 @@ impl RichText { · 46 unmodified lines
56 block: String,57 block: String,
57 mentions: &[Mention],58 mentions: &[Mention],
58 language_registry: &Arc<LanguageRegistry>,59 language_registry: &Arc<LanguageRegistry>,
60 executor: &BackgroundExecutor,
59 ) -> Self {61 ) -> Self {
60 let mut text = String::new();62 let mut text = String::new();
61 let mut highlights = Vec::new();63 let mut highlights = Vec::new();
@@ -70,6 +72,7 @@ impl RichText { · 8 unmodified lines
70 &mut highlights,72 &mut highlights,
71 &mut link_ranges,73 &mut link_ranges,
72 &mut link_urls,74 &mut link_urls,
75 executor,
73 );76 );
74 text.truncate(text.trim_end().len());77 text.truncate(text.trim_end().len());
75 78
@@ -184,6 +187,7 @@ pub fn render_markdown_mut( · 108 unmodified lines
184 highlights: &mut Vec<(Range<usize>, Highlight)>,187 highlights: &mut Vec<(Range<usize>, Highlight)>,
185 link_ranges: &mut Vec<Range<usize>>,188 link_ranges: &mut Vec<Range<usize>>,
186 link_urls: &mut Vec<String>,189 link_urls: &mut Vec<String>,
190 executor: &BackgroundExecutor,
187) {191) {
188 use pulldown_cmark::{CodeBlockKind, Event, Options, Parser, Tag, TagEnd};192 use pulldown_cmark::{CodeBlockKind, Event, Options, Parser, Tag, TagEnd};
189 193
@@ -202,7 +206,7 @@ pub fn render_markdown_mut( · 12 unmodified lines
202 match event {206 match event {
203 Event::Text(t) => {207 Event::Text(t) => {
204 if let Some(language) = &current_language {208 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);
206 } else {210 } else {
207 while let Some(mention) = mentions.first() {211 while let Some(mention) = mentions.first() {
208 if !source_range.contains_inclusive(&mention.range) {212 if !source_range.contains_inclusive(&mention.range) {
@@ -373,11 +377,14 @@ pub fn render_code( · 164 unmodified lines
373 highlights: &mut Vec<(Range<usize>, Highlight)>,377 highlights: &mut Vec<(Range<usize>, Highlight)>,
374 content: &str,378 content: &str,
375 language: &Arc<Language>,379 language: &Arc<Language>,
380 executor: &BackgroundExecutor,
376) {381) {
377 let prev_len = text.len();382 let prev_len = text.len();
378 text.push_str(content);383 text.push_str(content);
379 let mut offset = 0;384 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 {
381 if range.start > offset {388 if range.start > offset {
382 highlights.push((prev_len + offset..prev_len + range.start, Highlight::Code));389 highlights.push((prev_len + offset..prev_len + range.start, Highlight::Code));
383 }390 }
crates/rope/Cargo.toml+1−1
@@ -14,10 +14,10 @@ path = "src/rope.rs" · 13 unmodified lines
14[dependencies]14[dependencies]
15arrayvec = "0.7.1"15arrayvec = "0.7.1"
16log.workspace = true16log.workspace = true
17rayon.workspace = true
18sum_tree.workspace = true17sum_tree.workspace = true
19unicode-segmentation.workspace = true18unicode-segmentation.workspace = true
20util.workspace = true19util.workspace = true
20gpui.workspace = true
21 21
22[dev-dependencies]22[dev-dependencies]
23ctor.workspace = true23ctor.workspace = true
crates/rope/benches/rope_benchmark.rs+27−10
@@ -3,6 +3,7 @@ use std::ops::Range; · 2 unmodified lines
3use criterion::{3use criterion::{
4 BatchSize, BenchmarkId, Criterion, Throughput, black_box, criterion_group, criterion_main,4 BatchSize, BenchmarkId, Criterion, Throughput, black_box, criterion_group, criterion_main,
5};5};
6use gpui::{AsyncApp, TestAppContext};
6use rand::prelude::*;7use rand::prelude::*;
7use rand::rngs::StdRng;8use rand::rngs::StdRng;
8use rope::{Point, Rope};9use rope::{Point, Rope};
@@ -26,10 +27,10 @@ fn generate_random_text(rng: &mut StdRng, len: usize) -> String { · 17 unmodified lines
26 str27 str
27}28}
28 29
29fn generate_random_rope(rng: &mut StdRng, text_len: usize) -> Rope {30fn generate_random_rope(rng: &mut StdRng, text_len: usize, cx: &AsyncApp) -> Rope {
30 let text = generate_random_text(rng, text_len);31 let text = generate_random_text(rng, text_len);
31 let mut rope = Rope::new();32 let mut rope = Rope::new();
32 rope.push(&text);33 rope.push(&text, cx.background_executor());
33 rope34 rope
34}35}
35 36
@@ -82,11 +83,13 @@ fn rope_benchmarks(c: &mut Criterion) { · 46 unmodified lines
82 group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {83 group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {
83 let mut rng = StdRng::seed_from_u64(SEED);84 let mut rng = StdRng::seed_from_u64(SEED);
84 let text = generate_random_text(&mut rng, *size);85 let text = generate_random_text(&mut rng, *size);
86 let cx = TestAppContext::single();
87 let cx = cx.to_async();
85 88
86 b.iter(|| {89 b.iter(|| {
87 let mut rope = Rope::new();90 let mut rope = Rope::new();
88 for _ in 0..10 {91 for _ in 0..10 {
89 rope.push(&text);92 rope.push(&text, cx.background_executor());
90 }93 }
91 });94 });
92 });95 });
@@ -99,8 +102,10 @@ fn rope_benchmarks(c: &mut Criterion) { · 6 unmodified lines
99 group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {102 group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {
100 let mut rng = StdRng::seed_from_u64(SEED);103 let mut rng = StdRng::seed_from_u64(SEED);
101 let mut random_ropes = Vec::new();104 let mut random_ropes = Vec::new();
105 let cx = TestAppContext::single();
106 let cx = cx.to_async();
102 for _ in 0..5 {107 for _ in 0..5 {
103 let rope = generate_random_rope(&mut rng, *size);108 let rope = generate_random_rope(&mut rng, *size, &cx);
104 random_ropes.push(rope);109 random_ropes.push(rope);
105 }110 }
106 111
@@ -119,7 +124,9 @@ fn rope_benchmarks(c: &mut Criterion) { · 12 unmodified lines
119 group.throughput(Throughput::Bytes(*size as u64));124 group.throughput(Throughput::Bytes(*size as u64));
120 group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {125 group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {
121 let mut rng = StdRng::seed_from_u64(SEED);126 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);
123 130
124 b.iter_batched(131 b.iter_batched(
125 || generate_random_rope_ranges(&mut rng, &rope),132 || generate_random_rope_ranges(&mut rng, &rope),
@@ -139,7 +146,9 @@ fn rope_benchmarks(c: &mut Criterion) { · 13 unmodified lines
139 group.throughput(Throughput::Bytes(*size as u64));146 group.throughput(Throughput::Bytes(*size as u64));
140 group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {147 group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {
141 let mut rng = StdRng::seed_from_u64(SEED);148 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);
143 152
144 b.iter_batched(153 b.iter_batched(
145 || generate_random_rope_ranges(&mut rng, &rope),154 || generate_random_rope_ranges(&mut rng, &rope),
@@ -160,7 +169,9 @@ fn rope_benchmarks(c: &mut Criterion) { · 14 unmodified lines
160 group.throughput(Throughput::Bytes(*size as u64));169 group.throughput(Throughput::Bytes(*size as u64));
161 group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {170 group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {
162 let mut rng = StdRng::seed_from_u64(SEED);171 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);
164 175
165 b.iter(|| {176 b.iter(|| {
166 let chars = rope.chars().count();177 let chars = rope.chars().count();
@@ -175,7 +186,9 @@ fn rope_benchmarks(c: &mut Criterion) { · 8 unmodified lines
175 group.throughput(Throughput::Bytes(*size as u64));186 group.throughput(Throughput::Bytes(*size as u64));
176 group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {187 group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {
177 let mut rng = StdRng::seed_from_u64(SEED);188 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);
179 192
180 b.iter_batched(193 b.iter_batched(
181 || generate_random_rope_points(&mut rng, &rope),194 || generate_random_rope_points(&mut rng, &rope),
@@ -196,7 +209,9 @@ fn rope_benchmarks(c: &mut Criterion) { · 14 unmodified lines
196 group.throughput(Throughput::Bytes(*size as u64));209 group.throughput(Throughput::Bytes(*size as u64));
197 group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {210 group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {
198 let mut rng = StdRng::seed_from_u64(SEED);211 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);
200 215
201 b.iter_batched(216 b.iter_batched(
202 || generate_random_rope_points(&mut rng, &rope),217 || generate_random_rope_points(&mut rng, &rope),
@@ -216,7 +231,9 @@ fn rope_benchmarks(c: &mut Criterion) { · 13 unmodified lines
216 group.throughput(Throughput::Bytes(*size as u64));231 group.throughput(Throughput::Bytes(*size as u64));
217 group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {232 group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {
218 let mut rng = StdRng::seed_from_u64(SEED);233 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);
220 237
221 b.iter_batched(238 b.iter_batched(
222 || {239 || {
crates/rope/src/rope.rs+178−98
@@ -5,7 +5,7 @@ mod point_utf16; · 4 unmodified lines
5mod unclipped;5mod unclipped;
6 6
7use arrayvec::ArrayVec;7use arrayvec::ArrayVec;
8use rayon::iter::{IntoParallelIterator, ParallelIterator as _};8use gpui::BackgroundExecutor;
9use std::{9use std::{
10 cmp, fmt, io, mem,10 cmp, fmt, io, mem,
11 ops::{self, AddAssign, Range},11 ops::{self, AddAssign, Range},
@@ -31,6 +31,41 @@ impl Rope { · 19 unmodified lines
31 Self::default()31 Self::default()
32 }32 }
33 33
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
34 /// Checks that `index`-th byte is the first byte in a UTF-8 code point69 /// Checks that `index`-th byte is the first byte in a UTF-8 code point
35 /// sequence or the end of the string.70 /// sequence or the end of the string.
36 ///71 ///
@@ -145,12 +180,12 @@ impl Rope { · 108 unmodified lines
145 self.check_invariants();180 self.check_invariants();
146 }181 }
147 182
148 pub fn replace(&mut self, range: Range<usize>, text: &str) {183 pub fn replace(&mut self, range: Range<usize>, text: &str, executor: &BackgroundExecutor) {
149 let mut new_rope = Rope::new();184 let mut new_rope = Rope::new();
150 let mut cursor = self.cursor(0);185 let mut cursor = self.cursor(0);
151 new_rope.append(cursor.slice(range.start));186 new_rope.append(cursor.slice(range.start));
152 cursor.seek_forward(range.end);187 cursor.seek_forward(range.end);
153 new_rope.push(text);188 new_rope.push(text, executor);
154 new_rope.append(cursor.suffix());189 new_rope.append(cursor.suffix());
155 *self = new_rope;190 *self = new_rope;
156 }191 }
@@ -168,28 +203,12 @@ impl Rope { · 11 unmodified lines
168 self.slice(start..end)203 self.slice(start..end)
169 }204 }
170 205
171 pub fn push(&mut self, mut text: &str) {206 pub fn push(&mut self, mut text: &str, executor: &BackgroundExecutor) {
172 self.chunks.update_last(207 self.fill_last_chunk(&mut text);
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 };
186 208
187 let (suffix, remainder) = text.split_at(split_ix);209 if text.is_empty() {
188 last_chunk.push_str(suffix);210 return;
189 text = remainder;211 }
190 },
191 (),
192 );
193 212
194 #[cfg(all(test, not(rust_analyzer)))]213 #[cfg(all(test, not(rust_analyzer)))]
195 const NUM_CHUNKS: usize = 16;214 const NUM_CHUNKS: usize = 16;
@@ -200,7 +219,8 @@ impl Rope { · 4 unmodified lines
200 // but given the chunk boundary can land within a character219 // but given the chunk boundary can land within a character
201 // we need to accommodate for the worst case where every chunk gets cut short by up to 4 bytes220 // we need to accommodate for the worst case where every chunk gets cut short by up to 4 bytes
202 if text.len() > NUM_CHUNKS * chunk::MAX_BASE - NUM_CHUNKS * 4 {221 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);
204 }224 }
205 // 16 is enough as otherwise we will hit the branch above225 // 16 is enough as otherwise we will hit the branch above
206 let mut new_chunks = ArrayVec::<_, NUM_CHUNKS>::new();226 let mut new_chunks = ArrayVec::<_, NUM_CHUNKS>::new();
@@ -220,8 +240,57 @@ impl Rope { · 13 unmodified lines
220 self.check_invariants();240 self.check_invariants();
221 }241 }
222 242
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
223 /// A copy of `push` specialized for working with large quantities of text.292 /// 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) {
225 // To avoid frequent reallocs when loading large swaths of file contents,294 // To avoid frequent reallocs when loading large swaths of file contents,
226 // we estimate worst-case `new_chunks` capacity;295 // we estimate worst-case `new_chunks` capacity;
227 // Chunk is a fixed-capacity buffer. If a character falls on296 // Chunk is a fixed-capacity buffer. If a character falls on
@@ -254,8 +323,22 @@ impl Rope { · 26 unmodified lines
254 const PARALLEL_THRESHOLD: usize = 4 * (2 * sum_tree::TREE_BASE);323 const PARALLEL_THRESHOLD: usize = 4 * (2 * sum_tree::TREE_BASE);
255 324
256 if new_chunks.len() >= PARALLEL_THRESHOLD {325 if new_chunks.len() >= PARALLEL_THRESHOLD {
257 self.chunks326 let cx2 = executor.clone();
258 .par_extend(new_chunks.into_par_iter().map(Chunk::new), ());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;
259 } else {342 } else {
260 self.chunks343 self.chunks
261 .extend(new_chunks.into_iter().map(Chunk::new), ());344 .extend(new_chunks.into_iter().map(Chunk::new), ());
@@ -292,8 +375,13 @@ impl Rope { · 30 unmodified lines
292 }375 }
293 }376 }
294 377
295 pub fn push_front(&mut self, text: &str) {378 pub fn push_front(&mut self, text: &str, cx: &BackgroundExecutor) {
296 let suffix = mem::replace(self, Rope::from(text));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));
297 self.append(suffix);385 self.append(suffix);
298 }386 }
299 387
@@ -577,37 +665,19 @@ impl Rope { · 277 unmodified lines
577 }665 }
578}666}
579 667
580impl<'a> From<&'a str> for Rope {668// impl From<String> for Rope {
581 fn from(text: &'a str) -> Self {669// #[inline(always)]
582 let mut rope = Self::new();670// fn from(text: String) -> Self {
583 rope.push(text);671// Rope::from(text.as_str())
584 rope672// }
585 }673// }
586}
587 674
588impl<'a> FromIterator<&'a str> for Rope {675// impl From<&String> for Rope {
589 fn from_iter<T: IntoIterator<Item = &'a str>>(iter: T) -> Self {676// #[inline(always)]
590 let mut rope = Rope::new();677// fn from(text: &String) -> Self {
591 for chunk in iter {678// Rope::from(text.as_str())
592 rope.push(chunk);679// }
593 }680// }
594 rope
595 }
596}
597
598impl From<String> for Rope {
599 #[inline(always)]
600 fn from(text: String) -> Self {
601 Rope::from(text.as_str())
602 }
603}
604
605impl From<&String> for Rope {
606 #[inline(always)]
607 fn from(text: &String) -> Self {
608 Rope::from(text.as_str())
609 }
610}
611 681
612impl fmt::Display for Rope {682impl fmt::Display for Rope {
613 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {683 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@@ -1639,6 +1709,7 @@ where · 1025 unmodified lines
1639mod tests {1709mod tests {
1640 use super::*;1710 use super::*;
1641 use Bias::{Left, Right};1711 use Bias::{Left, Right};
1712 use gpui::TestAppContext;
1642 use rand::prelude::*;1713 use rand::prelude::*;
1643 use std::{cmp::Ordering, env, io::Read};1714 use std::{cmp::Ordering, env, io::Read};
1644 use util::RandomCharIter;1715 use util::RandomCharIter;
@@ -1648,17 +1719,17 @@ mod tests { · 3 unmodified lines
1648 zlog::init_test();1719 zlog::init_test();
1649 }1720 }
1650 1721
1651 #[test]1722 #[gpui::test]
1652 fn test_all_4_byte_chars() {1723 async fn test_all_4_byte_chars(cx: &mut TestAppContext) {
1653 let mut rope = Rope::new();1724 let mut rope = Rope::new();
1654 let text = "🏀".repeat(256);1725 let text = "🏀".repeat(256);
1655 rope.push(&text);1726 rope.push(&text, cx.background_executor());
1656 assert_eq!(rope.text(), text);1727 assert_eq!(rope.text(), text);
1657 }1728 }
1658 1729
1659 #[test]1730 #[gpui::test]
1660 fn test_clip() {1731 fn test_clip(cx: &mut TestAppContext) {
1661 let rope = Rope::from("🧘");1732 let rope = Rope::from_str("🧘", cx.background_executor());
1662 1733
1663 assert_eq!(rope.clip_offset(1, Bias::Left), 0);1734 assert_eq!(rope.clip_offset(1, Bias::Left), 0);
1664 assert_eq!(rope.clip_offset(1, Bias::Right), 4);1735 assert_eq!(rope.clip_offset(1, Bias::Right), 4);
@@ -1704,9 +1775,9 @@ mod tests { · 39 unmodified lines
1704 );1775 );
1705 }1776 }
1706 1777
1707 #[test]1778 #[gpui::test]
1708 fn test_prev_next_line() {1779 fn test_prev_next_line(cx: &mut TestAppContext) {
1709 let rope = Rope::from("abc\ndef\nghi\njkl");1780 let rope = Rope::from_str("abc\ndef\nghi\njkl", cx.background_executor());
1710 1781
1711 let mut chunks = rope.chunks();1782 let mut chunks = rope.chunks();
1712 assert_eq!(chunks.peek().unwrap().chars().next().unwrap(), 'a');1783 assert_eq!(chunks.peek().unwrap().chars().next().unwrap(), 'a');
@@ -1748,16 +1819,16 @@ mod tests { · 35 unmodified lines
1748 assert_eq!(chunks.peek(), None);1819 assert_eq!(chunks.peek(), None);
1749 }1820 }
1750 1821
1751 #[test]1822 #[gpui::test]
1752 fn test_lines() {1823 fn test_lines(cx: &mut TestAppContext) {
1753 let rope = Rope::from("abc\ndefg\nhi");1824 let rope = Rope::from_str("abc\ndefg\nhi", cx.background_executor());
1754 let mut lines = rope.chunks().lines();1825 let mut lines = rope.chunks().lines();
1755 assert_eq!(lines.next(), Some("abc"));1826 assert_eq!(lines.next(), Some("abc"));
1756 assert_eq!(lines.next(), Some("defg"));1827 assert_eq!(lines.next(), Some("defg"));
1757 assert_eq!(lines.next(), Some("hi"));1828 assert_eq!(lines.next(), Some("hi"));
1758 assert_eq!(lines.next(), None);1829 assert_eq!(lines.next(), None);
1759 1830
1760 let rope = Rope::from("abc\ndefg\nhi\n");1831 let rope = Rope::from_str("abc\ndefg\nhi\n", cx.background_executor());
1761 let mut lines = rope.chunks().lines();1832 let mut lines = rope.chunks().lines();
1762 assert_eq!(lines.next(), Some("abc"));1833 assert_eq!(lines.next(), Some("abc"));
1763 assert_eq!(lines.next(), Some("defg"));1834 assert_eq!(lines.next(), Some("defg"));
@@ -1765,14 +1836,14 @@ mod tests { · 1 unmodified line
1765 assert_eq!(lines.next(), Some(""));1836 assert_eq!(lines.next(), Some(""));
1766 assert_eq!(lines.next(), None);1837 assert_eq!(lines.next(), None);
1767 1838
1768 let rope = Rope::from("abc\ndefg\nhi");1839 let rope = Rope::from_str("abc\ndefg\nhi", cx.background_executor());
1769 let mut lines = rope.reversed_chunks_in_range(0..rope.len()).lines();1840 let mut lines = rope.reversed_chunks_in_range(0..rope.len()).lines();
1770 assert_eq!(lines.next(), Some("hi"));1841 assert_eq!(lines.next(), Some("hi"));
1771 assert_eq!(lines.next(), Some("defg"));1842 assert_eq!(lines.next(), Some("defg"));
1772 assert_eq!(lines.next(), Some("abc"));1843 assert_eq!(lines.next(), Some("abc"));
1773 assert_eq!(lines.next(), None);1844 assert_eq!(lines.next(), None);
1774 1845
1775 let rope = Rope::from("abc\ndefg\nhi\n");1846 let rope = Rope::from_str("abc\ndefg\nhi\n", cx.background_executor());
1776 let mut lines = rope.reversed_chunks_in_range(0..rope.len()).lines();1847 let mut lines = rope.reversed_chunks_in_range(0..rope.len()).lines();
1777 assert_eq!(lines.next(), Some(""));1848 assert_eq!(lines.next(), Some(""));
1778 assert_eq!(lines.next(), Some("hi"));1849 assert_eq!(lines.next(), Some("hi"));
@@ -1780,14 +1851,14 @@ mod tests { · 1 unmodified line
1780 assert_eq!(lines.next(), Some("abc"));1851 assert_eq!(lines.next(), Some("abc"));
1781 assert_eq!(lines.next(), None);1852 assert_eq!(lines.next(), None);
1782 1853
1783 let rope = Rope::from("abc\nlonger line test\nhi");1854 let rope = Rope::from_str("abc\nlonger line test\nhi", cx.background_executor());
1784 let mut lines = rope.chunks().lines();1855 let mut lines = rope.chunks().lines();
1785 assert_eq!(lines.next(), Some("abc"));1856 assert_eq!(lines.next(), Some("abc"));
1786 assert_eq!(lines.next(), Some("longer line test"));1857 assert_eq!(lines.next(), Some("longer line test"));
1787 assert_eq!(lines.next(), Some("hi"));1858 assert_eq!(lines.next(), Some("hi"));
1788 assert_eq!(lines.next(), None);1859 assert_eq!(lines.next(), None);
1789 1860
1790 let rope = Rope::from("abc\nlonger line test\nhi");1861 let rope = Rope::from_str("abc\nlonger line test\nhi", cx.background_executor());
1791 let mut lines = rope.reversed_chunks_in_range(0..rope.len()).lines();1862 let mut lines = rope.reversed_chunks_in_range(0..rope.len()).lines();
1792 assert_eq!(lines.next(), Some("hi"));1863 assert_eq!(lines.next(), Some("hi"));
1793 assert_eq!(lines.next(), Some("longer line test"));1864 assert_eq!(lines.next(), Some("longer line test"));
@@ -1796,7 +1867,7 @@ mod tests { · 2 unmodified lines
1796 }1867 }
1797 1868
1798 #[gpui::test(iterations = 100)]1869 #[gpui::test(iterations = 100)]
1799 fn test_random_rope(mut rng: StdRng) {1870 async fn test_random_rope(cx: &mut TestAppContext, mut rng: StdRng) {
1800 let operations = env::var("OPERATIONS")1871 let operations = env::var("OPERATIONS")
1801 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))1872 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
1802 .unwrap_or(10);1873 .unwrap_or(10);
@@ -1812,7 +1883,7 @@ mod tests { · 9 unmodified lines
1812 let mut new_actual = Rope::new();1883 let mut new_actual = Rope::new();
1813 let mut cursor = actual.cursor(0);1884 let mut cursor = actual.cursor(0);
1814 new_actual.append(cursor.slice(start_ix));1885 new_actual.append(cursor.slice(start_ix));
1815 new_actual.push(&new_text);1886 new_actual.push(&new_text, cx.background_executor());
1816 cursor.seek_forward(end_ix);1887 cursor.seek_forward(end_ix);
1817 new_actual.append(cursor.suffix());1888 new_actual.append(cursor.suffix());
1818 actual = new_actual;1889 actual = new_actual;
@@ -2112,10 +2183,10 @@ mod tests { · 293 unmodified lines
2112 }2183 }
2113 }2184 }
2114 2185
2115 #[test]2186 #[gpui::test]
2116 fn test_chunks_equals_str() {2187 fn test_chunks_equals_str(cx: &mut TestAppContext) {
2117 let text = "This is a multi-chunk\n& multi-line test string!";2188 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());
2119 for start in 0..text.len() {2190 for start in 0..text.len() {
2120 for end in start..text.len() {2191 for end in start..text.len() {
2121 let range = start..end;2192 let range = start..end;
@@ -2158,34 +2229,37 @@ mod tests { · 36 unmodified lines
2158 }2229 }
2159 }2230 }
2160 2231
2161 let rope = Rope::from("");2232 let rope = Rope::from_str("", cx.background_executor());
2162 assert!(rope.chunks_in_range(0..0).equals_str(""));2233 assert!(rope.chunks_in_range(0..0).equals_str(""));
2163 assert!(rope.reversed_chunks_in_range(0..0).equals_str(""));2234 assert!(rope.reversed_chunks_in_range(0..0).equals_str(""));
2164 assert!(!rope.chunks_in_range(0..0).equals_str("foo"));2235 assert!(!rope.chunks_in_range(0..0).equals_str("foo"));
2165 assert!(!rope.reversed_chunks_in_range(0..0).equals_str("foo"));2236 assert!(!rope.reversed_chunks_in_range(0..0).equals_str("foo"));
2166 }2237 }
2167 2238
2168 #[test]2239 #[gpui::test]
2169 fn test_is_char_boundary() {2240 fn test_is_char_boundary(cx: &mut TestAppContext) {
2170 let fixture = "地";2241 let fixture = "地";
2171 let rope = Rope::from("地");2242 let rope = Rope::from_str("地", cx.background_executor());
2172 for b in 0..=fixture.len() {2243 for b in 0..=fixture.len() {
2173 assert_eq!(rope.is_char_boundary(b), fixture.is_char_boundary(b));2244 assert_eq!(rope.is_char_boundary(b), fixture.is_char_boundary(b));
2174 }2245 }
2175 let fixture = "";2246 let fixture = "";
2176 let rope = Rope::from("");2247 let rope = Rope::from_str("", cx.background_executor());
2177 for b in 0..=fixture.len() {2248 for b in 0..=fixture.len() {
2178 assert_eq!(rope.is_char_boundary(b), fixture.is_char_boundary(b));2249 assert_eq!(rope.is_char_boundary(b), fixture.is_char_boundary(b));
2179 }2250 }
2180 let fixture = "🔴🟠🟡🟢🔵🟣⚫️⚪️🟤\n🏳️‍⚧️🏁🏳️‍🌈🏴‍☠️⛳️📬📭🏴🏳️🚩";2251 let fixture = "🔴🟠🟡🟢🔵🟣⚫️⚪️🟤\n🏳️‍⚧️🏁🏳️‍🌈🏴‍☠️⛳️📬📭🏴🏳️🚩";
2181 let rope = Rope::from("🔴🟠🟡🟢🔵🟣⚫️⚪️🟤\n🏳️‍⚧️🏁🏳️‍🌈🏴‍☠️⛳️📬📭🏴🏳️🚩");2252 let rope = Rope::from_str(
2253 "🔴🟠🟡🟢🔵🟣⚫️⚪️🟤\n🏳️‍⚧️🏁🏳️‍🌈🏴‍☠️⛳️📬📭🏴🏳️🚩",
2254 cx.background_executor(),
2255 );
2182 for b in 0..=fixture.len() {2256 for b in 0..=fixture.len() {
2183 assert_eq!(rope.is_char_boundary(b), fixture.is_char_boundary(b));2257 assert_eq!(rope.is_char_boundary(b), fixture.is_char_boundary(b));
2184 }2258 }
2185 }2259 }
2186 2260
2187 #[test]2261 #[gpui::test]
2188 fn test_floor_char_boundary() {2262 fn test_floor_char_boundary(cx: &mut TestAppContext) {
2189 // polyfill of str::floor_char_boundary2263 // polyfill of str::floor_char_boundary
2190 fn floor_char_boundary(str: &str, index: usize) -> usize {2264 fn floor_char_boundary(str: &str, index: usize) -> usize {
2191 if index >= str.len() {2265 if index >= str.len() {
@@ -2201,7 +2275,7 @@ mod tests { · 9 unmodified lines
2201 }2275 }
2202 2276
2203 let fixture = "地";2277 let fixture = "地";
2204 let rope = Rope::from("地");2278 let rope = Rope::from_str("地", cx.background_executor());
2205 for b in 0..=fixture.len() {2279 for b in 0..=fixture.len() {
2206 assert_eq!(2280 assert_eq!(
2207 rope.floor_char_boundary(b),2281 rope.floor_char_boundary(b),
@@ -2210,7 +2284,7 @@ mod tests { · 2 unmodified lines
2210 }2284 }
2211 2285
2212 let fixture = "";2286 let fixture = "";
2213 let rope = Rope::from("");2287 let rope = Rope::from_str("", cx.background_executor());
2214 for b in 0..=fixture.len() {2288 for b in 0..=fixture.len() {
2215 assert_eq!(2289 assert_eq!(
2216 rope.floor_char_boundary(b),2290 rope.floor_char_boundary(b),
@@ -2219,7 +2293,10 @@ mod tests { · 2 unmodified lines
2219 }2293 }
2220 2294
2221 let fixture = "🔴🟠🟡🟢🔵🟣⚫️⚪️🟤\n🏳️‍⚧️🏁🏳️‍🌈🏴‍☠️⛳️📬📭🏴🏳️🚩";2295 let fixture = "🔴🟠🟡🟢🔵🟣⚫️⚪️🟤\n🏳️‍⚧️🏁🏳️‍🌈🏴‍☠️⛳️📬📭🏴🏳️🚩";
2222 let rope = Rope::from("🔴🟠🟡🟢🔵🟣⚫️⚪️🟤\n🏳️‍⚧️🏁🏳️‍🌈🏴‍☠️⛳️📬📭🏴🏳️🚩");2296 let rope = Rope::from_str(
2297 "🔴🟠🟡🟢🔵🟣⚫️⚪️🟤\n🏳️‍⚧️🏁🏳️‍🌈🏴‍☠️⛳️📬📭🏴🏳️🚩",
2298 cx.background_executor(),
2299 );
2223 for b in 0..=fixture.len() {2300 for b in 0..=fixture.len() {
2224 assert_eq!(2301 assert_eq!(
2225 rope.floor_char_boundary(b),2302 rope.floor_char_boundary(b),
@@ -2228,8 +2305,8 @@ mod tests { · 2 unmodified lines
2228 }2305 }
2229 }2306 }
2230 2307
2231 #[test]2308 #[gpui::test]
2232 fn test_ceil_char_boundary() {2309 fn test_ceil_char_boundary(cx: &mut TestAppContext) {
2233 // polyfill of str::ceil_char_boundary2310 // polyfill of str::ceil_char_boundary
2234 fn ceil_char_boundary(str: &str, index: usize) -> usize {2311 fn ceil_char_boundary(str: &str, index: usize) -> usize {
2235 if index > str.len() {2312 if index > str.len() {
@@ -2244,19 +2321,22 @@ mod tests { · 8 unmodified lines
2244 }2321 }
2245 2322
2246 let fixture = "地";2323 let fixture = "地";
2247 let rope = Rope::from("地");2324 let rope = Rope::from_str("地", cx.background_executor());
2248 for b in 0..=fixture.len() {2325 for b in 0..=fixture.len() {
2249 assert_eq!(rope.ceil_char_boundary(b), ceil_char_boundary(&fixture, b));2326 assert_eq!(rope.ceil_char_boundary(b), ceil_char_boundary(&fixture, b));
2250 }2327 }
2251 2328
2252 let fixture = "";2329 let fixture = "";
2253 let rope = Rope::from("");2330 let rope = Rope::from_str("", cx.background_executor());
2254 for b in 0..=fixture.len() {2331 for b in 0..=fixture.len() {
2255 assert_eq!(rope.ceil_char_boundary(b), ceil_char_boundary(&fixture, b));2332 assert_eq!(rope.ceil_char_boundary(b), ceil_char_boundary(&fixture, b));
2256 }2333 }
2257 2334
2258 let fixture = "🔴🟠🟡🟢🔵🟣⚫️⚪️🟤\n🏳️‍⚧️🏁🏳️‍🌈🏴‍☠️⛳️📬📭🏴🏳️🚩";2335 let fixture = "🔴🟠🟡🟢🔵🟣⚫️⚪️🟤\n🏳️‍⚧️🏁🏳️‍🌈🏴‍☠️⛳️📬📭🏴🏳️🚩";
2259 let rope = Rope::from("🔴🟠🟡🟢🔵🟣⚫️⚪️🟤\n🏳️‍⚧️🏁🏳️‍🌈🏴‍☠️⛳️📬📭🏴🏳️🚩");2336 let rope = Rope::from_str(
2337 "🔴🟠🟡🟢🔵🟣⚫️⚪️🟤\n🏳️‍⚧️🏁🏳️‍🌈🏴‍☠️⛳️📬📭🏴🏳️🚩",
2338 cx.background_executor(),
2339 );
2260 for b in 0..=fixture.len() {2340 for b in 0..=fixture.len() {
2261 assert_eq!(rope.ceil_char_boundary(b), ceil_char_boundary(&fixture, b));2341 assert_eq!(rope.ceil_char_boundary(b), ceil_char_boundary(&fixture, b));
2262 }2342 }
crates/rules_library/src/rules_library.rs+8−2
@@ -554,7 +554,7 @@ impl RulesLibrary { · 553 unmodified lines
554 554
555 let prompt_id = PromptId::new();555 let prompt_id = PromptId::new();
556 let save = self.store.update(cx, |store, cx| {556 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)
558 });558 });
559 self.picker559 self.picker
560 .update(cx, |picker, cx| picker.refresh(window, cx));560 .update(cx, |picker, cx| picker.refresh(window, cx));
@@ -888,7 +888,13 @@ impl RulesLibrary { · 327 unmodified lines
888 let new_id = PromptId::new();888 let new_id = PromptId::new();
889 let body = rule.body_editor.read(cx).text(cx);889 let body = rule.body_editor.read(cx).text(cx);
890 let save = self.store.update(cx, |store, cx| {890 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 )
892 });898 });
893 self.picker899 self.picker
894 .update(cx, |picker, cx| picker.refresh(window, cx));900 .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
14[dependencies]14[dependencies]
15ordered-float.workspace = true15ordered-float.workspace = true
16rope.workspace = true16rope.workspace = true
17gpui.workspace = true
17 18
18[dev-dependencies]19[dev-dependencies]
19rand.workspace = true20rand.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
503#[cfg(test)]503#[cfg(test)]
504mod tests {504mod tests {
505 use super::*;505 use super::*;
506 use gpui::BackgroundExecutor;
506 use rand::prelude::*;507 use rand::prelude::*;
507 use std::env;508 use std::env;
508 509
509 #[test]510 #[gpui::test]
510 fn test_delete_first_of_two_lines() {511 fn test_delete_first_of_two_lines(cx: &mut gpui::TestAppContext) {
511 let old_text = "aaaa\nbbbb";512 let old_text = "aaaa\nbbbb";
512 let char_ops = vec![513 let char_ops = vec![
513 CharOperation::Delete { bytes: 5 },514 CharOperation::Delete { bytes: 5 },
@@ -523,18 +524,18 @@ mod tests { · 9 unmodified lines
523 apply_line_operations(old_text, &new_text, &expected_line_ops)524 apply_line_operations(old_text, &new_text, &expected_line_ops)
524 );525 );
525 526
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());
527 assert_eq!(line_ops, expected_line_ops);528 assert_eq!(line_ops, expected_line_ops);
528 }529 }
529 530
530 #[test]531 #[gpui::test]
531 fn test_delete_second_of_two_lines() {532 fn test_delete_second_of_two_lines(cx: &mut gpui::TestAppContext) {
532 let old_text = "aaaa\nbbbb";533 let old_text = "aaaa\nbbbb";
533 let char_ops = vec![534 let char_ops = vec![
534 CharOperation::Keep { bytes: 5 },535 CharOperation::Keep { bytes: 5 },
535 CharOperation::Delete { bytes: 4 },536 CharOperation::Delete { bytes: 4 },
536 ];537 ];
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());
538 assert_eq!(539 assert_eq!(
539 line_ops,540 line_ops,
540 vec![541 vec![
@@ -550,8 +551,8 @@ mod tests { · 9 unmodified lines
550 );551 );
551 }552 }
552 553
553 #[test]554 #[gpui::test]
554 fn test_add_new_line() {555 fn test_add_new_line(cx: &mut gpui::TestAppContext) {
555 let old_text = "aaaa\nbbbb";556 let old_text = "aaaa\nbbbb";
556 let char_ops = vec![557 let char_ops = vec![
557 CharOperation::Keep { bytes: 9 },558 CharOperation::Keep { bytes: 9 },
@@ -559,7 +560,7 @@ mod tests { · 1 unmodified line
559 text: "\ncccc".into(),560 text: "\ncccc".into(),
560 },561 },
561 ];562 ];
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());
563 assert_eq!(564 assert_eq!(
564 line_ops,565 line_ops,
565 vec![566 vec![
@@ -574,15 +575,15 @@ mod tests { · 8 unmodified lines
574 );575 );
575 }576 }
576 577
577 #[test]578 #[gpui::test]
578 fn test_delete_line_in_middle() {579 fn test_delete_line_in_middle(cx: &mut gpui::TestAppContext) {
579 let old_text = "aaaa\nbbbb\ncccc";580 let old_text = "aaaa\nbbbb\ncccc";
580 let char_ops = vec![581 let char_ops = vec![
581 CharOperation::Keep { bytes: 5 },582 CharOperation::Keep { bytes: 5 },
582 CharOperation::Delete { bytes: 5 },583 CharOperation::Delete { bytes: 5 },
583 CharOperation::Keep { bytes: 4 },584 CharOperation::Keep { bytes: 4 },
584 ];585 ];
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());
586 assert_eq!(587 assert_eq!(
587 line_ops,588 line_ops,
588 vec![589 vec![
@@ -598,8 +599,8 @@ mod tests { · 9 unmodified lines
598 );599 );
599 }600 }
600 601
601 #[test]602 #[gpui::test]
602 fn test_replace_line() {603 fn test_replace_line(cx: &mut gpui::TestAppContext) {
603 let old_text = "aaaa\nbbbb\ncccc";604 let old_text = "aaaa\nbbbb\ncccc";
604 let char_ops = vec![605 let char_ops = vec![
605 CharOperation::Keep { bytes: 5 },606 CharOperation::Keep { bytes: 5 },
@@ -609,7 +610,7 @@ mod tests { · 3 unmodified lines
609 },610 },
610 CharOperation::Keep { bytes: 5 },611 CharOperation::Keep { bytes: 5 },
611 ];612 ];
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());
613 assert_eq!(614 assert_eq!(
614 line_ops,615 line_ops,
615 vec![616 vec![
@@ -626,8 +627,8 @@ mod tests { · 10 unmodified lines
626 );627 );
627 }628 }
628 629
629 #[test]630 #[gpui::test]
630 fn test_multiple_edits_on_different_lines() {631 fn test_multiple_edits_on_different_lines(cx: &mut gpui::TestAppContext) {
631 let old_text = "aaaa\nbbbb\ncccc\ndddd";632 let old_text = "aaaa\nbbbb\ncccc\ndddd";
632 let char_ops = vec![633 let char_ops = vec![
633 CharOperation::Insert { text: "A".into() },634 CharOperation::Insert { text: "A".into() },
@@ -638,7 +639,7 @@ mod tests { · 4 unmodified lines
638 text: "\nEEEE".into(),639 text: "\nEEEE".into(),
639 },640 },
640 ];641 ];
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());
642 assert_eq!(643 assert_eq!(
643 line_ops,644 line_ops,
644 vec![645 vec![
@@ -656,15 +657,15 @@ mod tests { · 11 unmodified lines
656 );657 );
657 }658 }
658 659
659 #[test]660 #[gpui::test]
660 fn test_edit_at_end_of_line() {661 fn test_edit_at_end_of_line(cx: &mut gpui::TestAppContext) {
661 let old_text = "aaaa\nbbbb\ncccc";662 let old_text = "aaaa\nbbbb\ncccc";
662 let char_ops = vec![663 let char_ops = vec![
663 CharOperation::Keep { bytes: 4 },664 CharOperation::Keep { bytes: 4 },
664 CharOperation::Insert { text: "A".into() },665 CharOperation::Insert { text: "A".into() },
665 CharOperation::Keep { bytes: 10 },666 CharOperation::Keep { bytes: 10 },
666 ];667 ];
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());
668 assert_eq!(669 assert_eq!(
669 line_ops,670 line_ops,
670 vec![671 vec![
@@ -680,8 +681,8 @@ mod tests { · 9 unmodified lines
680 );681 );
681 }682 }
682 683
683 #[test]684 #[gpui::test]
684 fn test_insert_newline_character() {685 fn test_insert_newline_character(cx: &mut gpui::TestAppContext) {
685 let old_text = "aaaabbbb";686 let old_text = "aaaabbbb";
686 let char_ops = vec![687 let char_ops = vec![
687 CharOperation::Keep { bytes: 4 },688 CharOperation::Keep { bytes: 4 },
@@ -689,7 +690,7 @@ mod tests { · 1 unmodified line
689 CharOperation::Keep { bytes: 4 },690 CharOperation::Keep { bytes: 4 },
690 ];691 ];
691 let new_text = apply_char_operations(old_text, &char_ops);692 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());
693 assert_eq!(694 assert_eq!(
694 line_ops,695 line_ops,
695 vec![696 vec![
@@ -703,14 +704,14 @@ mod tests { · 7 unmodified lines
703 );704 );
704 }705 }
705 706
706 #[test]707 #[gpui::test]
707 fn test_insert_newline_at_beginning() {708 fn test_insert_newline_at_beginning(cx: &mut gpui::TestAppContext) {
708 let old_text = "aaaa\nbbbb";709 let old_text = "aaaa\nbbbb";
709 let char_ops = vec![710 let char_ops = vec![
710 CharOperation::Insert { text: "\n".into() },711 CharOperation::Insert { text: "\n".into() },
711 CharOperation::Keep { bytes: 9 },712 CharOperation::Keep { bytes: 9 },
712 ];713 ];
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());
714 assert_eq!(715 assert_eq!(
715 line_ops,716 line_ops,
716 vec![717 vec![
@@ -725,15 +726,15 @@ mod tests { · 8 unmodified lines
725 );726 );
726 }727 }
727 728
728 #[test]729 #[gpui::test]
729 fn test_delete_newline() {730 fn test_delete_newline(cx: &mut gpui::TestAppContext) {
730 let old_text = "aaaa\nbbbb";731 let old_text = "aaaa\nbbbb";
731 let char_ops = vec![732 let char_ops = vec![
732 CharOperation::Keep { bytes: 4 },733 CharOperation::Keep { bytes: 4 },
733 CharOperation::Delete { bytes: 1 },734 CharOperation::Delete { bytes: 1 },
734 CharOperation::Keep { bytes: 4 },735 CharOperation::Keep { bytes: 4 },
735 ];736 ];
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());
737 assert_eq!(738 assert_eq!(
738 line_ops,739 line_ops,
739 vec![740 vec![
@@ -749,8 +750,8 @@ mod tests { · 9 unmodified lines
749 );750 );
750 }751 }
751 752
752 #[test]753 #[gpui::test]
753 fn test_insert_multiple_newlines() {754 fn test_insert_multiple_newlines(cx: &mut gpui::TestAppContext) {
754 let old_text = "aaaa\nbbbb";755 let old_text = "aaaa\nbbbb";
755 let char_ops = vec![756 let char_ops = vec![
756 CharOperation::Keep { bytes: 5 },757 CharOperation::Keep { bytes: 5 },
@@ -759,7 +760,7 @@ mod tests { · 2 unmodified lines
759 },760 },
760 CharOperation::Keep { bytes: 4 },761 CharOperation::Keep { bytes: 4 },
761 ];762 ];
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());
763 assert_eq!(764 assert_eq!(
764 line_ops,765 line_ops,
765 vec![766 vec![
@@ -775,15 +776,15 @@ mod tests { · 9 unmodified lines
775 );776 );
776 }777 }
777 778
778 #[test]779 #[gpui::test]
779 fn test_delete_multiple_newlines() {780 fn test_delete_multiple_newlines(cx: &mut gpui::TestAppContext) {
780 let old_text = "aaaa\n\n\nbbbb";781 let old_text = "aaaa\n\n\nbbbb";
781 let char_ops = vec![782 let char_ops = vec![
782 CharOperation::Keep { bytes: 5 },783 CharOperation::Keep { bytes: 5 },
783 CharOperation::Delete { bytes: 2 },784 CharOperation::Delete { bytes: 2 },
784 CharOperation::Keep { bytes: 4 },785 CharOperation::Keep { bytes: 4 },
785 ];786 ];
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());
787 assert_eq!(788 assert_eq!(
788 line_ops,789 line_ops,
789 vec![790 vec![
@@ -799,8 +800,8 @@ mod tests { · 9 unmodified lines
799 );800 );
800 }801 }
801 802
802 #[test]803 #[gpui::test]
803 fn test_complex_scenario() {804 fn test_complex_scenario(cx: &mut gpui::TestAppContext) {
804 let old_text = "line1\nline2\nline3\nline4";805 let old_text = "line1\nline2\nline3\nline4";
805 let char_ops = vec![806 let char_ops = vec![
806 CharOperation::Keep { bytes: 6 },807 CharOperation::Keep { bytes: 6 },
@@ -814,7 +815,7 @@ mod tests { · 7 unmodified lines
814 },815 },
815 CharOperation::Keep { bytes: 6 },816 CharOperation::Keep { bytes: 6 },
816 ];817 ];
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());
818 assert_eq!(819 assert_eq!(
819 line_ops,820 line_ops,
820 vec![821 vec![
@@ -834,8 +835,8 @@ mod tests { · 13 unmodified lines
834 );835 );
835 }836 }
836 837
837 #[test]838 #[gpui::test]
838 fn test_cleaning_up_common_suffix() {839 fn test_cleaning_up_common_suffix(cx: &mut gpui::TestAppContext) {
839 let old_text = concat!(840 let old_text = concat!(
840 " for y in 0..size.y() {\n",841 " for y in 0..size.y() {\n",
841 " let a = 10;\n",842 " let a = 10;\n",
@@ -883,7 +884,7 @@ mod tests { · 41 unmodified lines
883 },884 },
884 CharOperation::Keep { bytes: 1 },885 CharOperation::Keep { bytes: 1 },
885 ];886 ];
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());
887 assert_eq!(888 assert_eq!(
888 line_ops,889 line_ops,
889 vec![890 vec![
@@ -901,8 +902,8 @@ mod tests { · 11 unmodified lines
901 );902 );
902 }903 }
903 904
904 #[test]905 #[gpui::test]
905 fn test_random_diffs() {906 fn test_random_diffs(cx: &mut gpui::TestAppContext) {
906 random_test(|mut rng| {907 random_test(|mut rng| {
907 let old_text_len = env::var("OLD_TEXT_LEN")908 let old_text_len = env::var("OLD_TEXT_LEN")
908 .map(|i| i.parse().expect("invalid `OLD_TEXT_LEN` variable"))909 .map(|i| i.parse().expect("invalid `OLD_TEXT_LEN` variable"))
@@ -922,15 +923,19 @@ mod tests { · 13 unmodified lines
922 assert_eq!(patched, new);923 assert_eq!(patched, new);
923 924
924 // Test char_ops_to_line_ops925 // 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());
926 println!("line operations: {:?}", line_ops);927 println!("line operations: {:?}", line_ops);
927 let patched = apply_line_operations(&old, &new, &line_ops);928 let patched = apply_line_operations(&old, &new, &line_ops);
928 assert_eq!(patched, new);929 assert_eq!(patched, new);
929 });930 });
930 }931 }
931 932
932 fn char_ops_to_line_ops(old_text: &str, char_ops: &[CharOperation]) -> Vec<LineOperation> {933 fn char_ops_to_line_ops(
933 let old_rope = Rope::from(old_text);934 old_text: &str,
935 char_ops: &[CharOperation],
936 executor: &BackgroundExecutor,
937 ) -> Vec<LineOperation> {
938 let old_rope = Rope::from_str(old_text, executor);
934 let mut diff = LineDiff::default();939 let mut diff = LineDiff::default();
935 for op in char_ops {940 for op in char_ops {
936 diff.push_char_operation(op, &old_rope);941 diff.push_char_operation(op, &old_rope);
crates/sum_tree/Cargo.toml+3−1
@@ -15,10 +15,12 @@ doctest = false · 14 unmodified lines
15 15
16[dependencies]16[dependencies]
17arrayvec = "0.7.1"17arrayvec = "0.7.1"
18rayon.workspace = true
19log.workspace = true18log.workspace = true
19futures.workspace = true
20itertools.workspace = true
20 21
21[dev-dependencies]22[dev-dependencies]
22ctor.workspace = true23ctor.workspace = true
23rand.workspace = true24rand.workspace = true
24zlog.workspace = true25zlog.workspace = true
26pollster = "0.4.0"
crates/sum_tree/src/sum_tree.rs+85−46
@@ -3,7 +3,8 @@ mod tree_map; · 2 unmodified lines
3 3
4use arrayvec::ArrayVec;4use arrayvec::ArrayVec;
5pub use cursor::{Cursor, FilterCursor, Iter};5pub use cursor::{Cursor, FilterCursor, Iter};
6use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator as _};6use futures::{StreamExt, stream};
7use itertools::Itertools as _;
7use std::marker::PhantomData;8use std::marker::PhantomData;
8use std::mem;9use std::mem;
9use std::{cmp::Ordering, fmt, iter::FromIterator, sync::Arc};10use std::{cmp::Ordering, fmt, iter::FromIterator, sync::Arc};
@@ -14,6 +15,18 @@ pub const TREE_BASE: usize = 2; · 4 unmodified lines
14#[cfg(not(test))]15#[cfg(not(test))]
15pub const TREE_BASE: usize = 6;16pub const TREE_BASE: usize = 6;
16 17
18pub 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
17/// An item that can be stored in a [`SumTree`]30/// An item that can be stored in a [`SumTree`]
18///31///
19/// Must be summarized by a type that implements [`Summary`]32/// Must be summarized by a type that implements [`Summary`]
@@ -298,62 +311,71 @@ impl<T: Item> SumTree<T> { · 278 unmodified lines
298 }311 }
299 }312 }
300 313
301 pub fn from_par_iter<I, Iter>(iter: I, cx: <T::Summary as Summary>::Context<'_>) -> Self314 pub async fn from_iter_async<I, S>(iter: I, spawn: S) -> Self
302 where315 where
303 I: IntoParallelIterator<Iter = Iter>,316 T: 'static + Send + Sync,
304 Iter: IndexedParallelIterator<Item = T>,317 for<'a> T::Summary: Summary<Context<'a> = ()> + Send + Sync,
305 T: Send + Sync,318 S: BackgroundSpawn,
306 T::Summary: Send + Sync,319 I: IntoIterator<Item = T>,
307 for<'a> <T::Summary as Summary>::Context<'a>: Sync,
308 {320 {
309 let mut nodes = iter321 let mut futures = vec![];
310 .into_par_iter()322 let chunks = iter.into_iter().chunks(2 * TREE_BASE);
311 .chunks(2 * TREE_BASE)323 for chunk in chunks.into_iter() {
312 .map(|items| {324 let items: ArrayVec<T, { 2 * TREE_BASE }> = chunk.into_iter().collect();
313 let items: ArrayVec<T, { 2 * TREE_BASE }> = items.into_iter().collect();325 futures.push(async move {
314 let item_summaries: ArrayVec<T::Summary, { 2 * TREE_BASE }> =326 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();
316 let mut summary = item_summaries[0].clone();328 let mut summary = item_summaries[0].clone();
317 for item_summary in &item_summaries[1..] {329 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, ());
319 }331 }
320 SumTree(Arc::new(Node::Leaf {332 SumTree(Arc::new(Node::Leaf {
321 summary,333 summary,
322 items,334 items,
323 item_summaries,335 item_summaries,
324 }))336 }))
325 })337 });
326 .collect::<Vec<_>>();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;
327 345
328 let mut height = 0;346 let mut height = 0;
329 while nodes.len() > 1 {347 while nodes.len() > 1 {
330 height += 1;348 height += 1;
331 nodes = nodes349 let current_nodes = mem::take(&mut nodes);
332 .into_par_iter()350 nodes = stream::iter(current_nodes)
333 .chunks(2 * TREE_BASE)351 .chunks(2 * TREE_BASE)
334 .map(|child_nodes| {352 .map(|chunk| {
335 let child_trees: ArrayVec<SumTree<T>, { 2 * TREE_BASE }> =353 spawn.background_spawn(async move {
336 child_nodes.into_iter().collect();354 let child_trees: ArrayVec<SumTree<T>, { 2 * TREE_BASE }> =
337 let child_summaries: ArrayVec<T::Summary, { 2 * TREE_BASE }> = child_trees355 chunk.into_iter().collect();
338 .iter()356 let child_summaries: ArrayVec<T::Summary, { 2 * TREE_BASE }> = child_trees
339 .map(|child_tree| child_tree.summary().clone())357 .iter()
340 .collect();358 .map(|child_tree| child_tree.summary().clone())
341 let mut summary = child_summaries[0].clone();359 .collect();
342 for child_summary in &child_summaries[1..] {360 let mut summary = child_summaries[0].clone();
343 <T::Summary as Summary>::add_summary(&mut summary, child_summary, cx);361 for child_summary in &child_summaries[1..] {
344 }362 <T::Summary as Summary>::add_summary(&mut summary, child_summary, ());
345 SumTree(Arc::new(Node::Internal {363 }
346 height,364 SumTree(Arc::new(Node::Internal {
347 summary,365 height,
348 child_summaries,366 summary,
349 child_trees,367 child_summaries,
350 }))368 child_trees,
369 }))
370 })
351 })371 })
352 .collect::<Vec<_>>();372 .buffered(4)
373 .collect::<Vec<_>>()
374 .await;
353 }375 }
354 376
355 if nodes.is_empty() {377 if nodes.is_empty() {
356 Self::new(cx)378 Self::new(())
357 } else {379 } else {
358 debug_assert_eq!(nodes.len(), 1);380 debug_assert_eq!(nodes.len(), 1);
359 nodes.pop().unwrap()381 nodes.pop().unwrap()
@@ -597,15 +619,15 @@ impl<T: Item> SumTree<T> { · 237 unmodified lines
597 self.append(Self::from_iter(iter, cx), cx);619 self.append(Self::from_iter(iter, cx), cx);
598 }620 }
599 621
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)
601 where623 where
602 I: IntoParallelIterator<Iter = Iter>,624 S: BackgroundSpawn,
603 Iter: IndexedParallelIterator<Item = T>,625 I: IntoIterator<Item = T> + 'static,
604 T: Send + Sync,626 T: 'static + Send + Sync,
605 T::Summary: Send + Sync,627 for<'b> T::Summary: Summary<Context<'b> = ()> + Send + Sync,
606 for<'a> <T::Summary as Summary>::Context<'a>: Sync,
607 {628 {
608 self.append(Self::from_par_iter(iter, cx), cx);629 let other = Self::from_iter_async(iter, spawn);
630 self.append(other.await, ());
609 }631 }
610 632
611 pub fn push(&mut self, item: T, cx: <T::Summary as Summary>::Context<'_>) {633 pub fn push(&mut self, item: T, cx: <T::Summary as Summary>::Context<'_>) {
@@ -1070,6 +1092,23 @@ mod tests { · 458 unmodified lines
1070 1092
1071 #[test]1093 #[test]
1072 fn test_random() {1094 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
1073 let mut starting_seed = 0;1112 let mut starting_seed = 0;
1074 if let Ok(value) = std::env::var("SEED") {1113 if let Ok(value) = std::env::var("SEED") {
1075 starting_seed = value.parse().expect("invalid SEED variable");1114 starting_seed = value.parse().expect("invalid SEED variable");
@@ -1095,7 +1134,7 @@ mod tests { · 19 unmodified lines
1095 .sample_iter(StandardUniform)1134 .sample_iter(StandardUniform)
1096 .take(count)1135 .take(count)
1097 .collect::<Vec<_>>();1136 .collect::<Vec<_>>();
1098 tree.par_extend(items, ());1137 pollster::block_on(tree.async_extend(items, NoSpawn));
1099 }1138 }
1100 1139
1101 for _ in 0..num_operations {1140 for _ in 0..num_operations {
@@ -1117,7 +1156,7 @@ mod tests { · 15 unmodified lines
1117 if rng.random() {1156 if rng.random() {
1118 new_tree.extend(new_items, ());1157 new_tree.extend(new_items, ());
1119 } else {1158 } else {
1120 new_tree.par_extend(new_items, ());1159 pollster::block_on(new_tree.async_extend(new_items, NoSpawn));
1121 }1160 }
1122 cursor.seek(&Count(splice_end), Bias::Right);1161 cursor.seek(&Count(splice_end), Bias::Right);
1123 new_tree.append(cursor.slice(&tree_end, Bias::Right), ());1162 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
28smallvec.workspace = true28smallvec.workspace = true
29sum_tree.workspace = true29sum_tree.workspace = true
30util.workspace = true30util.workspace = true
31gpui.workspace = true
31 32
32[dev-dependencies]33[dev-dependencies]
33collections = { workspace = true, features = ["test-support"] }34collections = { workspace = true, features = ["test-support"] }
crates/text/src/tests.rs+184−97
@@ -14,24 +14,29 @@ fn init_logger() { · 13 unmodified lines
14 zlog::init_test();14 zlog::init_test();
15}15}
16 16
17#[test]17#[gpui::test]
18fn test_edit() {18fn test_edit(cx: &mut gpui::TestAppContext) {
19 let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "abc");19 let mut buffer = Buffer::new(
20 ReplicaId::LOCAL,
21 BufferId::new(1).unwrap(),
22 "abc",
23 cx.background_executor(),
24 );
20 assert_eq!(buffer.text(), "abc");25 assert_eq!(buffer.text(), "abc");
21 buffer.edit([(3..3, "def")]);26 buffer.edit([(3..3, "def")], cx.background_executor());
22 assert_eq!(buffer.text(), "abcdef");27 assert_eq!(buffer.text(), "abcdef");
23 buffer.edit([(0..0, "ghi")]);28 buffer.edit([(0..0, "ghi")], cx.background_executor());
24 assert_eq!(buffer.text(), "ghiabcdef");29 assert_eq!(buffer.text(), "ghiabcdef");
25 buffer.edit([(5..5, "jkl")]);30 buffer.edit([(5..5, "jkl")], cx.background_executor());
26 assert_eq!(buffer.text(), "ghiabjklcdef");31 assert_eq!(buffer.text(), "ghiabjklcdef");
27 buffer.edit([(6..7, "")]);32 buffer.edit([(6..7, "")], cx.background_executor());
28 assert_eq!(buffer.text(), "ghiabjlcdef");33 assert_eq!(buffer.text(), "ghiabjlcdef");
29 buffer.edit([(4..9, "mno")]);34 buffer.edit([(4..9, "mno")], cx.background_executor());
30 assert_eq!(buffer.text(), "ghiamnoef");35 assert_eq!(buffer.text(), "ghiamnoef");
31}36}
32 37
33#[gpui::test(iterations = 100)]38#[gpui::test(iterations = 100)]
34fn test_random_edits(mut rng: StdRng) {39fn test_random_edits(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
35 let operations = env::var("OPERATIONS")40 let operations = env::var("OPERATIONS")
36 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))41 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
37 .unwrap_or(10);42 .unwrap_or(10);
@@ -44,6 +49,7 @@ fn test_random_edits(mut rng: StdRng) { · 6 unmodified lines
44 ReplicaId::LOCAL,49 ReplicaId::LOCAL,
45 BufferId::new(1).unwrap(),50 BufferId::new(1).unwrap(),
46 reference_string.clone(),51 reference_string.clone(),
52 cx.background_executor(),
47 );53 );
48 LineEnding::normalize(&mut reference_string);54 LineEnding::normalize(&mut reference_string);
49 55
@@ -56,7 +62,7 @@ fn test_random_edits(mut rng: StdRng) { · 6 unmodified lines
56 );62 );
57 63
58 for _i in 0..operations {64 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());
60 for (old_range, new_text) in edits.iter().rev() {66 for (old_range, new_text) in edits.iter().rev() {
61 reference_string.replace_range(old_range.clone(), new_text);67 reference_string.replace_range(old_range.clone(), new_text);
62 }68 }
@@ -106,7 +112,11 @@ fn test_random_edits(mut rng: StdRng) { · 43 unmodified lines
106 let mut text = old_buffer.visible_text.clone();112 let mut text = old_buffer.visible_text.clone();
107 for edit in edits {113 for edit in edits {
108 let new_text: String = buffer.text_for_range(edit.new.clone()).collect();114 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 );
110 }120 }
111 assert_eq!(text.to_string(), buffer.text());121 assert_eq!(text.to_string(), buffer.text());
112 122
@@ -161,14 +171,18 @@ fn test_random_edits(mut rng: StdRng) { · 48 unmodified lines
161 let mut text = old_buffer.visible_text.clone();171 let mut text = old_buffer.visible_text.clone();
162 for edit in subscription_edits.into_inner() {172 for edit in subscription_edits.into_inner() {
163 let new_text: String = buffer.text_for_range(edit.new.clone()).collect();173 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 );
165 }179 }
166 assert_eq!(text.to_string(), buffer.text());180 assert_eq!(text.to_string(), buffer.text());
167 }181 }
168}182}
169 183
170#[test]184#[gpui::test]
171fn test_line_endings() {185fn test_line_endings(cx: &mut gpui::TestAppContext) {
172 assert_eq!(LineEnding::detect(&"🍐✅\n".repeat(1000)), LineEnding::Unix);186 assert_eq!(LineEnding::detect(&"🍐✅\n".repeat(1000)), LineEnding::Unix);
173 assert_eq!(LineEnding::detect(&"abcd\n".repeat(1000)), LineEnding::Unix);187 assert_eq!(LineEnding::detect(&"abcd\n".repeat(1000)), LineEnding::Unix);
174 assert_eq!(188 assert_eq!(
@@ -184,25 +198,34 @@ fn test_line_endings() { · 9 unmodified lines
184 ReplicaId::LOCAL,198 ReplicaId::LOCAL,
185 BufferId::new(1).unwrap(),199 BufferId::new(1).unwrap(),
186 "one\r\ntwo\rthree",200 "one\r\ntwo\rthree",
201 cx.background_executor(),
187 );202 );
188 assert_eq!(buffer.text(), "one\ntwo\nthree");203 assert_eq!(buffer.text(), "one\ntwo\nthree");
189 assert_eq!(buffer.line_ending(), LineEnding::Windows);204 assert_eq!(buffer.line_ending(), LineEnding::Windows);
190 buffer.check_invariants();205 buffer.check_invariants();
191 206
192 buffer.edit([(buffer.len()..buffer.len(), "\r\nfour")]);207 buffer.edit(
193 buffer.edit([(0..0, "zero\r\n")]);208 [(buffer.len()..buffer.len(), "\r\nfour")],
209 cx.background_executor(),
210 );
211 buffer.edit([(0..0, "zero\r\n")], cx.background_executor());
194 assert_eq!(buffer.text(), "zero\none\ntwo\nthree\nfour");212 assert_eq!(buffer.text(), "zero\none\ntwo\nthree\nfour");
195 assert_eq!(buffer.line_ending(), LineEnding::Windows);213 assert_eq!(buffer.line_ending(), LineEnding::Windows);
196 buffer.check_invariants();214 buffer.check_invariants();
197}215}
198 216
199#[test]217#[gpui::test]
200fn test_line_len() {218fn test_line_len(cx: &mut gpui::TestAppContext) {
201 let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "");219 let mut buffer = Buffer::new(
202 buffer.edit([(0..0, "abcd\nefg\nhij")]);220 ReplicaId::LOCAL,
203 buffer.edit([(12..12, "kl\nmno")]);221 BufferId::new(1).unwrap(),
204 buffer.edit([(18..18, "\npqrs\n")]);222 "",
205 buffer.edit([(18..21, "\nPQ")]);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());
206 229
207 assert_eq!(buffer.line_len(0), 4);230 assert_eq!(buffer.line_len(0), 4);
208 assert_eq!(buffer.line_len(1), 3);231 assert_eq!(buffer.line_len(1), 3);
@@ -212,10 +235,15 @@ fn test_line_len() { · 3 unmodified lines
212 assert_eq!(buffer.line_len(5), 0);235 assert_eq!(buffer.line_len(5), 0);
213}236}
214 237
215#[test]238#[gpui::test]
216fn test_common_prefix_at_position() {239fn test_common_prefix_at_position(cx: &mut gpui::TestAppContext) {
217 let text = "a = str; b = δα";240 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 );
219 247
220 let offset1 = offset_after(text, "str");248 let offset1 = offset_after(text, "str");
221 let offset2 = offset_after(text, "δα");249 let offset2 = offset_after(text, "δα");
@@ -261,12 +289,13 @@ fn test_common_prefix_at_position() { · 39 unmodified lines
261 }289 }
262}290}
263 291
264#[test]292#[gpui::test]
265fn test_text_summary_for_range() {293fn test_text_summary_for_range(cx: &mut gpui::TestAppContext) {
266 let buffer = Buffer::new(294 let buffer = Buffer::new(
267 ReplicaId::LOCAL,295 ReplicaId::LOCAL,
268 BufferId::new(1).unwrap(),296 BufferId::new(1).unwrap(),
269 "ab\nefg\nhklm\nnopqrs\ntuvwxyz",297 "ab\nefg\nhklm\nnopqrs\ntuvwxyz",
298 cx.background_executor(),
270 );299 );
271 assert_eq!(300 assert_eq!(
272 buffer.text_summary_for_range::<TextSummary, _>(0..2),301 buffer.text_summary_for_range::<TextSummary, _>(0..2),
@@ -354,13 +383,18 @@ fn test_text_summary_for_range() { · 81 unmodified lines
354 );383 );
355}384}
356 385
357#[test]386#[gpui::test]
358fn test_chars_at() {387fn test_chars_at(cx: &mut gpui::TestAppContext) {
359 let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "");388 let mut buffer = Buffer::new(
360 buffer.edit([(0..0, "abcd\nefgh\nij")]);389 ReplicaId::LOCAL,
361 buffer.edit([(12..12, "kl\nmno")]);390 BufferId::new(1).unwrap(),
362 buffer.edit([(18..18, "\npqrs")]);391 "",
363 buffer.edit([(18..21, "\nPQ")]);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());
364 398
365 let chars = buffer.chars_at(Point::new(0, 0));399 let chars = buffer.chars_at(Point::new(0, 0));
366 assert_eq!(chars.collect::<String>(), "abcd\nefgh\nijkl\nmno\nPQrs");400 assert_eq!(chars.collect::<String>(), "abcd\nefgh\nijkl\nmno\nPQrs");
@@ -378,43 +412,53 @@ fn test_chars_at() { · 11 unmodified lines
378 assert_eq!(chars.collect::<String>(), "PQrs");412 assert_eq!(chars.collect::<String>(), "PQrs");
379 413
380 // Regression test:414 // Regression test:
381 let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "");415 let mut buffer = Buffer::new(
382 buffer.edit([(0..0, "[workspace]\nmembers = [\n \"xray_core\",\n \"xray_server\",\n \"xray_cli\",\n \"xray_wasm\",\n]\n")]);416 ReplicaId::LOCAL,
383 buffer.edit([(60..60, "\n")]);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());
384 423
385 let chars = buffer.chars_at(Point::new(6, 0));424 let chars = buffer.chars_at(Point::new(6, 0));
386 assert_eq!(chars.collect::<String>(), " \"xray_wasm\",\n]\n");425 assert_eq!(chars.collect::<String>(), " \"xray_wasm\",\n]\n");
387}426}
388 427
389#[test]428#[gpui::test]
390fn test_anchors() {429fn test_anchors(cx: &mut gpui::TestAppContext) {
391 let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "");430 let mut buffer = Buffer::new(
392 buffer.edit([(0..0, "abc")]);431 ReplicaId::LOCAL,
432 BufferId::new(1).unwrap(),
433 "",
434 cx.background_executor(),
435 );
436 buffer.edit([(0..0, "abc")], cx.background_executor());
393 let left_anchor = buffer.anchor_before(2);437 let left_anchor = buffer.anchor_before(2);
394 let right_anchor = buffer.anchor_after(2);438 let right_anchor = buffer.anchor_after(2);
395 439
396 buffer.edit([(1..1, "def\n")]);440 buffer.edit([(1..1, "def\n")], cx.background_executor());
397 assert_eq!(buffer.text(), "adef\nbc");441 assert_eq!(buffer.text(), "adef\nbc");
398 assert_eq!(left_anchor.to_offset(&buffer), 6);442 assert_eq!(left_anchor.to_offset(&buffer), 6);
399 assert_eq!(right_anchor.to_offset(&buffer), 6);443 assert_eq!(right_anchor.to_offset(&buffer), 6);
400 assert_eq!(left_anchor.to_point(&buffer), Point { row: 1, column: 1 });444 assert_eq!(left_anchor.to_point(&buffer), Point { row: 1, column: 1 });
401 assert_eq!(right_anchor.to_point(&buffer), Point { row: 1, column: 1 });445 assert_eq!(right_anchor.to_point(&buffer), Point { row: 1, column: 1 });
402 446
403 buffer.edit([(2..3, "")]);447 buffer.edit([(2..3, "")], cx.background_executor());
404 assert_eq!(buffer.text(), "adf\nbc");448 assert_eq!(buffer.text(), "adf\nbc");
405 assert_eq!(left_anchor.to_offset(&buffer), 5);449 assert_eq!(left_anchor.to_offset(&buffer), 5);
406 assert_eq!(right_anchor.to_offset(&buffer), 5);450 assert_eq!(right_anchor.to_offset(&buffer), 5);
407 assert_eq!(left_anchor.to_point(&buffer), Point { row: 1, column: 1 });451 assert_eq!(left_anchor.to_point(&buffer), Point { row: 1, column: 1 });
408 assert_eq!(right_anchor.to_point(&buffer), Point { row: 1, column: 1 });452 assert_eq!(right_anchor.to_point(&buffer), Point { row: 1, column: 1 });
409 453
410 buffer.edit([(5..5, "ghi\n")]);454 buffer.edit([(5..5, "ghi\n")], cx.background_executor());
411 assert_eq!(buffer.text(), "adf\nbghi\nc");455 assert_eq!(buffer.text(), "adf\nbghi\nc");
412 assert_eq!(left_anchor.to_offset(&buffer), 5);456 assert_eq!(left_anchor.to_offset(&buffer), 5);
413 assert_eq!(right_anchor.to_offset(&buffer), 9);457 assert_eq!(right_anchor.to_offset(&buffer), 9);
414 assert_eq!(left_anchor.to_point(&buffer), Point { row: 1, column: 1 });458 assert_eq!(left_anchor.to_point(&buffer), Point { row: 1, column: 1 });
415 assert_eq!(right_anchor.to_point(&buffer), Point { row: 2, column: 0 });459 assert_eq!(right_anchor.to_point(&buffer), Point { row: 2, column: 0 });
416 460
417 buffer.edit([(7..9, "")]);461 buffer.edit([(7..9, "")], cx.background_executor());
418 assert_eq!(buffer.text(), "adf\nbghc");462 assert_eq!(buffer.text(), "adf\nbghc");
419 assert_eq!(left_anchor.to_offset(&buffer), 5);463 assert_eq!(left_anchor.to_offset(&buffer), 5);
420 assert_eq!(right_anchor.to_offset(&buffer), 7);464 assert_eq!(right_anchor.to_offset(&buffer), 7);
@@ -504,13 +548,18 @@ fn test_anchors() { · 83 unmodified lines
504 );548 );
505}549}
506 550
507#[test]551#[gpui::test]
508fn test_anchors_at_start_and_end() {552fn test_anchors_at_start_and_end(cx: &mut gpui::TestAppContext) {
509 let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "");553 let mut buffer = Buffer::new(
554 ReplicaId::LOCAL,
555 BufferId::new(1).unwrap(),
556 "",
557 cx.background_executor(),
558 );
510 let before_start_anchor = buffer.anchor_before(0);559 let before_start_anchor = buffer.anchor_before(0);
511 let after_end_anchor = buffer.anchor_after(0);560 let after_end_anchor = buffer.anchor_after(0);
512 561
513 buffer.edit([(0..0, "abc")]);562 buffer.edit([(0..0, "abc")], cx.background_executor());
514 assert_eq!(buffer.text(), "abc");563 assert_eq!(buffer.text(), "abc");
515 assert_eq!(before_start_anchor.to_offset(&buffer), 0);564 assert_eq!(before_start_anchor.to_offset(&buffer), 0);
516 assert_eq!(after_end_anchor.to_offset(&buffer), 3);565 assert_eq!(after_end_anchor.to_offset(&buffer), 3);
@@ -518,8 +567,8 @@ fn test_anchors_at_start_and_end() { · 1 unmodified line
518 let after_start_anchor = buffer.anchor_after(0);567 let after_start_anchor = buffer.anchor_after(0);
519 let before_end_anchor = buffer.anchor_before(3);568 let before_end_anchor = buffer.anchor_before(3);
520 569
521 buffer.edit([(3..3, "def")]);570 buffer.edit([(3..3, "def")], cx.background_executor());
522 buffer.edit([(0..0, "ghi")]);571 buffer.edit([(0..0, "ghi")], cx.background_executor());
523 assert_eq!(buffer.text(), "ghiabcdef");572 assert_eq!(buffer.text(), "ghiabcdef");
524 assert_eq!(before_start_anchor.to_offset(&buffer), 0);573 assert_eq!(before_start_anchor.to_offset(&buffer), 0);
525 assert_eq!(after_start_anchor.to_offset(&buffer), 3);574 assert_eq!(after_start_anchor.to_offset(&buffer), 3);
@@ -527,15 +576,20 @@ fn test_anchors_at_start_and_end() { · 1 unmodified line
527 assert_eq!(after_end_anchor.to_offset(&buffer), 9);576 assert_eq!(after_end_anchor.to_offset(&buffer), 9);
528}577}
529 578
530#[test]579#[gpui::test]
531fn test_undo_redo() {580fn test_undo_redo(cx: &mut gpui::TestAppContext) {
532 let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "1234");581 let mut buffer = Buffer::new(
582 ReplicaId::LOCAL,
583 BufferId::new(1).unwrap(),
584 "1234",
585 cx.background_executor(),
586 );
533 // Set group interval to zero so as to not group edits in the undo stack.587 // Set group interval to zero so as to not group edits in the undo stack.
534 buffer.set_group_interval(Duration::from_secs(0));588 buffer.set_group_interval(Duration::from_secs(0));
535 589
536 buffer.edit([(1..1, "abx")]);590 buffer.edit([(1..1, "abx")], cx.background_executor());
537 buffer.edit([(3..4, "yzef")]);591 buffer.edit([(3..4, "yzef")], cx.background_executor());
538 buffer.edit([(3..5, "cd")]);592 buffer.edit([(3..5, "cd")], cx.background_executor());
539 assert_eq!(buffer.text(), "1abcdef234");593 assert_eq!(buffer.text(), "1abcdef234");
540 594
541 let entries = buffer.history.undo_stack.clone();595 let entries = buffer.history.undo_stack.clone();
@@ -563,26 +617,31 @@ fn test_undo_redo() { · 21 unmodified lines
563 assert_eq!(buffer.text(), "1234");617 assert_eq!(buffer.text(), "1234");
564}618}
565 619
566#[test]620#[gpui::test]
567fn test_history() {621fn test_history(cx: &mut gpui::TestAppContext) {
568 let mut now = Instant::now();622 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 );
570 buffer.set_group_interval(Duration::from_millis(300));629 buffer.set_group_interval(Duration::from_millis(300));
571 630
572 let transaction_1 = buffer.start_transaction_at(now).unwrap();631 let transaction_1 = buffer.start_transaction_at(now).unwrap();
573 buffer.edit([(2..4, "cd")]);632 buffer.edit([(2..4, "cd")], cx.background_executor());
574 buffer.end_transaction_at(now);633 buffer.end_transaction_at(now);
575 assert_eq!(buffer.text(), "12cd56");634 assert_eq!(buffer.text(), "12cd56");
576 635
577 buffer.start_transaction_at(now);636 buffer.start_transaction_at(now);
578 buffer.edit([(4..5, "e")]);637 buffer.edit([(4..5, "e")], cx.background_executor());
579 buffer.end_transaction_at(now).unwrap();638 buffer.end_transaction_at(now).unwrap();
580 assert_eq!(buffer.text(), "12cde6");639 assert_eq!(buffer.text(), "12cde6");
581 640
582 now += buffer.transaction_group_interval() + Duration::from_millis(1);641 now += buffer.transaction_group_interval() + Duration::from_millis(1);
583 buffer.start_transaction_at(now);642 buffer.start_transaction_at(now);
584 buffer.edit([(0..1, "a")]);643 buffer.edit([(0..1, "a")], cx.background_executor());
585 buffer.edit([(1..1, "b")]);644 buffer.edit([(1..1, "b")], cx.background_executor());
586 buffer.end_transaction_at(now).unwrap();645 buffer.end_transaction_at(now).unwrap();
587 assert_eq!(buffer.text(), "ab2cde6");646 assert_eq!(buffer.text(), "ab2cde6");
588 647
@@ -609,7 +668,7 @@ fn test_history() { · 20 unmodified lines
609 668
610 // Redo stack gets cleared after performing an edit.669 // Redo stack gets cleared after performing an edit.
611 buffer.start_transaction_at(now);670 buffer.start_transaction_at(now);
612 buffer.edit([(0..0, "X")]);671 buffer.edit([(0..0, "X")], cx.background_executor());
613 buffer.end_transaction_at(now);672 buffer.end_transaction_at(now);
614 assert_eq!(buffer.text(), "X12cde6");673 assert_eq!(buffer.text(), "X12cde6");
615 buffer.redo();674 buffer.redo();
@@ -630,26 +689,31 @@ fn test_history() { · 14 unmodified lines
630 assert_eq!(buffer.text(), "X12cde6");689 assert_eq!(buffer.text(), "X12cde6");
631}690}
632 691
633#[test]692#[gpui::test]
634fn test_finalize_last_transaction() {693fn test_finalize_last_transaction(cx: &mut gpui::TestAppContext) {
635 let now = Instant::now();694 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 );
637 buffer.history.group_interval = Duration::from_millis(1);701 buffer.history.group_interval = Duration::from_millis(1);
638 702
639 buffer.start_transaction_at(now);703 buffer.start_transaction_at(now);
640 buffer.edit([(2..4, "cd")]);704 buffer.edit([(2..4, "cd")], cx.background_executor());
641 buffer.end_transaction_at(now);705 buffer.end_transaction_at(now);
642 assert_eq!(buffer.text(), "12cd56");706 assert_eq!(buffer.text(), "12cd56");
643 707
644 buffer.finalize_last_transaction();708 buffer.finalize_last_transaction();
645 buffer.start_transaction_at(now);709 buffer.start_transaction_at(now);
646 buffer.edit([(4..5, "e")]);710 buffer.edit([(4..5, "e")], cx.background_executor());
647 buffer.end_transaction_at(now).unwrap();711 buffer.end_transaction_at(now).unwrap();
648 assert_eq!(buffer.text(), "12cde6");712 assert_eq!(buffer.text(), "12cde6");
649 713
650 buffer.start_transaction_at(now);714 buffer.start_transaction_at(now);
651 buffer.edit([(0..1, "a")]);715 buffer.edit([(0..1, "a")], cx.background_executor());
652 buffer.edit([(1..1, "b")]);716 buffer.edit([(1..1, "b")], cx.background_executor());
653 buffer.end_transaction_at(now).unwrap();717 buffer.end_transaction_at(now).unwrap();
654 assert_eq!(buffer.text(), "ab2cde6");718 assert_eq!(buffer.text(), "ab2cde6");
655 719
@@ -666,14 +730,19 @@ fn test_finalize_last_transaction() { · 10 unmodified lines
666 assert_eq!(buffer.text(), "ab2cde6");730 assert_eq!(buffer.text(), "ab2cde6");
667}731}
668 732
669#[test]733#[gpui::test]
670fn test_edited_ranges_for_transaction() {734fn test_edited_ranges_for_transaction(cx: &mut gpui::TestAppContext) {
671 let now = Instant::now();735 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 );
673 742
674 buffer.start_transaction_at(now);743 buffer.start_transaction_at(now);
675 buffer.edit([(2..4, "cd")]);744 buffer.edit([(2..4, "cd")], cx.background_executor());
676 buffer.edit([(6..6, "efg")]);745 buffer.edit([(6..6, "efg")], cx.background_executor());
677 buffer.end_transaction_at(now);746 buffer.end_transaction_at(now);
678 assert_eq!(buffer.text(), "12cd56efg7");747 assert_eq!(buffer.text(), "12cd56efg7");
679 748
@@ -685,7 +754,7 @@ fn test_edited_ranges_for_transaction() { · 5 unmodified lines
685 [2..4, 6..9]754 [2..4, 6..9]
686 );755 );
687 756
688 buffer.edit([(5..5, "hijk")]);757 buffer.edit([(5..5, "hijk")], cx.background_executor());
689 assert_eq!(buffer.text(), "12cd5hijk6efg7");758 assert_eq!(buffer.text(), "12cd5hijk6efg7");
690 assert_eq!(759 assert_eq!(
691 buffer760 buffer
@@ -694,7 +763,7 @@ fn test_edited_ranges_for_transaction() { · 2 unmodified lines
694 [2..4, 10..13]763 [2..4, 10..13]
695 );764 );
696 765
697 buffer.edit([(4..4, "l")]);766 buffer.edit([(4..4, "l")], cx.background_executor());
698 assert_eq!(buffer.text(), "12cdl5hijk6efg7");767 assert_eq!(buffer.text(), "12cdl5hijk6efg7");
699 assert_eq!(768 assert_eq!(
700 buffer769 buffer
@@ -704,27 +773,42 @@ fn test_edited_ranges_for_transaction() { · 3 unmodified lines
704 );773 );
705}774}
706 775
707#[test]776#[gpui::test]
708fn test_concurrent_edits() {777fn test_concurrent_edits(cx: &mut gpui::TestAppContext) {
709 let text = "abcdef";778 let text = "abcdef";
710 779
711 let mut buffer1 = Buffer::new(ReplicaId::new(1), BufferId::new(1).unwrap(), text);780 let mut buffer1 = Buffer::new(
712 let mut buffer2 = Buffer::new(ReplicaId::new(2), BufferId::new(1).unwrap(), text);781 ReplicaId::new(1),
713 let mut buffer3 = Buffer::new(ReplicaId::new(3), BufferId::new(1).unwrap(), text);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 );
714 798
715 let buf1_op = buffer1.edit([(1..2, "12")]);799 let buf1_op = buffer1.edit([(1..2, "12")], cx.background_executor());
716 assert_eq!(buffer1.text(), "a12cdef");800 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());
718 assert_eq!(buffer2.text(), "abc34ef");802 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());
720 assert_eq!(buffer3.text(), "abcde56");804 assert_eq!(buffer3.text(), "abcde56");
721 805
722 buffer1.apply_op(buf2_op.clone());806 buffer1.apply_op(buf2_op.clone(), Some(cx.background_executor()));
723 buffer1.apply_op(buf3_op.clone());807 buffer1.apply_op(buf3_op.clone(), Some(cx.background_executor()));
724 buffer2.apply_op(buf1_op.clone());808 buffer2.apply_op(buf1_op.clone(), Some(cx.background_executor()));
725 buffer2.apply_op(buf3_op);809 buffer2.apply_op(buf3_op, Some(cx.background_executor()));
726 buffer3.apply_op(buf1_op);810 buffer3.apply_op(buf1_op, Some(cx.background_executor()));
727 buffer3.apply_op(buf2_op);811 buffer3.apply_op(buf2_op, Some(cx.background_executor()));
728 812
729 assert_eq!(buffer1.text(), "a12c34e56");813 assert_eq!(buffer1.text(), "a12c34e56");
730 assert_eq!(buffer2.text(), "a12c34e56");814 assert_eq!(buffer2.text(), "a12c34e56");
@@ -732,7 +816,7 @@ fn test_concurrent_edits() { · 1 unmodified line
732}816}
733 817
734#[gpui::test(iterations = 100)]818#[gpui::test(iterations = 100)]
735fn test_random_concurrent_edits(mut rng: StdRng) {819fn test_random_concurrent_edits(mut rng: StdRng, cx: &mut gpui::TestAppContext) {
736 let peers = env::var("PEERS")820 let peers = env::var("PEERS")
737 .map(|i| i.parse().expect("invalid `PEERS` variable"))821 .map(|i| i.parse().expect("invalid `PEERS` variable"))
738 .unwrap_or(5);822 .unwrap_or(5);
@@ -753,6 +837,7 @@ fn test_random_concurrent_edits(mut rng: StdRng) { · 14 unmodified lines
753 ReplicaId::new(i as u16),837 ReplicaId::new(i as u16),
754 BufferId::new(1).unwrap(),838 BufferId::new(1).unwrap(),
755 base_text.clone(),839 base_text.clone(),
840 cx.background_executor(),
756 );841 );
757 buffer.history.group_interval = Duration::from_millis(rng.random_range(0..=200));842 buffer.history.group_interval = Duration::from_millis(rng.random_range(0..=200));
758 buffers.push(buffer);843 buffers.push(buffer);
@@ -769,7 +854,9 @@ fn test_random_concurrent_edits(mut rng: StdRng) { · 10 unmodified lines
769 let buffer = &mut buffers[replica_index];854 let buffer = &mut buffers[replica_index];
770 match rng.random_range(0..=100) {855 match rng.random_range(0..=100) {
771 0..=50 if mutation_count != 0 => {856 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;
773 network.broadcast(buffer.replica_id, vec![op]);860 network.broadcast(buffer.replica_id, vec![op]);
774 log::info!("buffer {:?} text: {:?}", buffer.replica_id, buffer.text());861 log::info!("buffer {:?} text: {:?}", buffer.replica_id, buffer.text());
775 mutation_count -= 1;862 mutation_count -= 1;
@@ -787,7 +874,7 @@ fn test_random_concurrent_edits(mut rng: StdRng) { · 11 unmodified lines
787 replica_id,874 replica_id,
788 ops.len()875 ops.len()
789 );876 );
790 buffer.apply_ops(ops);877 buffer.apply_ops(ops, Some(cx.background_executor()));
791 }878 }
792 }879 }
793 _ => {}880 _ => {}
crates/text/src/text.rs+63−17
@@ -15,6 +15,7 @@ use anyhow::{Context as _, Result}; · 14 unmodified lines
15use clock::Lamport;15use clock::Lamport;
16pub use clock::ReplicaId;16pub use clock::ReplicaId;
17use collections::{HashMap, HashSet};17use collections::{HashMap, HashSet};
18use gpui::BackgroundExecutor;
18use locator::Locator;19use locator::Locator;
19use operation_queue::OperationQueue;20use operation_queue::OperationQueue;
20pub use patch::Patch;21pub use patch::Patch;
@@ -709,11 +710,41 @@ impl FromIterator<char> for LineIndent { · 688 unmodified lines
709}710}
710 711
711impl Buffer {712impl 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 {
713 let mut base_text = base_text.into();720 let mut base_text = base_text.into();
714 let line_ending = LineEnding::detect(&base_text);721 let line_ending = LineEnding::detect(&base_text);
715 LineEnding::normalize(&mut base_text);722 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 )
717 }748 }
718 749
719 pub fn new_normalized(750 pub fn new_normalized(
@@ -808,7 +839,7 @@ impl Buffer { · 88 unmodified lines
808 self.history.group_interval839 self.history.group_interval
809 }840 }
810 841
811 pub fn edit<R, I, S, T>(&mut self, edits: R) -> Operation842 pub fn edit<R, I, S, T>(&mut self, edits: R, cx: &BackgroundExecutor) -> Operation
812 where843 where
813 R: IntoIterator<IntoIter = I>,844 R: IntoIterator<IntoIter = I>,
814 I: ExactSizeIterator<Item = (Range<S>, T)>,845 I: ExactSizeIterator<Item = (Range<S>, T)>,
@@ -821,7 +852,7 @@ impl Buffer { · 6 unmodified lines
821 852
822 self.start_transaction();853 self.start_transaction();
823 let timestamp = self.lamport_clock.tick();854 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));
825 856
826 self.history.push(operation.clone());857 self.history.push(operation.clone());
827 self.history.push_undo(operation.timestamp());858 self.history.push_undo(operation.timestamp());
@@ -834,6 +865,7 @@ impl Buffer { · 6 unmodified lines
834 &mut self,865 &mut self,
835 edits: impl ExactSizeIterator<Item = (Range<S>, T)>,866 edits: impl ExactSizeIterator<Item = (Range<S>, T)>,
836 timestamp: clock::Lamport,867 timestamp: clock::Lamport,
868 executor: &BackgroundExecutor,
837 ) -> EditOperation {869 ) -> EditOperation {
838 let mut edits_patch = Patch::default();870 let mut edits_patch = Patch::default();
839 let mut edit_op = EditOperation {871 let mut edit_op = EditOperation {
@@ -922,7 +954,7 @@ impl Buffer { · 82 unmodified lines
922 });954 });
923 insertion_slices.push(InsertionSlice::from_fragment(timestamp, &fragment));955 insertion_slices.push(InsertionSlice::from_fragment(timestamp, &fragment));
924 new_insertions.push(InsertionFragment::insert_new(&fragment));956 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);
926 new_fragments.push(fragment, &None);958 new_fragments.push(fragment, &None);
927 insertion_offset += new_text.len();959 insertion_offset += new_text.len();
928 }960 }
@@ -1001,22 +1033,26 @@ impl Buffer { · 72 unmodified lines
1001 self.snapshot.line_ending = line_ending;1033 self.snapshot.line_ending = line_ending;
1002 }1034 }
1003 1035
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 ) {
1005 let mut deferred_ops = Vec::new();1041 let mut deferred_ops = Vec::new();
1006 for op in ops {1042 for op in ops {
1007 self.history.push(op.clone());1043 self.history.push(op.clone());
1008 if self.can_apply_op(&op) {1044 if self.can_apply_op(&op) {
1009 self.apply_op(op);1045 self.apply_op(op, executor);
1010 } else {1046 } else {
1011 self.deferred_replicas.insert(op.replica_id());1047 self.deferred_replicas.insert(op.replica_id());
1012 deferred_ops.push(op);1048 deferred_ops.push(op);
1013 }1049 }
1014 }1050 }
1015 self.deferred_ops.insert(deferred_ops);1051 self.deferred_ops.insert(deferred_ops);
1016 self.flush_deferred_ops();1052 self.flush_deferred_ops(executor);
1017 }1053 }
1018 1054
1019 fn apply_op(&mut self, op: Operation) {1055 fn apply_op(&mut self, op: Operation, executor: Option<&BackgroundExecutor>) {
1020 match op {1056 match op {
1021 Operation::Edit(edit) => {1057 Operation::Edit(edit) => {
1022 if !self.version.observed(edit.timestamp) {1058 if !self.version.observed(edit.timestamp) {
@@ -1025,6 +1061,7 @@ impl Buffer { · 2 unmodified lines
1025 &edit.ranges,1061 &edit.ranges,
1026 &edit.new_text,1062 &edit.new_text,
1027 edit.timestamp,1063 edit.timestamp,
1064 executor,
1028 );1065 );
1029 self.snapshot.version.observe(edit.timestamp);1066 self.snapshot.version.observe(edit.timestamp);
1030 self.lamport_clock.observe(edit.timestamp);1067 self.lamport_clock.observe(edit.timestamp);
@@ -1055,6 +1092,7 @@ impl Buffer { · 24 unmodified lines
1055 ranges: &[Range<FullOffset>],1092 ranges: &[Range<FullOffset>],
1056 new_text: &[Arc<str>],1093 new_text: &[Arc<str>],
1057 timestamp: clock::Lamport,1094 timestamp: clock::Lamport,
1095 executor: Option<&BackgroundExecutor>,
1058 ) {1096 ) {
1059 if ranges.is_empty() {1097 if ranges.is_empty() {
1060 return;1098 return;
@@ -1170,7 +1208,10 @@ impl Buffer { · 109 unmodified lines
1170 });1208 });
1171 insertion_slices.push(InsertionSlice::from_fragment(timestamp, &fragment));1209 insertion_slices.push(InsertionSlice::from_fragment(timestamp, &fragment));
1172 new_insertions.push(InsertionFragment::insert_new(&fragment));1210 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 }
1174 new_fragments.push(fragment, &None);1215 new_fragments.push(fragment, &None);
1175 insertion_offset += new_text.len();1216 insertion_offset += new_text.len();
1176 }1217 }
@@ -1348,12 +1389,12 @@ impl Buffer { · 171 unmodified lines
1348 self.subscriptions.publish_mut(&edits);1389 self.subscriptions.publish_mut(&edits);
1349 }1390 }
1350 1391
1351 fn flush_deferred_ops(&mut self) {1392 fn flush_deferred_ops(&mut self, executor: Option<&BackgroundExecutor>) {
1352 self.deferred_replicas.clear();1393 self.deferred_replicas.clear();
1353 let mut deferred_ops = Vec::new();1394 let mut deferred_ops = Vec::new();
1354 for op in self.deferred_ops.drain().iter().cloned() {1395 for op in self.deferred_ops.drain().iter().cloned() {
1355 if self.can_apply_op(&op) {1396 if self.can_apply_op(&op) {
1356 self.apply_op(op);1397 self.apply_op(op, executor);
1357 } else {1398 } else {
1358 self.deferred_replicas.insert(op.replica_id());1399 self.deferred_replicas.insert(op.replica_id());
1359 deferred_ops.push(op);1400 deferred_ops.push(op);
@@ -1711,9 +1752,9 @@ impl Buffer { · 351 unmodified lines
1711#[cfg(any(test, feature = "test-support"))]1752#[cfg(any(test, feature = "test-support"))]
1712impl Buffer {1753impl Buffer {
1713 #[track_caller]1754 #[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) {
1715 let edits = self.edits_for_marked_text(marked_string);1756 let edits = self.edits_for_marked_text(marked_string);
1716 self.edit(edits);1757 self.edit(edits, cx);
1717 }1758 }
1718 1759
1719 #[track_caller]1760 #[track_caller]
@@ -1850,6 +1891,7 @@ impl Buffer { · 130 unmodified lines
1850 &mut self,1891 &mut self,
1851 rng: &mut T,1892 rng: &mut T,
1852 edit_count: usize,1893 edit_count: usize,
1894 executor: &BackgroundExecutor,
1853 ) -> (Vec<(Range<usize>, Arc<str>)>, Operation)1895 ) -> (Vec<(Range<usize>, Arc<str>)>, Operation)
1854 where1896 where
1855 T: rand::Rng,1897 T: rand::Rng,
@@ -1857,7 +1899,7 @@ impl Buffer { · 1 unmodified line
1857 let mut edits = self.get_random_edits(rng, edit_count);1899 let mut edits = self.get_random_edits(rng, edit_count);
1858 log::info!("mutating buffer {:?} with {:?}", self.replica_id, edits);1900 log::info!("mutating buffer {:?} with {:?}", self.replica_id, edits);
1859 1901
1860 let op = self.edit(edits.iter().cloned());1902 let op = self.edit(edits.iter().cloned(), executor);
1861 if let Operation::Edit(edit) = &op {1903 if let Operation::Edit(edit) = &op {
1862 assert_eq!(edits.len(), edit.new_text.len());1904 assert_eq!(edits.len(), edit.new_text.len());
1863 for (edit, new_text) in edits.iter_mut().zip(&edit.new_text) {1905 for (edit, new_text) in edits.iter_mut().zip(&edit.new_text) {
@@ -2692,8 +2734,12 @@ impl<'a> RopeBuilder<'a> { · 828 unmodified lines
2692 }2734 }
2693 }2735 }
2694 2736
2695 fn push_str(&mut self, text: &str) {2737 fn push_str(&mut self, text: &str, cx: &BackgroundExecutor) {
2696 self.new_visible.push(text);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);
2697 }2743 }
2698 2744
2699 fn finish(mut self) -> (Rope, Rope) {2745 fn finish(mut self) -> (Rope, Rope) {
crates/vim/src/motion.rs+6−4
@@ -3096,6 +3096,7 @@ mod test { · 3095 unmodified lines
3096 use indoc::indoc;3096 use indoc::indoc;
3097 use language::Point;3097 use language::Point;
3098 use multi_buffer::MultiBufferRow;3098 use multi_buffer::MultiBufferRow;
3099 use text::Rope;
3099 3100
3100 #[gpui::test]3101 #[gpui::test]
3101 async fn test_start_end_of_paragraph(cx: &mut gpui::TestAppContext) {3102 async fn test_start_end_of_paragraph(cx: &mut gpui::TestAppContext) {
@@ -3822,7 +3823,7 @@ mod test { · 720 unmodified lines
3822 cx.update_editor(|editor, _window, cx| {3823 cx.update_editor(|editor, _window, cx| {
3823 let range = editor.selections.newest_anchor().range();3824 let range = editor.selections.newest_anchor().range();
3824 let inlay_text = " field: int,\n field2: string\n field3: float";3825 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));
3826 editor.splice_inlays(&[], vec![inlay], cx);3827 editor.splice_inlays(&[], vec![inlay], cx);
3827 });3828 });
3828 3829
@@ -3854,7 +3855,7 @@ mod test { · 25 unmodified lines
3854 let end_of_line =3855 let end_of_line =
3855 snapshot.anchor_after(Point::new(0, snapshot.line_len(MultiBufferRow(0))));3856 snapshot.anchor_after(Point::new(0, snapshot.line_len(MultiBufferRow(0))));
3856 let inlay_text = " hint";3857 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));
3858 editor.splice_inlays(&[], vec![inlay], cx);3859 editor.splice_inlays(&[], vec![inlay], cx);
3859 });3860 });
3860 cx.simulate_keystrokes("$");3861 cx.simulate_keystrokes("$");
@@ -3893,7 +3894,7 @@ mod test { · 32 unmodified lines
3893 // The empty line is at line 3 (0-indexed)3894 // The empty line is at line 3 (0-indexed)
3894 let line_start = snapshot.anchor_after(Point::new(3, 0));3895 let line_start = snapshot.anchor_after(Point::new(3, 0));
3895 let inlay_text = ": Vec<u32>";3896 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));
3897 editor.splice_inlays(&[], vec![inlay], cx);3898 editor.splice_inlays(&[], vec![inlay], cx);
3898 });3899 });
3899 3900
@@ -3937,7 +3938,8 @@ mod test { · 37 unmodified lines
3937 let snapshot = editor.buffer().read(cx).snapshot(cx);3938 let snapshot = editor.buffer().read(cx).snapshot(cx);
3938 let empty_line_start = snapshot.anchor_after(Point::new(2, 0));3939 let empty_line_start = snapshot.anchor_after(Point::new(2, 0));
3939 let inlay_text = ": i32";3940 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));
3941 editor.splice_inlays(&[], vec![inlay], cx);3943 editor.splice_inlays(&[], vec![inlay], cx);
3942 });3944 });
3943 3945
crates/workspace/src/workspace.rs+2−2
@@ -7580,13 +7580,13 @@ pub fn create_and_open_local_file( · 7579 unmodified lines
7580 path: &'static Path,7580 path: &'static Path,
7581 window: &mut Window,7581 window: &mut Window,
7582 cx: &mut Context<Workspace>,7582 cx: &mut Context<Workspace>,
7583 default_content: impl 'static + Send + FnOnce() -> Rope,7583 default_content: impl 'static + Send + FnOnce(&mut AsyncApp) -> Rope,
7584) -> Task<Result<Box<dyn ItemHandle>>> {7584) -> Task<Result<Box<dyn ItemHandle>>> {
7585 cx.spawn_in(window, async move |workspace, cx| {7585 cx.spawn_in(window, async move |workspace, cx| {
7586 let fs = workspace.read_with(cx, |workspace, _| workspace.app_state().fs.clone())?;7586 let fs = workspace.read_with(cx, |workspace, _| workspace.app_state().fs.clone())?;
7587 if !fs.is_file(path).await {7587 if !fs.is_file(path).await {
7588 fs.create_file(path, Default::default()).await?;7588 fs.create_file(path, Default::default()).await?;
7589 fs.save(path, &default_content(), Default::default())7589 fs.save(path, &default_content(cx), Default::default())
7590 .await?;7590 .await?;
7591 }7591 }
7592 7592
crates/worktree/src/worktree_tests.rs+22−12
@@ -20,6 +20,7 @@ use std::{ · 19 unmodified lines
20 path::{Path, PathBuf},20 path::{Path, PathBuf},
21 sync::Arc,21 sync::Arc,
22};22};
23use text::Rope;
23use util::{24use util::{
24 ResultExt, path,25 ResultExt, path,
25 rel_path::{RelPath, rel_path},26 rel_path::{RelPath, rel_path},
@@ -646,9 +647,13 @@ async fn test_dirs_no_longer_ignored(cx: &mut TestAppContext) { · 620 unmodified lines
646 647
647 // Update the gitignore so that node_modules is no longer ignored,648 // Update the gitignore so that node_modules is no longer ignored,
648 // but a subdirectory is ignored649 // but a subdirectory is ignored
649 fs.save("/root/.gitignore".as_ref(), &"e".into(), Default::default())650 fs.save(
650 .await651 "/root/.gitignore".as_ref(),
651 .unwrap();652 &Rope::from_str("e", cx.background_executor()),
653 Default::default(),
654 )
655 .await
656 .unwrap();
652 cx.executor().run_until_parked();657 cx.executor().run_until_parked();
653 658
654 // All of the directories that are no longer ignored are now loaded.659 // 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
716 .update(cx, |tree, cx| {721 .update(cx, |tree, cx| {
717 tree.write_file(722 tree.write_file(
718 rel_path("tracked-dir/file.txt").into(),723 rel_path("tracked-dir/file.txt").into(),
719 "hello".into(),724 Rope::from_str("hello", cx.background_executor()),
720 Default::default(),725 Default::default(),
721 cx,726 cx,
722 )727 )
@@ -727,7 +732,7 @@ async fn test_write_file(cx: &mut TestAppContext) { · 4 unmodified lines
727 .update(cx, |tree, cx| {732 .update(cx, |tree, cx| {
728 tree.write_file(733 tree.write_file(
729 rel_path("ignored-dir/file.txt").into(),734 rel_path("ignored-dir/file.txt").into(),
730 "world".into(),735 Rope::from_str("world", cx.background_executor()),
731 Default::default(),736 Default::default(),
732 cx,737 cx,
733 )738 )
@@ -1465,7 +1470,7 @@ async fn test_random_worktree_operations_during_initial_scan( · 731 unmodified lines
1465 let fs = FakeFs::new(cx.background_executor.clone()) as Arc<dyn Fs>;1470 let fs = FakeFs::new(cx.background_executor.clone()) as Arc<dyn Fs>;
1466 fs.as_fake().insert_tree(root_dir, json!({})).await;1471 fs.as_fake().insert_tree(root_dir, json!({})).await;
1467 for _ in 0..initial_entries {1472 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;
1469 }1474 }
1470 log::info!("generated initial tree");1475 log::info!("generated initial tree");
1471 1476
@@ -1555,7 +1560,7 @@ async fn test_random_worktree_changes(cx: &mut TestAppContext, mut rng: StdRng) · 83 unmodified lines
1555 let fs = FakeFs::new(cx.background_executor.clone()) as Arc<dyn Fs>;1560 let fs = FakeFs::new(cx.background_executor.clone()) as Arc<dyn Fs>;
1556 fs.as_fake().insert_tree(root_dir, json!({})).await;1561 fs.as_fake().insert_tree(root_dir, json!({})).await;
1557 for _ in 0..initial_entries {1562 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;
1559 }1564 }
1560 log::info!("generated initial tree");1565 log::info!("generated initial tree");
1561 1566
@@ -1598,7 +1603,7 @@ async fn test_random_worktree_changes(cx: &mut TestAppContext, mut rng: StdRng) · 36 unmodified lines
1598 .await1603 .await
1599 .log_err();1604 .log_err();
1600 } else {1605 } 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;
1602 }1607 }
1603 1608
1604 let buffered_event_count = fs.as_fake().buffered_event_count();1609 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
1607 log::info!("flushing {} events", len);1612 log::info!("flushing {} events", len);
1608 fs.as_fake().flush_events(len);1613 fs.as_fake().flush_events(len);
1609 } else {1614 } 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;
1611 mutations_len -= 1;1616 mutations_len -= 1;
1612 }1617 }
1613 1618
@@ -1759,8 +1764,12 @@ fn randomly_mutate_worktree( · 145 unmodified lines
1759 })1764 })
1760 } else {1765 } else {
1761 log::info!("overwriting file {:?} ({})", &entry.path, entry.id.0);1766 log::info!("overwriting file {:?} ({})", &entry.path, entry.id.0);
1762 let task =1767 let task = worktree.write_file(
1763 worktree.write_file(entry.path.clone(), "".into(), Default::default(), cx);1768 entry.path.clone(),
1769 Rope::default(),
1770 Default::default(),
1771 cx,
1772 );
1764 cx.background_spawn(async move {1773 cx.background_spawn(async move {
1765 task.await?;1774 task.await?;
1766 Ok(())1775 Ok(())
@@ -1775,6 +1784,7 @@ async fn randomly_mutate_fs( · 8 unmodified lines
1775 root_path: &Path,1784 root_path: &Path,
1776 insertion_probability: f64,1785 insertion_probability: f64,
1777 rng: &mut impl Rng,1786 rng: &mut impl Rng,
1787 executor: &BackgroundExecutor,
1778) {1788) {
1779 log::info!("mutating fs");1789 log::info!("mutating fs");
1780 let mut files = Vec::new();1790 let mut files = Vec::new();
@@ -1849,7 +1859,7 @@ async fn randomly_mutate_fs( · 68 unmodified lines
1849 );1859 );
1850 fs.save(1860 fs.save(
1851 &ignore_path,1861 &ignore_path,
1852 &ignore_contents.as_str().into(),1862 &Rope::from_str(ignore_contents.as_str(), executor),
1853 Default::default(),1863 Default::default(),
1854 )1864 )
1855 .await1865 .await
crates/zed/src/zed.rs+37−17
@@ -28,10 +28,10 @@ use git_ui::commit_view::CommitViewToolbar; · 27 unmodified lines
28use git_ui::git_panel::GitPanel;28use git_ui::git_panel::GitPanel;
29use git_ui::project_diff::ProjectDiffToolbar;29use git_ui::project_diff::ProjectDiffToolbar;
30use gpui::{30use gpui::{
31 Action, App, AppContext as _, Context, DismissEvent, Element, Entity, Focusable, KeyBinding,31 Action, App, AppContext as _, AsyncApp, Context, DismissEvent, Element, Entity, Focusable,
32 ParentElement, PathPromptOptions, PromptLevel, ReadGlobal, SharedString, Styled, Task,32 KeyBinding, ParentElement, PathPromptOptions, PromptLevel, ReadGlobal, SharedString, Styled,
33 TitlebarOptions, UpdateGlobal, Window, WindowKind, WindowOptions, actions, image_cache, point,33 Task, TitlebarOptions, UpdateGlobal, Window, WindowKind, WindowOptions, actions, image_cache,
34 px, retain_all,34 point, px, retain_all,
35};35};
36use image_viewer::ImageInfo;36use image_viewer::ImageInfo;
37use language::Capability;37use language::Capability;
@@ -201,7 +201,12 @@ pub fn init(cx: &mut App) { · 163 unmodified lines
201 with_active_or_new_workspace(cx, |_, window, cx| {201 with_active_or_new_workspace(cx, |_, window, cx| {
202 open_settings_file(202 open_settings_file(
203 paths::keymap_file(),203 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 },
205 window,210 window,
206 cx,211 cx,
207 );212 );
@@ -211,7 +216,12 @@ pub fn init(cx: &mut App) { · 3 unmodified lines
211 with_active_or_new_workspace(cx, |_, window, cx| {216 with_active_or_new_workspace(cx, |_, window, cx| {
212 open_settings_file(217 open_settings_file(
213 paths::settings_file(),218 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 },
215 window,225 window,
216 cx,226 cx,
217 );227 );
@@ -226,7 +236,12 @@ pub fn init(cx: &mut App) { · 8 unmodified lines
226 with_active_or_new_workspace(cx, |_, window, cx| {236 with_active_or_new_workspace(cx, |_, window, cx| {
227 open_settings_file(237 open_settings_file(
228 paths::tasks_file(),238 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 },
230 window,245 window,
231 cx,246 cx,
232 );247 );
@@ -236,7 +251,12 @@ pub fn init(cx: &mut App) { · 3 unmodified lines
236 with_active_or_new_workspace(cx, |_, window, cx| {251 with_active_or_new_workspace(cx, |_, window, cx| {
237 open_settings_file(252 open_settings_file(
238 paths::debug_scenarios_file(),253 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 },
240 window,260 window,
241 cx,261 cx,
242 );262 );
@@ -1939,7 +1959,7 @@ fn open_bundled_file( · 1696 unmodified lines
1939 1959
1940fn open_settings_file(1960fn open_settings_file(
1941 abs_path: &'static Path,1961 abs_path: &'static Path,
1942 default_content: impl FnOnce() -> Rope + Send + 'static,1962 default_content: impl FnOnce(&mut AsyncApp) -> Rope + Send + 'static,
1943 window: &mut Window,1963 window: &mut Window,
1944 cx: &mut Context<Workspace>,1964 cx: &mut Context<Workspace>,
1945) {1965) {
@@ -4355,7 +4375,7 @@ mod tests { · 2409 unmodified lines
4355 .fs4375 .fs
4356 .save(4376 .save(
4357 "/settings.json".as_ref(),4377 "/settings.json".as_ref(),
4358 &r#"{"base_keymap": "Atom"}"#.into(),4378 &Rope::from_str_small(r#"{"base_keymap": "Atom"}"#),
4359 Default::default(),4379 Default::default(),
4360 )4380 )
4361 .await4381 .await
@@ -4365,7 +4385,7 @@ mod tests { · 3 unmodified lines
4365 .fs4385 .fs
4366 .save(4386 .save(
4367 "/keymap.json".as_ref(),4387 "/keymap.json".as_ref(),
4368 &r#"[{"bindings": {"backspace": "test_only::ActionA"}}]"#.into(),4388 &Rope::from_str_small(r#"[{"bindings": {"backspace": "test_only::ActionA"}}]"#),
4369 Default::default(),4389 Default::default(),
4370 )4390 )
4371 .await4391 .await
@@ -4413,7 +4433,7 @@ mod tests { · 41 unmodified lines
4413 .fs4433 .fs
4414 .save(4434 .save(
4415 "/keymap.json".as_ref(),4435 "/keymap.json".as_ref(),
4416 &r#"[{"bindings": {"backspace": "test_only::ActionB"}}]"#.into(),4436 &Rope::from_str_small(r#"[{"bindings": {"backspace": "test_only::ActionB"}}]"#),
4417 Default::default(),4437 Default::default(),
4418 )4438 )
4419 .await4439 .await
@@ -4433,7 +4453,7 @@ mod tests { · 13 unmodified lines
4433 .fs4453 .fs
4434 .save(4454 .save(
4435 "/settings.json".as_ref(),4455 "/settings.json".as_ref(),
4436 &r#"{"base_keymap": "JetBrains"}"#.into(),4456 &Rope::from_str_small(r#"{"base_keymap": "JetBrains"}"#),
4437 Default::default(),4457 Default::default(),
4438 )4458 )
4439 .await4459 .await
@@ -4473,7 +4493,7 @@ mod tests { · 33 unmodified lines
4473 .fs4493 .fs
4474 .save(4494 .save(
4475 "/settings.json".as_ref(),4495 "/settings.json".as_ref(),
4476 &r#"{"base_keymap": "Atom"}"#.into(),4496 &Rope::from_str_small(r#"{"base_keymap": "Atom"}"#),
4477 Default::default(),4497 Default::default(),
4478 )4498 )
4479 .await4499 .await
@@ -4482,7 +4502,7 @@ mod tests { · 2 unmodified lines
4482 .fs4502 .fs
4483 .save(4503 .save(
4484 "/keymap.json".as_ref(),4504 "/keymap.json".as_ref(),
4485 &r#"[{"bindings": {"backspace": "test_only::ActionA"}}]"#.into(),4505 &Rope::from_str_small(r#"[{"bindings": {"backspace": "test_only::ActionA"}}]"#),
4486 Default::default(),4506 Default::default(),
4487 )4507 )
4488 .await4508 .await
@@ -4525,7 +4545,7 @@ mod tests { · 36 unmodified lines
4525 .fs4545 .fs
4526 .save(4546 .save(
4527 "/keymap.json".as_ref(),4547 "/keymap.json".as_ref(),
4528 &r#"[{"bindings": {"backspace": null}}]"#.into(),4548 &Rope::from_str_small(r#"[{"bindings": {"backspace": null}}]"#),
4529 Default::default(),4549 Default::default(),
4530 )4550 )
4531 .await4551 .await
@@ -4545,7 +4565,7 @@ mod tests { · 13 unmodified lines
4545 .fs4565 .fs
4546 .save(4566 .save(
4547 "/settings.json".as_ref(),4567 "/settings.json".as_ref(),
4548 &r#"{"base_keymap": "JetBrains"}"#.into(),4568 &Rope::from_str_small(r#"{"base_keymap": "JetBrains"}"#),
4549 Default::default(),4569 Default::default(),
4550 )4570 )
4551 .await4571 .await
crates/zed/src/zed/open_listener.rs+2−2
@@ -861,7 +861,7 @@ mod tests { · 860 unmodified lines
861 .fs861 .fs
862 .save(862 .save(
863 Path::new(file1_path),863 Path::new(file1_path),
864 &Rope::from("content1"),864 &Rope::from_str("content1", cx.background_executor()),
865 LineEnding::Unix,865 LineEnding::Unix,
866 )866 )
867 .await867 .await
@@ -875,7 +875,7 @@ mod tests { · 7 unmodified lines
875 .fs875 .fs
876 .save(876 .save(
877 Path::new(file2_path),877 Path::new(file2_path),
878 &Rope::from("content2"),878 &Rope::from_str("content2", cx.background_executor()),
879 LineEnding::Unix,879 LineEnding::Unix,
880 )880 )
881 .await881 .await
crates/zeta/src/zeta.rs+2−1
@@ -1836,12 +1836,13 @@ mod tests { · 1835 unmodified lines
1836 let fs = project::FakeFs::new(cx.executor());1836 let fs = project::FakeFs::new(cx.executor());
1837 let project = Project::test(fs.clone(), [], cx).await;1837 let project = Project::test(fs.clone(), [], cx).await;
1838 1838
1839 let buffer = cx.new(|_cx| {1839 let buffer = cx.new(|cx| {
1840 Buffer::remote(1840 Buffer::remote(
1841 language::BufferId::new(1).unwrap(),1841 language::BufferId::new(1).unwrap(),
1842 ReplicaId::new(1),1842 ReplicaId::new(1),
1843 language::Capability::ReadWrite,1843 language::Capability::ReadWrite,
1844 "fn main() {\n println!(\"Hello\");\n}",1844 "fn main() {\n println!(\"Hello\");\n}",
1845 cx.background_executor(),
1845 )1846 )
1846 });1847 });
1847 1848