WIP

Nathan Sobo created

Change summary

Cargo.lock                                         |     1 
crates/editor2/src/display_map.rs                  |  1857 
crates/editor2/src/display_map/fold_map.rs         |     8 
crates/editor2/src/display_map/inlay_map.rs        |     2 
crates/editor2/src/display_map/wrap_map.rs         |     4 
crates/editor2/src/editor.rs                       |   423 
crates/editor2/src/editor_tests.rs                 | 16386 +++++++--------
crates/editor2/src/git.rs                          |   364 
crates/editor2/src/highlight_matching_bracket.rs   |   198 
crates/editor2/src/hover_popover.rs                |  1649 
crates/editor2/src/inlay_hint_cache.rs             |  4318 ++--
crates/editor2/src/items.rs                        |    11 
crates/editor2/src/link_go_to_definition.rs        |  1403 
crates/editor2/src/mouse_context_menu.rs           |   122 
crates/editor2/src/movement.rs                     |     5 
crates/editor2/src/scroll.rs                       |    63 
crates/editor2/src/scroll/actions.rs               |     2 
crates/editor2/src/selections_collection.rs        |     6 
crates/editor2/src/test.rs                         |     9 
crates/editor2/src/test/editor_lsp_test_context.rs |     2 
crates/editor2/src/test/editor_test_context.rs     |   343 
crates/gpui2/src/text_system.rs                    |     2 
crates/language2/Cargo.toml                        |     1 
crates/language2/src/language2.rs                  |     1 
crates/language2/src/markdown.rs                   |   301 
25 files changed, 13,875 insertions(+), 13,606 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -4408,6 +4408,7 @@ dependencies = [
  "lsp2",
  "parking_lot 0.11.2",
  "postage",
+ "pulldown-cmark",
  "rand 0.8.5",
  "regex",
  "rpc2",

crates/editor2/src/display_map.rs 🔗

@@ -11,11 +11,7 @@ use crate::{
 pub use block_map::{BlockMap, BlockPoint};
 use collections::{BTreeMap, HashMap, HashSet};
 use fold_map::FoldMap;
-use gpui::{
-    fonts::{FontId, HighlightStyle, Underline},
-    text_layout::{Line, RunStyle},
-    Entity, Hsla, Model, ModelContext,
-};
+use gpui::{FontId, HighlightStyle, Hsla, Line, Model, ModelContext, UnderlineStyle};
 use inlay_map::InlayMap;
 use language::{
     language_settings::language_settings, OffsetUtf16, Point, Subscription as BufferSubscription,
@@ -60,10 +56,6 @@ pub struct DisplayMap {
     pub clip_at_line_ends: bool,
 }
 
-impl Entity for DisplayMap {
-    type Event = ();
-}
-
 impl DisplayMap {
     pub fn new(
         buffer: Model<MultiBuffer>,
@@ -253,7 +245,7 @@ impl DisplayMap {
             .update(cx, |map, cx| map.set_font(font_id, font_size, cx))
     }
 
-    pub fn set_fold_ellipses_color(&mut self, color: Color) -> bool {
+    pub fn set_fold_ellipses_color(&mut self, color: Hsla) -> bool {
         self.fold_map.set_ellipses_color(color)
     }
 
@@ -295,7 +287,7 @@ impl DisplayMap {
         self.block_map.read(snapshot, edits);
     }
 
-    fn tab_size(buffer: &ModelHandle<MultiBuffer>, cx: &mut ModelContext<Self>) -> NonZeroU32 {
+    fn tab_size(buffer: &Model<MultiBuffer>, cx: &mut ModelContext<Self>) -> NonZeroU32 {
         let language = buffer
             .read(cx)
             .as_singleton()
@@ -540,10 +532,10 @@ impl DisplaySnapshot {
                 // Omit underlines for HINT/INFO diagnostics on 'unnecessary' code.
                 if severity <= DiagnosticSeverity::WARNING || !chunk.is_unnecessary {
                     let diagnostic_style = super::diagnostic_style(severity, true, style);
-                    diagnostic_highlight.underline = Some(Underline {
+                    diagnostic_highlight.underline = Some(UnderlineStyle {
                         color: Some(diagnostic_style.message.text.color),
                         thickness: 1.0.into(),
-                        squiggly: true,
+                        wavy: true,
                     });
                 }
             }
@@ -566,8 +558,8 @@ impl DisplaySnapshot {
         &self,
         display_row: u32,
         TextLayoutDetails {
-            font_cache,
-            text_layout_cache,
+            text_system: font_cache,
+            text_system: text_layout_cache,
             editor_style,
         }: &TextLayoutDetails,
     ) -> Line {
@@ -591,14 +583,12 @@ impl DisplaySnapshot {
             };
             ended_in_newline = chunk.chunk.ends_with("\n");
 
-            styles.push((
-                chunk.chunk.len(),
-                RunStyle {
-                    font_id: text_style.font_id,
-                    color: text_style.color,
-                    underline: text_style.underline,
-                },
-            ));
+            styles.push(
+                todo!(), // len: chunk.chunk.len(),
+                         // font_id: text_style.font_id,
+                         // color: text_style.color,
+                         // underline: text_style.underline,
+            );
         }
 
         // our pixel positioning logic assumes each line ends in \n,
@@ -607,17 +597,16 @@ impl DisplaySnapshot {
         if !ended_in_newline && display_row == self.max_point().row() {
             line.push_str("\n");
 
-            styles.push((
-                "\n".len(),
-                RunStyle {
-                    font_id: editor_style.text.font_id,
-                    color: editor_style.text_color,
-                    underline: editor_style.text.underline,
-                },
-            ));
+            todo!();
+            // styles.push(RunStyle {
+            //     len: "\n".len(),
+            //     font_id: editor_style.text.font_id,
+            //     color: editor_style.text_color,
+            //     underline: editor_style.text.underline,
+            // });
         }
 
-        text_layout_cache.layout_str(&line, editor_style.text.font_size, &styles)
+        text_layout_cache.layout_text(&line, editor_style.text.font_size, &styles)
     }
 
     pub fn x_for_point(
@@ -1007,905 +996,905 @@ pub fn next_rows(display_row: u32, display_map: &DisplaySnapshot) -> impl Iterat
     })
 }
 
-#[cfg(test)]
-pub mod tests {
-    use super::*;
-    use crate::{
-        movement,
-        test::{editor_test_context::EditorTestContext, marked_display_snapshot},
-    };
-    use gpui::{elements::*, test::observe, AppContext, Hsla};
-    use language::{
-        language_settings::{AllLanguageSettings, AllLanguageSettingsContent},
-        Buffer, Language, LanguageConfig, SelectionGoal,
-    };
-    use project::Project;
-    use rand::{prelude::*, Rng};
-    use settings::SettingsStore;
-    use smol::stream::StreamExt;
-    use std::{env, sync::Arc};
-    use theme::SyntaxTheme;
-    use util::test::{marked_text_ranges, sample_text};
-    use Bias::*;
-
-    #[gpui::test(iterations = 100)]
-    async fn test_random_display_map(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
-        cx.foreground().set_block_on_ticks(0..=50);
-        cx.foreground().forbid_parking();
-        let operations = env::var("OPERATIONS")
-            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
-            .unwrap_or(10);
-
-        let font_cache = cx.font_cache().clone();
-        let mut tab_size = rng.gen_range(1..=4);
-        let buffer_start_excerpt_header_height = rng.gen_range(1..=5);
-        let excerpt_header_height = rng.gen_range(1..=5);
-        let family_id = font_cache
-            .load_family(&["Helvetica"], &Default::default())
-            .unwrap();
-        let font_id = font_cache
-            .select_font(family_id, &Default::default())
-            .unwrap();
-        let font_size = 14.0;
-        let max_wrap_width = 300.0;
-        let mut wrap_width = if rng.gen_bool(0.1) {
-            None
-        } else {
-            Some(rng.gen_range(0.0..=max_wrap_width))
-        };
-
-        log::info!("tab size: {}", tab_size);
-        log::info!("wrap width: {:?}", wrap_width);
-
-        cx.update(|cx| {
-            init_test(cx, |s| s.defaults.tab_size = NonZeroU32::new(tab_size));
-        });
-
-        let buffer = cx.update(|cx| {
-            if rng.gen() {
-                let len = rng.gen_range(0..10);
-                let text = util::RandomCharIter::new(&mut rng)
-                    .take(len)
-                    .collect::<String>();
-                MultiBuffer::build_simple(&text, cx)
-            } else {
-                MultiBuffer::build_random(&mut rng, cx)
-            }
-        });
-
-        let map = cx.add_model(|cx| {
-            DisplayMap::new(
-                buffer.clone(),
-                font_id,
-                font_size,
-                wrap_width,
-                buffer_start_excerpt_header_height,
-                excerpt_header_height,
-                cx,
-            )
-        });
-        let mut notifications = observe(&map, cx);
-        let mut fold_count = 0;
-        let mut blocks = Vec::new();
-
-        let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
-        log::info!("buffer text: {:?}", snapshot.buffer_snapshot.text());
-        log::info!("fold text: {:?}", snapshot.fold_snapshot.text());
-        log::info!("tab text: {:?}", snapshot.tab_snapshot.text());
-        log::info!("wrap text: {:?}", snapshot.wrap_snapshot.text());
-        log::info!("block text: {:?}", snapshot.block_snapshot.text());
-        log::info!("display text: {:?}", snapshot.text());
-
-        for _i in 0..operations {
-            match rng.gen_range(0..100) {
-                0..=19 => {
-                    wrap_width = if rng.gen_bool(0.2) {
-                        None
-                    } else {
-                        Some(rng.gen_range(0.0..=max_wrap_width))
-                    };
-                    log::info!("setting wrap width to {:?}", wrap_width);
-                    map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
-                }
-                20..=29 => {
-                    let mut tab_sizes = vec![1, 2, 3, 4];
-                    tab_sizes.remove((tab_size - 1) as usize);
-                    tab_size = *tab_sizes.choose(&mut rng).unwrap();
-                    log::info!("setting tab size to {:?}", tab_size);
-                    cx.update(|cx| {
-                        cx.update_global::<SettingsStore, _, _>(|store, cx| {
-                            store.update_user_settings::<AllLanguageSettings>(cx, |s| {
-                                s.defaults.tab_size = NonZeroU32::new(tab_size);
-                            });
-                        });
-                    });
-                }
-                30..=44 => {
-                    map.update(cx, |map, cx| {
-                        if rng.gen() || blocks.is_empty() {
-                            let buffer = map.snapshot(cx).buffer_snapshot;
-                            let block_properties = (0..rng.gen_range(1..=1))
-                                .map(|_| {
-                                    let position =
-                                        buffer.anchor_after(buffer.clip_offset(
-                                            rng.gen_range(0..=buffer.len()),
-                                            Bias::Left,
-                                        ));
-
-                                    let disposition = if rng.gen() {
-                                        BlockDisposition::Above
-                                    } else {
-                                        BlockDisposition::Below
-                                    };
-                                    let height = rng.gen_range(1..5);
-                                    log::info!(
-                                        "inserting block {:?} {:?} with height {}",
-                                        disposition,
-                                        position.to_point(&buffer),
-                                        height
-                                    );
-                                    BlockProperties {
-                                        style: BlockStyle::Fixed,
-                                        position,
-                                        height,
-                                        disposition,
-                                        render: Arc::new(|_| Empty::new().into_any()),
-                                    }
-                                })
-                                .collect::<Vec<_>>();
-                            blocks.extend(map.insert_blocks(block_properties, cx));
-                        } else {
-                            blocks.shuffle(&mut rng);
-                            let remove_count = rng.gen_range(1..=4.min(blocks.len()));
-                            let block_ids_to_remove = (0..remove_count)
-                                .map(|_| blocks.remove(rng.gen_range(0..blocks.len())))
-                                .collect();
-                            log::info!("removing block ids {:?}", block_ids_to_remove);
-                            map.remove_blocks(block_ids_to_remove, cx);
-                        }
-                    });
-                }
-                45..=79 => {
-                    let mut ranges = Vec::new();
-                    for _ in 0..rng.gen_range(1..=3) {
-                        buffer.read_with(cx, |buffer, cx| {
-                            let buffer = buffer.read(cx);
-                            let end = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Right);
-                            let start = buffer.clip_offset(rng.gen_range(0..=end), Left);
-                            ranges.push(start..end);
-                        });
-                    }
-
-                    if rng.gen() && fold_count > 0 {
-                        log::info!("unfolding ranges: {:?}", ranges);
-                        map.update(cx, |map, cx| {
-                            map.unfold(ranges, true, cx);
-                        });
-                    } else {
-                        log::info!("folding ranges: {:?}", ranges);
-                        map.update(cx, |map, cx| {
-                            map.fold(ranges, cx);
-                        });
-                    }
-                }
-                _ => {
-                    buffer.update(cx, |buffer, cx| buffer.randomly_mutate(&mut rng, 5, cx));
-                }
-            }
-
-            if map.read_with(cx, |map, cx| map.is_rewrapping(cx)) {
-                notifications.next().await.unwrap();
-            }
-
-            let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
-            fold_count = snapshot.fold_count();
-            log::info!("buffer text: {:?}", snapshot.buffer_snapshot.text());
-            log::info!("fold text: {:?}", snapshot.fold_snapshot.text());
-            log::info!("tab text: {:?}", snapshot.tab_snapshot.text());
-            log::info!("wrap text: {:?}", snapshot.wrap_snapshot.text());
-            log::info!("block text: {:?}", snapshot.block_snapshot.text());
-            log::info!("display text: {:?}", snapshot.text());
-
-            // Line boundaries
-            let buffer = &snapshot.buffer_snapshot;
-            for _ in 0..5 {
-                let row = rng.gen_range(0..=buffer.max_point().row);
-                let column = rng.gen_range(0..=buffer.line_len(row));
-                let point = buffer.clip_point(Point::new(row, column), Left);
-
-                let (prev_buffer_bound, prev_display_bound) = snapshot.prev_line_boundary(point);
-                let (next_buffer_bound, next_display_bound) = snapshot.next_line_boundary(point);
-
-                assert!(prev_buffer_bound <= point);
-                assert!(next_buffer_bound >= point);
-                assert_eq!(prev_buffer_bound.column, 0);
-                assert_eq!(prev_display_bound.column(), 0);
-                if next_buffer_bound < buffer.max_point() {
-                    assert_eq!(buffer.chars_at(next_buffer_bound).next(), Some('\n'));
-                }
-
-                assert_eq!(
-                    prev_display_bound,
-                    prev_buffer_bound.to_display_point(&snapshot),
-                    "row boundary before {:?}. reported buffer row boundary: {:?}",
-                    point,
-                    prev_buffer_bound
-                );
-                assert_eq!(
-                    next_display_bound,
-                    next_buffer_bound.to_display_point(&snapshot),
-                    "display row boundary after {:?}. reported buffer row boundary: {:?}",
-                    point,
-                    next_buffer_bound
-                );
-                assert_eq!(
-                    prev_buffer_bound,
-                    prev_display_bound.to_point(&snapshot),
-                    "row boundary before {:?}. reported display row boundary: {:?}",
-                    point,
-                    prev_display_bound
-                );
-                assert_eq!(
-                    next_buffer_bound,
-                    next_display_bound.to_point(&snapshot),
-                    "row boundary after {:?}. reported display row boundary: {:?}",
-                    point,
-                    next_display_bound
-                );
-            }
-
-            // Movement
-            let min_point = snapshot.clip_point(DisplayPoint::new(0, 0), Left);
-            let max_point = snapshot.clip_point(snapshot.max_point(), Right);
-            for _ in 0..5 {
-                let row = rng.gen_range(0..=snapshot.max_point().row());
-                let column = rng.gen_range(0..=snapshot.line_len(row));
-                let point = snapshot.clip_point(DisplayPoint::new(row, column), Left);
-
-                log::info!("Moving from point {:?}", point);
-
-                let moved_right = movement::right(&snapshot, point);
-                log::info!("Right {:?}", moved_right);
-                if point < max_point {
-                    assert!(moved_right > point);
-                    if point.column() == snapshot.line_len(point.row())
-                        || snapshot.soft_wrap_indent(point.row()).is_some()
-                            && point.column() == snapshot.line_len(point.row()) - 1
-                    {
-                        assert!(moved_right.row() > point.row());
-                    }
-                } else {
-                    assert_eq!(moved_right, point);
-                }
-
-                let moved_left = movement::left(&snapshot, point);
-                log::info!("Left {:?}", moved_left);
-                if point > min_point {
-                    assert!(moved_left < point);
-                    if point.column() == 0 {
-                        assert!(moved_left.row() < point.row());
-                    }
-                } else {
-                    assert_eq!(moved_left, point);
-                }
-            }
-        }
-    }
-
-    #[gpui::test(retries = 5)]
-    async fn test_soft_wraps(cx: &mut gpui::TestAppContext) {
-        cx.foreground().set_block_on_ticks(usize::MAX..=usize::MAX);
-        cx.update(|cx| {
-            init_test(cx, |_| {});
-        });
-
-        let mut cx = EditorTestContext::new(cx).await;
-        let editor = cx.editor.clone();
-        let window = cx.window.clone();
-
-        cx.update_window(window, |cx| {
-            let text_layout_details =
-                editor.read_with(cx, |editor, cx| editor.text_layout_details(cx));
-
-            let font_cache = cx.font_cache().clone();
-
-            let family_id = font_cache
-                .load_family(&["Helvetica"], &Default::default())
-                .unwrap();
-            let font_id = font_cache
-                .select_font(family_id, &Default::default())
-                .unwrap();
-            let font_size = 12.0;
-            let wrap_width = Some(64.);
-
-            let text = "one two three four five\nsix seven eight";
-            let buffer = MultiBuffer::build_simple(text, cx);
-            let map = cx.add_model(|cx| {
-                DisplayMap::new(buffer.clone(), font_id, font_size, wrap_width, 1, 1, cx)
-            });
-
-            let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
-            assert_eq!(
-                snapshot.text_chunks(0).collect::<String>(),
-                "one two \nthree four \nfive\nsix seven \neight"
-            );
-            assert_eq!(
-                snapshot.clip_point(DisplayPoint::new(0, 8), Bias::Left),
-                DisplayPoint::new(0, 7)
-            );
-            assert_eq!(
-                snapshot.clip_point(DisplayPoint::new(0, 8), Bias::Right),
-                DisplayPoint::new(1, 0)
-            );
-            assert_eq!(
-                movement::right(&snapshot, DisplayPoint::new(0, 7)),
-                DisplayPoint::new(1, 0)
-            );
-            assert_eq!(
-                movement::left(&snapshot, DisplayPoint::new(1, 0)),
-                DisplayPoint::new(0, 7)
-            );
-
-            let x = snapshot.x_for_point(DisplayPoint::new(1, 10), &text_layout_details);
-            assert_eq!(
-                movement::up(
-                    &snapshot,
-                    DisplayPoint::new(1, 10),
-                    SelectionGoal::None,
-                    false,
-                    &text_layout_details,
-                ),
-                (
-                    DisplayPoint::new(0, 7),
-                    SelectionGoal::HorizontalPosition(x)
-                )
-            );
-            assert_eq!(
-                movement::down(
-                    &snapshot,
-                    DisplayPoint::new(0, 7),
-                    SelectionGoal::HorizontalPosition(x),
-                    false,
-                    &text_layout_details
-                ),
-                (
-                    DisplayPoint::new(1, 10),
-                    SelectionGoal::HorizontalPosition(x)
-                )
-            );
-            assert_eq!(
-                movement::down(
-                    &snapshot,
-                    DisplayPoint::new(1, 10),
-                    SelectionGoal::HorizontalPosition(x),
-                    false,
-                    &text_layout_details
-                ),
-                (
-                    DisplayPoint::new(2, 4),
-                    SelectionGoal::HorizontalPosition(x)
-                )
-            );
-
-            let ix = snapshot.buffer_snapshot.text().find("seven").unwrap();
-            buffer.update(cx, |buffer, cx| {
-                buffer.edit([(ix..ix, "and ")], None, cx);
-            });
-
-            let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
-            assert_eq!(
-                snapshot.text_chunks(1).collect::<String>(),
-                "three four \nfive\nsix and \nseven eight"
-            );
-
-            // Re-wrap on font size changes
-            map.update(cx, |map, cx| map.set_font(font_id, font_size + 3., cx));
-
-            let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
-            assert_eq!(
-                snapshot.text_chunks(1).collect::<String>(),
-                "three \nfour five\nsix and \nseven \neight"
-            )
-        });
-    }
-
-    #[gpui::test]
-    fn test_text_chunks(cx: &mut gpui::AppContext) {
-        init_test(cx, |_| {});
-
-        let text = sample_text(6, 6, 'a');
-        let buffer = MultiBuffer::build_simple(&text, cx);
-        let family_id = cx
-            .font_cache()
-            .load_family(&["Helvetica"], &Default::default())
-            .unwrap();
-        let font_id = cx
-            .font_cache()
-            .select_font(family_id, &Default::default())
-            .unwrap();
-        let font_size = 14.0;
-        let map =
-            cx.add_model(|cx| DisplayMap::new(buffer.clone(), font_id, font_size, None, 1, 1, cx));
-
-        buffer.update(cx, |buffer, cx| {
-            buffer.edit(
-                vec![
-                    (Point::new(1, 0)..Point::new(1, 0), "\t"),
-                    (Point::new(1, 1)..Point::new(1, 1), "\t"),
-                    (Point::new(2, 1)..Point::new(2, 1), "\t"),
-                ],
-                None,
-                cx,
-            )
-        });
-
-        assert_eq!(
-            map.update(cx, |map, cx| map.snapshot(cx))
-                .text_chunks(1)
-                .collect::<String>()
-                .lines()
-                .next(),
-            Some("    b   bbbbb")
-        );
-        assert_eq!(
-            map.update(cx, |map, cx| map.snapshot(cx))
-                .text_chunks(2)
-                .collect::<String>()
-                .lines()
-                .next(),
-            Some("c   ccccc")
-        );
-    }
-
-    #[gpui::test]
-    async fn test_chunks(cx: &mut gpui::TestAppContext) {
-        use unindent::Unindent as _;
-
-        let text = r#"
-            fn outer() {}
-
-            mod module {
-                fn inner() {}
-            }"#
-        .unindent();
-
-        let theme = SyntaxTheme::new(vec![
-            ("mod.body".to_string(), Color::red().into()),
-            ("fn.name".to_string(), Color::blue().into()),
-        ]);
-        let language = Arc::new(
-            Language::new(
-                LanguageConfig {
-                    name: "Test".into(),
-                    path_suffixes: vec![".test".to_string()],
-                    ..Default::default()
-                },
-                Some(tree_sitter_rust::language()),
-            )
-            .with_highlights_query(
-                r#"
-                (mod_item name: (identifier) body: _ @mod.body)
-                (function_item name: (identifier) @fn.name)
-                "#,
-            )
-            .unwrap(),
-        );
-        language.set_theme(&theme);
-
-        cx.update(|cx| init_test(cx, |s| s.defaults.tab_size = Some(2.try_into().unwrap())));
-
-        let buffer = cx
-            .add_model(|cx| Buffer::new(0, cx.model_id() as u64, text).with_language(language, cx));
-        buffer.condition(cx, |buf, _| !buf.is_parsing()).await;
-        let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
-
-        let font_cache = cx.font_cache();
-        let family_id = font_cache
-            .load_family(&["Helvetica"], &Default::default())
-            .unwrap();
-        let font_id = font_cache
-            .select_font(family_id, &Default::default())
-            .unwrap();
-        let font_size = 14.0;
-
-        let map = cx.add_model(|cx| DisplayMap::new(buffer, font_id, font_size, None, 1, 1, cx));
-        assert_eq!(
-            cx.update(|cx| syntax_chunks(0..5, &map, &theme, cx)),
-            vec![
-                ("fn ".to_string(), None),
-                ("outer".to_string(), Some(Color::blue())),
-                ("() {}\n\nmod module ".to_string(), None),
-                ("{\n    fn ".to_string(), Some(Color::red())),
-                ("inner".to_string(), Some(Color::blue())),
-                ("() {}\n}".to_string(), Some(Color::red())),
-            ]
-        );
-        assert_eq!(
-            cx.update(|cx| syntax_chunks(3..5, &map, &theme, cx)),
-            vec![
-                ("    fn ".to_string(), Some(Color::red())),
-                ("inner".to_string(), Some(Color::blue())),
-                ("() {}\n}".to_string(), Some(Color::red())),
-            ]
-        );
-
-        map.update(cx, |map, cx| {
-            map.fold(vec![Point::new(0, 6)..Point::new(3, 2)], cx)
-        });
-        assert_eq!(
-            cx.update(|cx| syntax_chunks(0..2, &map, &theme, cx)),
-            vec![
-                ("fn ".to_string(), None),
-                ("out".to_string(), Some(Color::blue())),
-                ("⋯".to_string(), None),
-                ("  fn ".to_string(), Some(Color::red())),
-                ("inner".to_string(), Some(Color::blue())),
-                ("() {}\n}".to_string(), Some(Color::red())),
-            ]
-        );
-    }
-
-    #[gpui::test]
-    async fn test_chunks_with_soft_wrapping(cx: &mut gpui::TestAppContext) {
-        use unindent::Unindent as _;
-
-        cx.foreground().set_block_on_ticks(usize::MAX..=usize::MAX);
-
-        let text = r#"
-            fn outer() {}
-
-            mod module {
-                fn inner() {}
-            }"#
-        .unindent();
-
-        let theme = SyntaxTheme::new(vec![
-            ("mod.body".to_string(), Color::red().into()),
-            ("fn.name".to_string(), Color::blue().into()),
-        ]);
-        let language = Arc::new(
-            Language::new(
-                LanguageConfig {
-                    name: "Test".into(),
-                    path_suffixes: vec![".test".to_string()],
-                    ..Default::default()
-                },
-                Some(tree_sitter_rust::language()),
-            )
-            .with_highlights_query(
-                r#"
-                (mod_item name: (identifier) body: _ @mod.body)
-                (function_item name: (identifier) @fn.name)
-                "#,
-            )
-            .unwrap(),
-        );
-        language.set_theme(&theme);
-
-        cx.update(|cx| init_test(cx, |_| {}));
-
-        let buffer = cx
-            .add_model(|cx| Buffer::new(0, cx.model_id() as u64, text).with_language(language, cx));
-        buffer.condition(cx, |buf, _| !buf.is_parsing()).await;
-        let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
-
-        let font_cache = cx.font_cache();
-
-        let family_id = font_cache
-            .load_family(&["Courier"], &Default::default())
-            .unwrap();
-        let font_id = font_cache
-            .select_font(family_id, &Default::default())
-            .unwrap();
-        let font_size = 16.0;
-
-        let map =
-            cx.add_model(|cx| DisplayMap::new(buffer, font_id, font_size, Some(40.0), 1, 1, cx));
-        assert_eq!(
-            cx.update(|cx| syntax_chunks(0..5, &map, &theme, cx)),
-            [
-                ("fn \n".to_string(), None),
-                ("oute\nr".to_string(), Some(Color::blue())),
-                ("() \n{}\n\n".to_string(), None),
-            ]
-        );
-        assert_eq!(
-            cx.update(|cx| syntax_chunks(3..5, &map, &theme, cx)),
-            [("{}\n\n".to_string(), None)]
-        );
-
-        map.update(cx, |map, cx| {
-            map.fold(vec![Point::new(0, 6)..Point::new(3, 2)], cx)
-        });
-        assert_eq!(
-            cx.update(|cx| syntax_chunks(1..4, &map, &theme, cx)),
-            [
-                ("out".to_string(), Some(Color::blue())),
-                ("⋯\n".to_string(), None),
-                ("  \nfn ".to_string(), Some(Color::red())),
-                ("i\n".to_string(), Some(Color::blue()))
-            ]
-        );
-    }
-
-    #[gpui::test]
-    async fn test_chunks_with_text_highlights(cx: &mut gpui::TestAppContext) {
-        cx.update(|cx| init_test(cx, |_| {}));
-
-        let theme = SyntaxTheme::new(vec![
-            ("operator".to_string(), Color::red().into()),
-            ("string".to_string(), Color::green().into()),
-        ]);
-        let language = Arc::new(
-            Language::new(
-                LanguageConfig {
-                    name: "Test".into(),
-                    path_suffixes: vec![".test".to_string()],
-                    ..Default::default()
-                },
-                Some(tree_sitter_rust::language()),
-            )
-            .with_highlights_query(
-                r#"
-                ":" @operator
-                (string_literal) @string
-                "#,
-            )
-            .unwrap(),
-        );
-        language.set_theme(&theme);
-
-        let (text, highlighted_ranges) = marked_text_ranges(r#"constˇ «a»: B = "c «d»""#, false);
-
-        let buffer = cx
-            .add_model(|cx| Buffer::new(0, cx.model_id() as u64, text).with_language(language, cx));
-        buffer.condition(cx, |buf, _| !buf.is_parsing()).await;
-
-        let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
-        let buffer_snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx));
-
-        let font_cache = cx.font_cache();
-        let family_id = font_cache
-            .load_family(&["Courier"], &Default::default())
-            .unwrap();
-        let font_id = font_cache
-            .select_font(family_id, &Default::default())
-            .unwrap();
-        let font_size = 16.0;
-        let map = cx.add_model(|cx| DisplayMap::new(buffer, font_id, font_size, None, 1, 1, cx));
-
-        enum MyType {}
-
-        let style = HighlightStyle {
-            color: Some(Color::blue()),
-            ..Default::default()
-        };
-
-        map.update(cx, |map, _cx| {
-            map.highlight_text(
-                TypeId::of::<MyType>(),
-                highlighted_ranges
-                    .into_iter()
-                    .map(|range| {
-                        buffer_snapshot.anchor_before(range.start)
-                            ..buffer_snapshot.anchor_before(range.end)
-                    })
-                    .collect(),
-                style,
-            );
-        });
-
-        assert_eq!(
-            cx.update(|cx| chunks(0..10, &map, &theme, cx)),
-            [
-                ("const ".to_string(), None, None),
-                ("a".to_string(), None, Some(Color::blue())),
-                (":".to_string(), Some(Color::red()), None),
-                (" B = ".to_string(), None, None),
-                ("\"c ".to_string(), Some(Color::green()), None),
-                ("d".to_string(), Some(Color::green()), Some(Color::blue())),
-                ("\"".to_string(), Some(Color::green()), None),
-            ]
-        );
-    }
-
-    #[gpui::test]
-    fn test_clip_point(cx: &mut gpui::AppContext) {
-        init_test(cx, |_| {});
-
-        fn assert(text: &str, shift_right: bool, bias: Bias, cx: &mut gpui::AppContext) {
-            let (unmarked_snapshot, mut markers) = marked_display_snapshot(text, cx);
-
-            match bias {
-                Bias::Left => {
-                    if shift_right {
-                        *markers[1].column_mut() += 1;
-                    }
-
-                    assert_eq!(unmarked_snapshot.clip_point(markers[1], bias), markers[0])
-                }
-                Bias::Right => {
-                    if shift_right {
-                        *markers[0].column_mut() += 1;
-                    }
-
-                    assert_eq!(unmarked_snapshot.clip_point(markers[0], bias), markers[1])
-                }
-            };
-        }
-
-        use Bias::{Left, Right};
-        assert("ˇˇα", false, Left, cx);
-        assert("ˇˇα", true, Left, cx);
-        assert("ˇˇα", false, Right, cx);
-        assert("ˇαˇ", true, Right, cx);
-        assert("ˇˇ✋", false, Left, cx);
-        assert("ˇˇ✋", true, Left, cx);
-        assert("ˇˇ✋", false, Right, cx);
-        assert("ˇ✋ˇ", true, Right, cx);
-        assert("ˇˇ🍐", false, Left, cx);
-        assert("ˇˇ🍐", true, Left, cx);
-        assert("ˇˇ🍐", false, Right, cx);
-        assert("ˇ🍐ˇ", true, Right, cx);
-        assert("ˇˇ\t", false, Left, cx);
-        assert("ˇˇ\t", true, Left, cx);
-        assert("ˇˇ\t", false, Right, cx);
-        assert("ˇ\tˇ", true, Right, cx);
-        assert(" ˇˇ\t", false, Left, cx);
-        assert(" ˇˇ\t", true, Left, cx);
-        assert(" ˇˇ\t", false, Right, cx);
-        assert(" ˇ\tˇ", true, Right, cx);
-        assert("   ˇˇ\t", false, Left, cx);
-        assert("   ˇˇ\t", false, Right, cx);
-    }
-
-    #[gpui::test]
-    fn test_clip_at_line_ends(cx: &mut gpui::AppContext) {
-        init_test(cx, |_| {});
-
-        fn assert(text: &str, cx: &mut gpui::AppContext) {
-            let (mut unmarked_snapshot, markers) = marked_display_snapshot(text, cx);
-            unmarked_snapshot.clip_at_line_ends = true;
-            assert_eq!(
-                unmarked_snapshot.clip_point(markers[1], Bias::Left),
-                markers[0]
-            );
-        }
-
-        assert("ˇˇ", cx);
-        assert("ˇaˇ", cx);
-        assert("aˇbˇ", cx);
-        assert("aˇαˇ", cx);
-    }
-
-    #[gpui::test]
-    fn test_tabs_with_multibyte_chars(cx: &mut gpui::AppContext) {
-        init_test(cx, |_| {});
-
-        let text = "✅\t\tα\nβ\t\n🏀β\t\tγ";
-        let buffer = MultiBuffer::build_simple(text, cx);
-        let font_cache = cx.font_cache();
-        let family_id = font_cache
-            .load_family(&["Helvetica"], &Default::default())
-            .unwrap();
-        let font_id = font_cache
-            .select_font(family_id, &Default::default())
-            .unwrap();
-        let font_size = 14.0;
-
-        let map =
-            cx.add_model(|cx| DisplayMap::new(buffer.clone(), font_id, font_size, None, 1, 1, cx));
-        let map = map.update(cx, |map, cx| map.snapshot(cx));
-        assert_eq!(map.text(), "✅       α\nβ   \n🏀β      γ");
-        assert_eq!(
-            map.text_chunks(0).collect::<String>(),
-            "✅       α\nβ   \n🏀β      γ"
-        );
-        assert_eq!(map.text_chunks(1).collect::<String>(), "β   \n🏀β      γ");
-        assert_eq!(map.text_chunks(2).collect::<String>(), "🏀β      γ");
-
-        let point = Point::new(0, "✅\t\t".len() as u32);
-        let display_point = DisplayPoint::new(0, "✅       ".len() as u32);
-        assert_eq!(point.to_display_point(&map), display_point);
-        assert_eq!(display_point.to_point(&map), point);
-
-        let point = Point::new(1, "β\t".len() as u32);
-        let display_point = DisplayPoint::new(1, "β   ".len() as u32);
-        assert_eq!(point.to_display_point(&map), display_point);
-        assert_eq!(display_point.to_point(&map), point,);
-
-        let point = Point::new(2, "🏀β\t\t".len() as u32);
-        let display_point = DisplayPoint::new(2, "🏀β      ".len() as u32);
-        assert_eq!(point.to_display_point(&map), display_point);
-        assert_eq!(display_point.to_point(&map), point,);
-
-        // Display points inside of expanded tabs
-        assert_eq!(
-            DisplayPoint::new(0, "✅      ".len() as u32).to_point(&map),
-            Point::new(0, "✅\t".len() as u32),
-        );
-        assert_eq!(
-            DisplayPoint::new(0, "✅ ".len() as u32).to_point(&map),
-            Point::new(0, "✅".len() as u32),
-        );
-
-        // Clipping display points inside of multi-byte characters
-        assert_eq!(
-            map.clip_point(DisplayPoint::new(0, "✅".len() as u32 - 1), Left),
-            DisplayPoint::new(0, 0)
-        );
-        assert_eq!(
-            map.clip_point(DisplayPoint::new(0, "✅".len() as u32 - 1), Bias::Right),
-            DisplayPoint::new(0, "✅".len() as u32)
-        );
-    }
-
-    #[gpui::test]
-    fn test_max_point(cx: &mut gpui::AppContext) {
-        init_test(cx, |_| {});
-
-        let buffer = MultiBuffer::build_simple("aaa\n\t\tbbb", cx);
-        let font_cache = cx.font_cache();
-        let family_id = font_cache
-            .load_family(&["Helvetica"], &Default::default())
-            .unwrap();
-        let font_id = font_cache
-            .select_font(family_id, &Default::default())
-            .unwrap();
-        let font_size = 14.0;
-        let map =
-            cx.add_model(|cx| DisplayMap::new(buffer.clone(), font_id, font_size, None, 1, 1, cx));
-        assert_eq!(
-            map.update(cx, |map, cx| map.snapshot(cx)).max_point(),
-            DisplayPoint::new(1, 11)
-        )
-    }
-
-    fn syntax_chunks<'a>(
-        rows: Range<u32>,
-        map: &ModelHandle<DisplayMap>,
-        theme: &'a SyntaxTheme,
-        cx: &mut AppContext,
-    ) -> Vec<(String, Option<Color>)> {
-        chunks(rows, map, theme, cx)
-            .into_iter()
-            .map(|(text, color, _)| (text, color))
-            .collect()
-    }
-
-    fn chunks<'a>(
-        rows: Range<u32>,
-        map: &ModelHandle<DisplayMap>,
-        theme: &'a SyntaxTheme,
-        cx: &mut AppContext,
-    ) -> Vec<(String, Option<Color>, Option<Color>)> {
-        let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
-        let mut chunks: Vec<(String, Option<Color>, Option<Color>)> = Vec::new();
-        for chunk in snapshot.chunks(rows, true, None, None) {
-            let syntax_color = chunk
-                .syntax_highlight_id
-                .and_then(|id| id.style(theme)?.color);
-            let highlight_color = chunk.highlight_style.and_then(|style| style.color);
-            if let Some((last_chunk, last_syntax_color, last_highlight_color)) = chunks.last_mut() {
-                if syntax_color == *last_syntax_color && highlight_color == *last_highlight_color {
-                    last_chunk.push_str(chunk.text);
-                    continue;
-                }
-            }
-            chunks.push((chunk.text.to_string(), syntax_color, highlight_color));
-        }
-        chunks
-    }
-
-    fn init_test(cx: &mut AppContext, f: impl Fn(&mut AllLanguageSettingsContent)) {
-        cx.foreground().forbid_parking();
-        cx.set_global(SettingsStore::test(cx));
-        language::init(cx);
-        crate::init(cx);
-        Project::init_settings(cx);
-        theme::init((), cx);
-        cx.update_global::<SettingsStore, _, _>(|store, cx| {
-            store.update_user_settings::<AllLanguageSettings>(cx, f);
-        });
-    }
-}
+// #[cfg(test)]
+// pub mod tests {
+//     use super::*;
+//     use crate::{
+//         movement,
+//         test::{editor_test_context::EditorTestContext, marked_display_snapshot},
+//     };
+//     use gpui::{AppContext, Hsla};
+//     use language::{
+//         language_settings::{AllLanguageSettings, AllLanguageSettingsContent},
+//         Buffer, Language, LanguageConfig, SelectionGoal,
+//     };
+//     use project::Project;
+//     use rand::{prelude::*, Rng};
+//     use settings::SettingsStore;
+//     use smol::stream::StreamExt;
+//     use std::{env, sync::Arc};
+//     use theme::SyntaxTheme;
+//     use util::test::{marked_text_ranges, sample_text};
+//     use Bias::*;
+
+//     #[gpui::test(iterations = 100)]
+//     async fn test_random_display_map(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
+//         cx.foreground().set_block_on_ticks(0..=50);
+//         cx.foreground().forbid_parking();
+//         let operations = env::var("OPERATIONS")
+//             .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
+//             .unwrap_or(10);
+
+//         let font_cache = cx.font_cache().clone();
+//         let mut tab_size = rng.gen_range(1..=4);
+//         let buffer_start_excerpt_header_height = rng.gen_range(1..=5);
+//         let excerpt_header_height = rng.gen_range(1..=5);
+//         let family_id = font_cache
+//             .load_family(&["Helvetica"], &Default::default())
+//             .unwrap();
+//         let font_id = font_cache
+//             .select_font(family_id, &Default::default())
+//             .unwrap();
+//         let font_size = 14.0;
+//         let max_wrap_width = 300.0;
+//         let mut wrap_width = if rng.gen_bool(0.1) {
+//             None
+//         } else {
+//             Some(rng.gen_range(0.0..=max_wrap_width))
+//         };
+
+//         log::info!("tab size: {}", tab_size);
+//         log::info!("wrap width: {:?}", wrap_width);
+
+//         cx.update(|cx| {
+//             init_test(cx, |s| s.defaults.tab_size = NonZeroU32::new(tab_size));
+//         });
+
+//         let buffer = cx.update(|cx| {
+//             if rng.gen() {
+//                 let len = rng.gen_range(0..10);
+//                 let text = util::RandomCharIter::new(&mut rng)
+//                     .take(len)
+//                     .collect::<String>();
+//                 MultiBuffer::build_simple(&text, cx)
+//             } else {
+//                 MultiBuffer::build_random(&mut rng, cx)
+//             }
+//         });
+
+//         let map = cx.add_model(|cx| {
+//             DisplayMap::new(
+//                 buffer.clone(),
+//                 font_id,
+//                 font_size,
+//                 wrap_width,
+//                 buffer_start_excerpt_header_height,
+//                 excerpt_header_height,
+//                 cx,
+//             )
+//         });
+//         let mut notifications = observe(&map, cx);
+//         let mut fold_count = 0;
+//         let mut blocks = Vec::new();
+
+//         let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
+//         log::info!("buffer text: {:?}", snapshot.buffer_snapshot.text());
+//         log::info!("fold text: {:?}", snapshot.fold_snapshot.text());
+//         log::info!("tab text: {:?}", snapshot.tab_snapshot.text());
+//         log::info!("wrap text: {:?}", snapshot.wrap_snapshot.text());
+//         log::info!("block text: {:?}", snapshot.block_snapshot.text());
+//         log::info!("display text: {:?}", snapshot.text());
+
+//         for _i in 0..operations {
+//             match rng.gen_range(0..100) {
+//                 0..=19 => {
+//                     wrap_width = if rng.gen_bool(0.2) {
+//                         None
+//                     } else {
+//                         Some(rng.gen_range(0.0..=max_wrap_width))
+//                     };
+//                     log::info!("setting wrap width to {:?}", wrap_width);
+//                     map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
+//                 }
+//                 20..=29 => {
+//                     let mut tab_sizes = vec![1, 2, 3, 4];
+//                     tab_sizes.remove((tab_size - 1) as usize);
+//                     tab_size = *tab_sizes.choose(&mut rng).unwrap();
+//                     log::info!("setting tab size to {:?}", tab_size);
+//                     cx.update(|cx| {
+//                         cx.update_global::<SettingsStore, _, _>(|store, cx| {
+//                             store.update_user_settings::<AllLanguageSettings>(cx, |s| {
+//                                 s.defaults.tab_size = NonZeroU32::new(tab_size);
+//                             });
+//                         });
+//                     });
+//                 }
+//                 30..=44 => {
+//                     map.update(cx, |map, cx| {
+//                         if rng.gen() || blocks.is_empty() {
+//                             let buffer = map.snapshot(cx).buffer_snapshot;
+//                             let block_properties = (0..rng.gen_range(1..=1))
+//                                 .map(|_| {
+//                                     let position =
+//                                         buffer.anchor_after(buffer.clip_offset(
+//                                             rng.gen_range(0..=buffer.len()),
+//                                             Bias::Left,
+//                                         ));
+
+//                                     let disposition = if rng.gen() {
+//                                         BlockDisposition::Above
+//                                     } else {
+//                                         BlockDisposition::Below
+//                                     };
+//                                     let height = rng.gen_range(1..5);
+//                                     log::info!(
+//                                         "inserting block {:?} {:?} with height {}",
+//                                         disposition,
+//                                         position.to_point(&buffer),
+//                                         height
+//                                     );
+//                                     BlockProperties {
+//                                         style: BlockStyle::Fixed,
+//                                         position,
+//                                         height,
+//                                         disposition,
+//                                         render: Arc::new(|_| Empty::new().into_any()),
+//                                     }
+//                                 })
+//                                 .collect::<Vec<_>>();
+//                             blocks.extend(map.insert_blocks(block_properties, cx));
+//                         } else {
+//                             blocks.shuffle(&mut rng);
+//                             let remove_count = rng.gen_range(1..=4.min(blocks.len()));
+//                             let block_ids_to_remove = (0..remove_count)
+//                                 .map(|_| blocks.remove(rng.gen_range(0..blocks.len())))
+//                                 .collect();
+//                             log::info!("removing block ids {:?}", block_ids_to_remove);
+//                             map.remove_blocks(block_ids_to_remove, cx);
+//                         }
+//                     });
+//                 }
+//                 45..=79 => {
+//                     let mut ranges = Vec::new();
+//                     for _ in 0..rng.gen_range(1..=3) {
+//                         buffer.read_with(cx, |buffer, cx| {
+//                             let buffer = buffer.read(cx);
+//                             let end = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Right);
+//                             let start = buffer.clip_offset(rng.gen_range(0..=end), Left);
+//                             ranges.push(start..end);
+//                         });
+//                     }
+
+//                     if rng.gen() && fold_count > 0 {
+//                         log::info!("unfolding ranges: {:?}", ranges);
+//                         map.update(cx, |map, cx| {
+//                             map.unfold(ranges, true, cx);
+//                         });
+//                     } else {
+//                         log::info!("folding ranges: {:?}", ranges);
+//                         map.update(cx, |map, cx| {
+//                             map.fold(ranges, cx);
+//                         });
+//                     }
+//                 }
+//                 _ => {
+//                     buffer.update(cx, |buffer, cx| buffer.randomly_mutate(&mut rng, 5, cx));
+//                 }
+//             }
+
+//             if map.read_with(cx, |map, cx| map.is_rewrapping(cx)) {
+//                 notifications.next().await.unwrap();
+//             }
+
+//             let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
+//             fold_count = snapshot.fold_count();
+//             log::info!("buffer text: {:?}", snapshot.buffer_snapshot.text());
+//             log::info!("fold text: {:?}", snapshot.fold_snapshot.text());
+//             log::info!("tab text: {:?}", snapshot.tab_snapshot.text());
+//             log::info!("wrap text: {:?}", snapshot.wrap_snapshot.text());
+//             log::info!("block text: {:?}", snapshot.block_snapshot.text());
+//             log::info!("display text: {:?}", snapshot.text());
+
+//             // Line boundaries
+//             let buffer = &snapshot.buffer_snapshot;
+//             for _ in 0..5 {
+//                 let row = rng.gen_range(0..=buffer.max_point().row);
+//                 let column = rng.gen_range(0..=buffer.line_len(row));
+//                 let point = buffer.clip_point(Point::new(row, column), Left);
+
+//                 let (prev_buffer_bound, prev_display_bound) = snapshot.prev_line_boundary(point);
+//                 let (next_buffer_bound, next_display_bound) = snapshot.next_line_boundary(point);
+
+//                 assert!(prev_buffer_bound <= point);
+//                 assert!(next_buffer_bound >= point);
+//                 assert_eq!(prev_buffer_bound.column, 0);
+//                 assert_eq!(prev_display_bound.column(), 0);
+//                 if next_buffer_bound < buffer.max_point() {
+//                     assert_eq!(buffer.chars_at(next_buffer_bound).next(), Some('\n'));
+//                 }
+
+//                 assert_eq!(
+//                     prev_display_bound,
+//                     prev_buffer_bound.to_display_point(&snapshot),
+//                     "row boundary before {:?}. reported buffer row boundary: {:?}",
+//                     point,
+//                     prev_buffer_bound
+//                 );
+//                 assert_eq!(
+//                     next_display_bound,
+//                     next_buffer_bound.to_display_point(&snapshot),
+//                     "display row boundary after {:?}. reported buffer row boundary: {:?}",
+//                     point,
+//                     next_buffer_bound
+//                 );
+//                 assert_eq!(
+//                     prev_buffer_bound,
+//                     prev_display_bound.to_point(&snapshot),
+//                     "row boundary before {:?}. reported display row boundary: {:?}",
+//                     point,
+//                     prev_display_bound
+//                 );
+//                 assert_eq!(
+//                     next_buffer_bound,
+//                     next_display_bound.to_point(&snapshot),
+//                     "row boundary after {:?}. reported display row boundary: {:?}",
+//                     point,
+//                     next_display_bound
+//                 );
+//             }
+
+//             // Movement
+//             let min_point = snapshot.clip_point(DisplayPoint::new(0, 0), Left);
+//             let max_point = snapshot.clip_point(snapshot.max_point(), Right);
+//             for _ in 0..5 {
+//                 let row = rng.gen_range(0..=snapshot.max_point().row());
+//                 let column = rng.gen_range(0..=snapshot.line_len(row));
+//                 let point = snapshot.clip_point(DisplayPoint::new(row, column), Left);
+
+//                 log::info!("Moving from point {:?}", point);
+
+//                 let moved_right = movement::right(&snapshot, point);
+//                 log::info!("Right {:?}", moved_right);
+//                 if point < max_point {
+//                     assert!(moved_right > point);
+//                     if point.column() == snapshot.line_len(point.row())
+//                         || snapshot.soft_wrap_indent(point.row()).is_some()
+//                             && point.column() == snapshot.line_len(point.row()) - 1
+//                     {
+//                         assert!(moved_right.row() > point.row());
+//                     }
+//                 } else {
+//                     assert_eq!(moved_right, point);
+//                 }
+
+//                 let moved_left = movement::left(&snapshot, point);
+//                 log::info!("Left {:?}", moved_left);
+//                 if point > min_point {
+//                     assert!(moved_left < point);
+//                     if point.column() == 0 {
+//                         assert!(moved_left.row() < point.row());
+//                     }
+//                 } else {
+//                     assert_eq!(moved_left, point);
+//                 }
+//             }
+//         }
+//     }
+
+//     #[gpui::test(retries = 5)]
+//     async fn test_soft_wraps(cx: &mut gpui::TestAppContext) {
+//         cx.foreground().set_block_on_ticks(usize::MAX..=usize::MAX);
+//         cx.update(|cx| {
+//             init_test(cx, |_| {});
+//         });
+
+//         let mut cx = EditorTestContext::new(cx).await;
+//         let editor = cx.editor.clone();
+//         let window = cx.window.clone();
+
+//         cx.update_window(window, |cx| {
+//             let text_layout_details =
+//                 editor.read_with(cx, |editor, cx| editor.text_layout_details(cx));
+
+//             let font_cache = cx.font_cache().clone();
+
+//             let family_id = font_cache
+//                 .load_family(&["Helvetica"], &Default::default())
+//                 .unwrap();
+//             let font_id = font_cache
+//                 .select_font(family_id, &Default::default())
+//                 .unwrap();
+//             let font_size = 12.0;
+//             let wrap_width = Some(64.);
+
+//             let text = "one two three four five\nsix seven eight";
+//             let buffer = MultiBuffer::build_simple(text, cx);
+//             let map = cx.add_model(|cx| {
+//                 DisplayMap::new(buffer.clone(), font_id, font_size, wrap_width, 1, 1, cx)
+//             });
+
+//             let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
+//             assert_eq!(
+//                 snapshot.text_chunks(0).collect::<String>(),
+//                 "one two \nthree four \nfive\nsix seven \neight"
+//             );
+//             assert_eq!(
+//                 snapshot.clip_point(DisplayPoint::new(0, 8), Bias::Left),
+//                 DisplayPoint::new(0, 7)
+//             );
+//             assert_eq!(
+//                 snapshot.clip_point(DisplayPoint::new(0, 8), Bias::Right),
+//                 DisplayPoint::new(1, 0)
+//             );
+//             assert_eq!(
+//                 movement::right(&snapshot, DisplayPoint::new(0, 7)),
+//                 DisplayPoint::new(1, 0)
+//             );
+//             assert_eq!(
+//                 movement::left(&snapshot, DisplayPoint::new(1, 0)),
+//                 DisplayPoint::new(0, 7)
+//             );
+
+//             let x = snapshot.x_for_point(DisplayPoint::new(1, 10), &text_layout_details);
+//             assert_eq!(
+//                 movement::up(
+//                     &snapshot,
+//                     DisplayPoint::new(1, 10),
+//                     SelectionGoal::None,
+//                     false,
+//                     &text_layout_details,
+//                 ),
+//                 (
+//                     DisplayPoint::new(0, 7),
+//                     SelectionGoal::HorizontalPosition(x)
+//                 )
+//             );
+//             assert_eq!(
+//                 movement::down(
+//                     &snapshot,
+//                     DisplayPoint::new(0, 7),
+//                     SelectionGoal::HorizontalPosition(x),
+//                     false,
+//                     &text_layout_details
+//                 ),
+//                 (
+//                     DisplayPoint::new(1, 10),
+//                     SelectionGoal::HorizontalPosition(x)
+//                 )
+//             );
+//             assert_eq!(
+//                 movement::down(
+//                     &snapshot,
+//                     DisplayPoint::new(1, 10),
+//                     SelectionGoal::HorizontalPosition(x),
+//                     false,
+//                     &text_layout_details
+//                 ),
+//                 (
+//                     DisplayPoint::new(2, 4),
+//                     SelectionGoal::HorizontalPosition(x)
+//                 )
+//             );
+
+//             let ix = snapshot.buffer_snapshot.text().find("seven").unwrap();
+//             buffer.update(cx, |buffer, cx| {
+//                 buffer.edit([(ix..ix, "and ")], None, cx);
+//             });
+
+//             let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
+//             assert_eq!(
+//                 snapshot.text_chunks(1).collect::<String>(),
+//                 "three four \nfive\nsix and \nseven eight"
+//             );
+
+//             // Re-wrap on font size changes
+//             map.update(cx, |map, cx| map.set_font(font_id, font_size + 3., cx));
+
+//             let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
+//             assert_eq!(
+//                 snapshot.text_chunks(1).collect::<String>(),
+//                 "three \nfour five\nsix and \nseven \neight"
+//             )
+//         });
+//     }
+
+//     #[gpui::test]
+//     fn test_text_chunks(cx: &mut gpui::AppContext) {
+//         init_test(cx, |_| {});
+
+//         let text = sample_text(6, 6, 'a');
+//         let buffer = MultiBuffer::build_simple(&text, cx);
+//         let family_id = cx
+//             .font_cache()
+//             .load_family(&["Helvetica"], &Default::default())
+//             .unwrap();
+//         let font_id = cx
+//             .font_cache()
+//             .select_font(family_id, &Default::default())
+//             .unwrap();
+//         let font_size = 14.0;
+//         let map =
+//             cx.add_model(|cx| DisplayMap::new(buffer.clone(), font_id, font_size, None, 1, 1, cx));
+
+//         buffer.update(cx, |buffer, cx| {
+//             buffer.edit(
+//                 vec![
+//                     (Point::new(1, 0)..Point::new(1, 0), "\t"),
+//                     (Point::new(1, 1)..Point::new(1, 1), "\t"),
+//                     (Point::new(2, 1)..Point::new(2, 1), "\t"),
+//                 ],
+//                 None,
+//                 cx,
+//             )
+//         });
+
+//         assert_eq!(
+//             map.update(cx, |map, cx| map.snapshot(cx))
+//                 .text_chunks(1)
+//                 .collect::<String>()
+//                 .lines()
+//                 .next(),
+//             Some("    b   bbbbb")
+//         );
+//         assert_eq!(
+//             map.update(cx, |map, cx| map.snapshot(cx))
+//                 .text_chunks(2)
+//                 .collect::<String>()
+//                 .lines()
+//                 .next(),
+//             Some("c   ccccc")
+//         );
+//     }
+
+//     #[gpui::test]
+//     async fn test_chunks(cx: &mut gpui::TestAppContext) {
+//         use unindent::Unindent as _;
+
+//         let text = r#"
+//             fn outer() {}
+
+//             mod module {
+//                 fn inner() {}
+//             }"#
+//         .unindent();
+
+//         let theme = SyntaxTheme::new(vec![
+//             ("mod.body".to_string(), Hsla::red().into()),
+//             ("fn.name".to_string(), Hsla::blue().into()),
+//         ]);
+//         let language = Arc::new(
+//             Language::new(
+//                 LanguageConfig {
+//                     name: "Test".into(),
+//                     path_suffixes: vec![".test".to_string()],
+//                     ..Default::default()
+//                 },
+//                 Some(tree_sitter_rust::language()),
+//             )
+//             .with_highlights_query(
+//                 r#"
+//                 (mod_item name: (identifier) body: _ @mod.body)
+//                 (function_item name: (identifier) @fn.name)
+//                 "#,
+//             )
+//             .unwrap(),
+//         );
+//         language.set_theme(&theme);
+
+//         cx.update(|cx| init_test(cx, |s| s.defaults.tab_size = Some(2.try_into().unwrap())));
+
+//         let buffer = cx
+//             .add_model(|cx| Buffer::new(0, cx.model_id() as u64, text).with_language(language, cx));
+//         buffer.condition(cx, |buf, _| !buf.is_parsing()).await;
+//         let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
+
+//         let font_cache = cx.font_cache();
+//         let family_id = font_cache
+//             .load_family(&["Helvetica"], &Default::default())
+//             .unwrap();
+//         let font_id = font_cache
+//             .select_font(family_id, &Default::default())
+//             .unwrap();
+//         let font_size = 14.0;
+
+//         let map = cx.add_model(|cx| DisplayMap::new(buffer, font_id, font_size, None, 1, 1, cx));
+//         assert_eq!(
+//             cx.update(|cx| syntax_chunks(0..5, &map, &theme, cx)),
+//             vec![
+//                 ("fn ".to_string(), None),
+//                 ("outer".to_string(), Some(Hsla::blue())),
+//                 ("() {}\n\nmod module ".to_string(), None),
+//                 ("{\n    fn ".to_string(), Some(Hsla::red())),
+//                 ("inner".to_string(), Some(Hsla::blue())),
+//                 ("() {}\n}".to_string(), Some(Hsla::red())),
+//             ]
+//         );
+//         assert_eq!(
+//             cx.update(|cx| syntax_chunks(3..5, &map, &theme, cx)),
+//             vec![
+//                 ("    fn ".to_string(), Some(Hsla::red())),
+//                 ("inner".to_string(), Some(Hsla::blue())),
+//                 ("() {}\n}".to_string(), Some(Hsla::red())),
+//             ]
+//         );
+
+//         map.update(cx, |map, cx| {
+//             map.fold(vec![Point::new(0, 6)..Point::new(3, 2)], cx)
+//         });
+//         assert_eq!(
+//             cx.update(|cx| syntax_chunks(0..2, &map, &theme, cx)),
+//             vec![
+//                 ("fn ".to_string(), None),
+//                 ("out".to_string(), Some(Hsla::blue())),
+//                 ("⋯".to_string(), None),
+//                 ("  fn ".to_string(), Some(Hsla::red())),
+//                 ("inner".to_string(), Some(Hsla::blue())),
+//                 ("() {}\n}".to_string(), Some(Hsla::red())),
+//             ]
+//         );
+//     }
+
+//     #[gpui::test]
+//     async fn test_chunks_with_soft_wrapping(cx: &mut gpui::TestAppContext) {
+//         use unindent::Unindent as _;
+
+//         cx.foreground().set_block_on_ticks(usize::MAX..=usize::MAX);
+
+//         let text = r#"
+//             fn outer() {}
+
+//             mod module {
+//                 fn inner() {}
+//             }"#
+//         .unindent();
+
+//         let theme = SyntaxTheme::new(vec![
+//             ("mod.body".to_string(), Hsla::red().into()),
+//             ("fn.name".to_string(), Hsla::blue().into()),
+//         ]);
+//         let language = Arc::new(
+//             Language::new(
+//                 LanguageConfig {
+//                     name: "Test".into(),
+//                     path_suffixes: vec![".test".to_string()],
+//                     ..Default::default()
+//                 },
+//                 Some(tree_sitter_rust::language()),
+//             )
+//             .with_highlights_query(
+//                 r#"
+//                 (mod_item name: (identifier) body: _ @mod.body)
+//                 (function_item name: (identifier) @fn.name)
+//                 "#,
+//             )
+//             .unwrap(),
+//         );
+//         language.set_theme(&theme);
+
+//         cx.update(|cx| init_test(cx, |_| {}));
+
+//         let buffer = cx
+//             .add_model(|cx| Buffer::new(0, cx.model_id() as u64, text).with_language(language, cx));
+//         buffer.condition(cx, |buf, _| !buf.is_parsing()).await;
+//         let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
+
+//         let font_cache = cx.font_cache();
+
+//         let family_id = font_cache
+//             .load_family(&["Courier"], &Default::default())
+//             .unwrap();
+//         let font_id = font_cache
+//             .select_font(family_id, &Default::default())
+//             .unwrap();
+//         let font_size = 16.0;
+
+//         let map =
+//             cx.add_model(|cx| DisplayMap::new(buffer, font_id, font_size, Some(40.0), 1, 1, cx));
+//         assert_eq!(
+//             cx.update(|cx| syntax_chunks(0..5, &map, &theme, cx)),
+//             [
+//                 ("fn \n".to_string(), None),
+//                 ("oute\nr".to_string(), Some(Hsla::blue())),
+//                 ("() \n{}\n\n".to_string(), None),
+//             ]
+//         );
+//         assert_eq!(
+//             cx.update(|cx| syntax_chunks(3..5, &map, &theme, cx)),
+//             [("{}\n\n".to_string(), None)]
+//         );
+
+//         map.update(cx, |map, cx| {
+//             map.fold(vec![Point::new(0, 6)..Point::new(3, 2)], cx)
+//         });
+//         assert_eq!(
+//             cx.update(|cx| syntax_chunks(1..4, &map, &theme, cx)),
+//             [
+//                 ("out".to_string(), Some(Hsla::blue())),
+//                 ("⋯\n".to_string(), None),
+//                 ("  \nfn ".to_string(), Some(Hsla::red())),
+//                 ("i\n".to_string(), Some(Hsla::blue()))
+//             ]
+//         );
+//     }
+
+//     #[gpui::test]
+//     async fn test_chunks_with_text_highlights(cx: &mut gpui::TestAppContext) {
+//         cx.update(|cx| init_test(cx, |_| {}));
+
+//         let theme = SyntaxTheme::new(vec![
+//             ("operator".to_string(), Hsla::red().into()),
+//             ("string".to_string(), Hsla::green().into()),
+//         ]);
+//         let language = Arc::new(
+//             Language::new(
+//                 LanguageConfig {
+//                     name: "Test".into(),
+//                     path_suffixes: vec![".test".to_string()],
+//                     ..Default::default()
+//                 },
+//                 Some(tree_sitter_rust::language()),
+//             )
+//             .with_highlights_query(
+//                 r#"
+//                 ":" @operator
+//                 (string_literal) @string
+//                 "#,
+//             )
+//             .unwrap(),
+//         );
+//         language.set_theme(&theme);
+
+//         let (text, highlighted_ranges) = marked_text_ranges(r#"constˇ «a»: B = "c «d»""#, false);
+
+//         let buffer = cx
+//             .add_model(|cx| Buffer::new(0, cx.model_id() as u64, text).with_language(language, cx));
+//         buffer.condition(cx, |buf, _| !buf.is_parsing()).await;
+
+//         let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
+//         let buffer_snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx));
+
+//         let font_cache = cx.font_cache();
+//         let family_id = font_cache
+//             .load_family(&["Courier"], &Default::default())
+//             .unwrap();
+//         let font_id = font_cache
+//             .select_font(family_id, &Default::default())
+//             .unwrap();
+//         let font_size = 16.0;
+//         let map = cx.add_model(|cx| DisplayMap::new(buffer, font_id, font_size, None, 1, 1, cx));
+
+//         enum MyType {}
+
+//         let style = HighlightStyle {
+//             color: Some(Hsla::blue()),
+//             ..Default::default()
+//         };
+
+//         map.update(cx, |map, _cx| {
+//             map.highlight_text(
+//                 TypeId::of::<MyType>(),
+//                 highlighted_ranges
+//                     .into_iter()
+//                     .map(|range| {
+//                         buffer_snapshot.anchor_before(range.start)
+//                             ..buffer_snapshot.anchor_before(range.end)
+//                     })
+//                     .collect(),
+//                 style,
+//             );
+//         });
+
+//         assert_eq!(
+//             cx.update(|cx| chunks(0..10, &map, &theme, cx)),
+//             [
+//                 ("const ".to_string(), None, None),
+//                 ("a".to_string(), None, Some(Hsla::blue())),
+//                 (":".to_string(), Some(Hsla::red()), None),
+//                 (" B = ".to_string(), None, None),
+//                 ("\"c ".to_string(), Some(Hsla::green()), None),
+//                 ("d".to_string(), Some(Hsla::green()), Some(Hsla::blue())),
+//                 ("\"".to_string(), Some(Hsla::green()), None),
+//             ]
+//         );
+//     }
+
+//     #[gpui::test]
+//     fn test_clip_point(cx: &mut gpui::AppContext) {
+//         init_test(cx, |_| {});
+
+//         fn assert(text: &str, shift_right: bool, bias: Bias, cx: &mut gpui::AppContext) {
+//             let (unmarked_snapshot, mut markers) = marked_display_snapshot(text, cx);
+
+//             match bias {
+//                 Bias::Left => {
+//                     if shift_right {
+//                         *markers[1].column_mut() += 1;
+//                     }
+
+//                     assert_eq!(unmarked_snapshot.clip_point(markers[1], bias), markers[0])
+//                 }
+//                 Bias::Right => {
+//                     if shift_right {
+//                         *markers[0].column_mut() += 1;
+//                     }
+
+//                     assert_eq!(unmarked_snapshot.clip_point(markers[0], bias), markers[1])
+//                 }
+//             };
+//         }
+
+//         use Bias::{Left, Right};
+//         assert("ˇˇα", false, Left, cx);
+//         assert("ˇˇα", true, Left, cx);
+//         assert("ˇˇα", false, Right, cx);
+//         assert("ˇαˇ", true, Right, cx);
+//         assert("ˇˇ✋", false, Left, cx);
+//         assert("ˇˇ✋", true, Left, cx);
+//         assert("ˇˇ✋", false, Right, cx);
+//         assert("ˇ✋ˇ", true, Right, cx);
+//         assert("ˇˇ🍐", false, Left, cx);
+//         assert("ˇˇ🍐", true, Left, cx);
+//         assert("ˇˇ🍐", false, Right, cx);
+//         assert("ˇ🍐ˇ", true, Right, cx);
+//         assert("ˇˇ\t", false, Left, cx);
+//         assert("ˇˇ\t", true, Left, cx);
+//         assert("ˇˇ\t", false, Right, cx);
+//         assert("ˇ\tˇ", true, Right, cx);
+//         assert(" ˇˇ\t", false, Left, cx);
+//         assert(" ˇˇ\t", true, Left, cx);
+//         assert(" ˇˇ\t", false, Right, cx);
+//         assert(" ˇ\tˇ", true, Right, cx);
+//         assert("   ˇˇ\t", false, Left, cx);
+//         assert("   ˇˇ\t", false, Right, cx);
+//     }
+
+//     #[gpui::test]
+//     fn test_clip_at_line_ends(cx: &mut gpui::AppContext) {
+//         init_test(cx, |_| {});
+
+//         fn assert(text: &str, cx: &mut gpui::AppContext) {
+//             let (mut unmarked_snapshot, markers) = marked_display_snapshot(text, cx);
+//             unmarked_snapshot.clip_at_line_ends = true;
+//             assert_eq!(
+//                 unmarked_snapshot.clip_point(markers[1], Bias::Left),
+//                 markers[0]
+//             );
+//         }
+
+//         assert("ˇˇ", cx);
+//         assert("ˇaˇ", cx);
+//         assert("aˇbˇ", cx);
+//         assert("aˇαˇ", cx);
+//     }
+
+//     #[gpui::test]
+//     fn test_tabs_with_multibyte_chars(cx: &mut gpui::AppContext) {
+//         init_test(cx, |_| {});
+
+//         let text = "✅\t\tα\nβ\t\n🏀β\t\tγ";
+//         let buffer = MultiBuffer::build_simple(text, cx);
+//         let font_cache = cx.font_cache();
+//         let family_id = font_cache
+//             .load_family(&["Helvetica"], &Default::default())
+//             .unwrap();
+//         let font_id = font_cache
+//             .select_font(family_id, &Default::default())
+//             .unwrap();
+//         let font_size = 14.0;
+
+//         let map =
+//             cx.add_model(|cx| DisplayMap::new(buffer.clone(), font_id, font_size, None, 1, 1, cx));
+//         let map = map.update(cx, |map, cx| map.snapshot(cx));
+//         assert_eq!(map.text(), "✅       α\nβ   \n🏀β      γ");
+//         assert_eq!(
+//             map.text_chunks(0).collect::<String>(),
+//             "✅       α\nβ   \n🏀β      γ"
+//         );
+//         assert_eq!(map.text_chunks(1).collect::<String>(), "β   \n🏀β      γ");
+//         assert_eq!(map.text_chunks(2).collect::<String>(), "🏀β      γ");
+
+//         let point = Point::new(0, "✅\t\t".len() as u32);
+//         let display_point = DisplayPoint::new(0, "✅       ".len() as u32);
+//         assert_eq!(point.to_display_point(&map), display_point);
+//         assert_eq!(display_point.to_point(&map), point);
+
+//         let point = Point::new(1, "β\t".len() as u32);
+//         let display_point = DisplayPoint::new(1, "β   ".len() as u32);
+//         assert_eq!(point.to_display_point(&map), display_point);
+//         assert_eq!(display_point.to_point(&map), point,);
+
+//         let point = Point::new(2, "🏀β\t\t".len() as u32);
+//         let display_point = DisplayPoint::new(2, "🏀β      ".len() as u32);
+//         assert_eq!(point.to_display_point(&map), display_point);
+//         assert_eq!(display_point.to_point(&map), point,);
+
+//         // Display points inside of expanded tabs
+//         assert_eq!(
+//             DisplayPoint::new(0, "✅      ".len() as u32).to_point(&map),
+//             Point::new(0, "✅\t".len() as u32),
+//         );
+//         assert_eq!(
+//             DisplayPoint::new(0, "✅ ".len() as u32).to_point(&map),
+//             Point::new(0, "✅".len() as u32),
+//         );
+
+//         // Clipping display points inside of multi-byte characters
+//         assert_eq!(
+//             map.clip_point(DisplayPoint::new(0, "✅".len() as u32 - 1), Left),
+//             DisplayPoint::new(0, 0)
+//         );
+//         assert_eq!(
+//             map.clip_point(DisplayPoint::new(0, "✅".len() as u32 - 1), Bias::Right),
+//             DisplayPoint::new(0, "✅".len() as u32)
+//         );
+//     }
+
+//     #[gpui::test]
+//     fn test_max_point(cx: &mut gpui::AppContext) {
+//         init_test(cx, |_| {});
+
+//         let buffer = MultiBuffer::build_simple("aaa\n\t\tbbb", cx);
+//         let font_cache = cx.font_cache();
+//         let family_id = font_cache
+//             .load_family(&["Helvetica"], &Default::default())
+//             .unwrap();
+//         let font_id = font_cache
+//             .select_font(family_id, &Default::default())
+//             .unwrap();
+//         let font_size = 14.0;
+//         let map =
+//             cx.add_model(|cx| DisplayMap::new(buffer.clone(), font_id, font_size, None, 1, 1, cx));
+//         assert_eq!(
+//             map.update(cx, |map, cx| map.snapshot(cx)).max_point(),
+//             DisplayPoint::new(1, 11)
+//         )
+//     }
+
+//     fn syntax_chunks<'a>(
+//         rows: Range<u32>,
+//         map: &Model<DisplayMap>,
+//         theme: &'a SyntaxTheme,
+//         cx: &mut AppContext,
+//     ) -> Vec<(String, Option<Hsla>)> {
+//         chunks(rows, map, theme, cx)
+//             .into_iter()
+//             .map(|(text, color, _)| (text, color))
+//             .collect()
+//     }
+
+//     fn chunks<'a>(
+//         rows: Range<u32>,
+//         map: &Model<DisplayMap>,
+//         theme: &'a SyntaxTheme,
+//         cx: &mut AppContext,
+//     ) -> Vec<(String, Option<Hsla>, Option<Hsla>)> {
+//         let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
+//         let mut chunks: Vec<(String, Option<Hsla>, Option<Hsla>)> = Vec::new();
+//         for chunk in snapshot.chunks(rows, true, None, None) {
+//             let syntax_color = chunk
+//                 .syntax_highlight_id
+//                 .and_then(|id| id.style(theme)?.color);
+//             let highlight_color = chunk.highlight_style.and_then(|style| style.color);
+//             if let Some((last_chunk, last_syntax_color, last_highlight_color)) = chunks.last_mut() {
+//                 if syntax_color == *last_syntax_color && highlight_color == *last_highlight_color {
+//                     last_chunk.push_str(chunk.text);
+//                     continue;
+//                 }
+//             }
+//             chunks.push((chunk.text.to_string(), syntax_color, highlight_color));
+//         }
+//         chunks
+//     }
+
+//     fn init_test(cx: &mut AppContext, f: impl Fn(&mut AllLanguageSettingsContent)) {
+//         cx.foreground().forbid_parking();
+//         cx.set_global(SettingsStore::test(cx));
+//         language::init(cx);
+//         crate::init(cx);
+//         Project::init_settings(cx);
+//         theme::init((), cx);
+//         cx.update_global::<SettingsStore, _, _>(|store, cx| {
+//             store.update_user_settings::<AllLanguageSettings>(cx, f);
+//         });
+//     }
+// }

crates/editor2/src/display_map/fold_map.rs 🔗

@@ -3,7 +3,7 @@ use super::{
     Highlights,
 };
 use crate::{Anchor, AnchorRangeExt, MultiBufferSnapshot, ToOffset};
-use gpui::{fonts::HighlightStyle, Hsla};
+use gpui::{HighlightStyle, Hsla};
 use language::{Chunk, Edit, Point, TextSummary};
 use std::{
     any::TypeId,
@@ -221,7 +221,7 @@ impl FoldMap {
         (FoldMapWriter(self), snapshot, edits)
     }
 
-    pub fn set_ellipses_color(&mut self, color: Color) -> bool {
+    pub fn set_ellipses_color(&mut self, color: Hsla) -> bool {
         if self.ellipses_color != Some(color) {
             self.ellipses_color = Some(color);
             true
@@ -469,7 +469,7 @@ pub struct FoldSnapshot {
     folds: SumTree<Fold>,
     pub inlay_snapshot: InlaySnapshot,
     pub version: usize,
-    pub ellipses_color: Option<Color>,
+    pub ellipses_color: Option<Hsla>,
 }
 
 impl FoldSnapshot {
@@ -959,7 +959,7 @@ pub struct FoldChunks<'a> {
     inlay_offset: InlayOffset,
     output_offset: usize,
     max_output_offset: usize,
-    ellipses_color: Option<Color>,
+    ellipses_color: Option<Hsla>,
 }
 
 impl<'a> Iterator for FoldChunks<'a> {

crates/editor2/src/display_map/inlay_map.rs 🔗

@@ -1,6 +1,6 @@
 use crate::{Anchor, InlayId, MultiBufferSnapshot, ToOffset};
 use collections::{BTreeMap, BTreeSet};
-use gpui::fonts::HighlightStyle;
+use gpui::HighlightStyle;
 use language::{Chunk, Edit, Point, TextSummary};
 use multi_buffer::{MultiBufferChunks, MultiBufferRows};
 use std::{

crates/editor2/src/display_map/wrap_map.rs 🔗

@@ -4,7 +4,7 @@ use super::{
     Highlights,
 };
 use crate::MultiBufferSnapshot;
-use gpui::{AppContext, Entity, Model, ModelContext, Task};
+use gpui::{AppContext, FontId, Model, ModelContext, Pixels, Task};
 use language::{Chunk, Point};
 use lazy_static::lazy_static;
 use smol::future::yield_now;
@@ -22,7 +22,7 @@ pub struct WrapMap {
     edits_since_sync: Patch<u32>,
     wrap_width: Option<f32>,
     background_task: Option<Task<()>>,
-    font: (FontId, f32),
+    font: (FontId, Pixels),
 }
 
 #[derive(Clone)]

crates/editor2/src/editor.rs 🔗

@@ -38,9 +38,8 @@ pub use element::{
 use futures::FutureExt;
 use fuzzy::{StringMatch, StringMatchCandidate};
 use gpui::{
-    serde_json, AnyElement, AppContext, AsyncAppContext, ClipboardItem,
-    Element, Entity, Hsla, Model, Subscription, Task, View, ViewContext,
-    WindowContext,
+    serde_json, AnyElement, AppContext, AsyncAppContext, ClipboardItem, Element, Entity, Hsla,
+    Model, Quad, Subscription, Task, Text, View, ViewContext, WeakView, WindowContext,
 };
 use highlight_matching_bracket::refresh_matching_bracket_highlights;
 use hover_popover::{hide_hover, HoverState};
@@ -50,10 +49,10 @@ use itertools::Itertools;
 pub use language::{char_kind, CharKind};
 use language::{
     language_settings::{self, all_language_settings, InlayHintSettings},
-    point_from_lsp, AutoindentMode, BracketPair, Buffer, CodeAction, CodeLabel,
-    Completion, CursorShape, Diagnostic, DiagnosticSeverity, File, IndentKind,
-    IndentSize, Language, LanguageRegistry, LanguageServerName, OffsetRangeExt, OffsetUtf16, Point,
-    Selection, SelectionGoal, TransactionId,
+    point_from_lsp, AutoindentMode, BracketPair, Buffer, CodeAction, CodeLabel, Completion,
+    CursorShape, Diagnostic, DiagnosticSeverity, File, IndentKind, IndentSize, Language,
+    LanguageRegistry, LanguageServerName, OffsetRangeExt, OffsetUtf16, Point, Selection,
+    SelectionGoal, TransactionId,
 };
 use link_go_to_definition::{
     hide_link_definition, show_link_definition, GoToDefinitionLink, InlayHighlight,
@@ -113,7 +112,7 @@ pub const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
 pub fn render_parsed_markdown<Tag: 'static>(
     parsed: &language::ParsedMarkdown,
     editor_style: &EditorStyle,
-    workspace: Option<WeakViewHandle<Workspace>>,
+    workspace: Option<WeakView<Workspace>>,
     cx: &mut ViewContext<Editor>,
 ) -> Text {
     enum RenderedMarkdown {}
@@ -124,51 +123,55 @@ pub fn render_parsed_markdown<Tag: 'static>(
 
     let mut region_id = 0;
 
-    Text::new(parsed.text, editor_style.text.clone())
-        .with_highlights(
-            parsed
-                .highlights
-                .iter()
-                .filter_map(|(range, highlight)| {
-                    let highlight = highlight.to_highlight_style(&editor_style.syntax)?;
-                    Some((range.clone(), highlight))
-                })
-                .collect::<Vec<_>>(),
-        )
-        .with_custom_runs(parsed.region_ranges, move |ix, bounds, cx| {
-            region_id += 1;
-            let region = parsed.regions[ix].clone();
-
-            if let Some(link) = region.link {
-                cx.scene().push_cursor_region(CursorRegion {
-                    bounds,
-                    style: CursorStyle::PointingHand,
-                });
-                cx.scene().push_mouse_region(
-                    MouseRegion::new::<(RenderedMarkdown, Tag)>(view_id, region_id, bounds)
-                        .on_down::<Editor, _>(MouseButton::Left, move |_, _, cx| match &link {
-                            markdown::Link::Web { url } => cx.platform().open_url(url),
-                            markdown::Link::Path { path } => {
-                                if let Some(workspace) = &workspace {
-                                    _ = workspace.update(cx, |workspace, cx| {
-                                        workspace.open_abs_path(path.clone(), false, cx).detach();
-                                    });
-                                }
-                            }
-                        }),
-                );
-            }
-
-            if region.code {
-                cx.scene().push_quad(gpui::Quad {
-                    bounds,
-                    background: Some(code_span_background_color),
-                    border: Default::default(),
-                    corner_radii: (2.0).into(),
-                });
-            }
-        })
-        .with_soft_wrap(true)
+    todo!()
+    // Text::new(parsed.text, editor_style.text.clone())
+    //     .with_highlights(
+    //         parsed
+    //             .highlights
+    //             .iter()
+    //             .filter_map(|(range, highlight)| {
+    //                 let highlight = highlight.to_highlight_style(&editor_style.syntax)?;
+    //                 Some((range.clone(), highlight))
+    //             })
+    //             .collect::<Vec<_>>(),
+    //     )
+    //     .with_custom_runs(parsed.region_ranges, move |ix, bounds, cx| {
+    //         region_id += 1;
+    //         let region = parsed.regions[ix].clone();
+
+    //         if let Some(link) = region.link {
+    //             cx.scene().push_cursor_region(CursorRegion {
+    //                 bounds,
+    //                 style: CursorStyle::PointingHand,
+    //             });
+    //             cx.scene().push_mouse_region(
+    //                 MouseRegion::new::<(RenderedMarkdown, Tag)>(view_id, region_id, bounds)
+    //                     .on_down::<Editor, _>(MouseButton::Left, move |_, _, cx| match &link {
+    //                         markdown::Link::Web { url } => cx.platform().open_url(url),
+    //                         markdown::Link::Path { path } => {
+    //                             if let Some(workspace) = &workspace {
+    //                                 _ = workspace.update(cx, |workspace, cx| {
+    //                                     workspace.open_abs_path(path.clone(), false, cx).detach();
+    //                                 });
+    //                             }
+    //                         }
+    //                     }),
+    //             );
+    //         }
+
+    //         if region.code {
+    //             cx.draw_quad(Quad {
+    //                 bounds,
+    //                 background: Some(code_span_background_color),
+    //                 corner_radii: (2.0).into(),
+    //                 order: todo!(),
+    //                 content_mask: todo!(),
+    //                 border_color: todo!(),
+    //                 border_widths: todo!(),
+    //             });
+    //         }
+    //     })
+    //     .with_soft_wrap(true)
 }
 
 #[derive(Clone, Deserialize, PartialEq, Default)]
@@ -416,133 +419,133 @@ pub fn init_settings(cx: &mut AppContext) {
 
 pub fn init(cx: &mut AppContext) {
     init_settings(cx);
-    cx.add_action(Editor::new_file);
-    cx.add_action(Editor::new_file_in_direction);
-    cx.add_action(Editor::cancel);
-    cx.add_action(Editor::newline);
-    cx.add_action(Editor::newline_above);
-    cx.add_action(Editor::newline_below);
-    cx.add_action(Editor::backspace);
-    cx.add_action(Editor::delete);
-    cx.add_action(Editor::tab);
-    cx.add_action(Editor::tab_prev);
-    cx.add_action(Editor::indent);
-    cx.add_action(Editor::outdent);
-    cx.add_action(Editor::delete_line);
-    cx.add_action(Editor::join_lines);
-    cx.add_action(Editor::sort_lines_case_sensitive);
-    cx.add_action(Editor::sort_lines_case_insensitive);
-    cx.add_action(Editor::reverse_lines);
-    cx.add_action(Editor::shuffle_lines);
-    cx.add_action(Editor::convert_to_upper_case);
-    cx.add_action(Editor::convert_to_lower_case);
-    cx.add_action(Editor::convert_to_title_case);
-    cx.add_action(Editor::convert_to_snake_case);
-    cx.add_action(Editor::convert_to_kebab_case);
-    cx.add_action(Editor::convert_to_upper_camel_case);
-    cx.add_action(Editor::convert_to_lower_camel_case);
-    cx.add_action(Editor::delete_to_previous_word_start);
-    cx.add_action(Editor::delete_to_previous_subword_start);
-    cx.add_action(Editor::delete_to_next_word_end);
-    cx.add_action(Editor::delete_to_next_subword_end);
-    cx.add_action(Editor::delete_to_beginning_of_line);
-    cx.add_action(Editor::delete_to_end_of_line);
-    cx.add_action(Editor::cut_to_end_of_line);
-    cx.add_action(Editor::duplicate_line);
-    cx.add_action(Editor::move_line_up);
-    cx.add_action(Editor::move_line_down);
-    cx.add_action(Editor::transpose);
-    cx.add_action(Editor::cut);
-    cx.add_action(Editor::copy);
-    cx.add_action(Editor::paste);
-    cx.add_action(Editor::undo);
-    cx.add_action(Editor::redo);
-    cx.add_action(Editor::move_up);
-    cx.add_action(Editor::move_page_up);
-    cx.add_action(Editor::move_down);
-    cx.add_action(Editor::move_page_down);
-    cx.add_action(Editor::next_screen);
-    cx.add_action(Editor::move_left);
-    cx.add_action(Editor::move_right);
-    cx.add_action(Editor::move_to_previous_word_start);
-    cx.add_action(Editor::move_to_previous_subword_start);
-    cx.add_action(Editor::move_to_next_word_end);
-    cx.add_action(Editor::move_to_next_subword_end);
-    cx.add_action(Editor::move_to_beginning_of_line);
-    cx.add_action(Editor::move_to_end_of_line);
-    cx.add_action(Editor::move_to_start_of_paragraph);
-    cx.add_action(Editor::move_to_end_of_paragraph);
-    cx.add_action(Editor::move_to_beginning);
-    cx.add_action(Editor::move_to_end);
-    cx.add_action(Editor::select_up);
-    cx.add_action(Editor::select_down);
-    cx.add_action(Editor::select_left);
-    cx.add_action(Editor::select_right);
-    cx.add_action(Editor::select_to_previous_word_start);
-    cx.add_action(Editor::select_to_previous_subword_start);
-    cx.add_action(Editor::select_to_next_word_end);
-    cx.add_action(Editor::select_to_next_subword_end);
-    cx.add_action(Editor::select_to_beginning_of_line);
-    cx.add_action(Editor::select_to_end_of_line);
-    cx.add_action(Editor::select_to_start_of_paragraph);
-    cx.add_action(Editor::select_to_end_of_paragraph);
-    cx.add_action(Editor::select_to_beginning);
-    cx.add_action(Editor::select_to_end);
-    cx.add_action(Editor::select_all);
-    cx.add_action(Editor::select_all_matches);
-    cx.add_action(Editor::select_line);
-    cx.add_action(Editor::split_selection_into_lines);
-    cx.add_action(Editor::add_selection_above);
-    cx.add_action(Editor::add_selection_below);
-    cx.add_action(Editor::select_next);
-    cx.add_action(Editor::select_previous);
-    cx.add_action(Editor::toggle_comments);
-    cx.add_action(Editor::select_larger_syntax_node);
-    cx.add_action(Editor::select_smaller_syntax_node);
-    cx.add_action(Editor::move_to_enclosing_bracket);
-    cx.add_action(Editor::undo_selection);
-    cx.add_action(Editor::redo_selection);
-    cx.add_action(Editor::go_to_diagnostic);
-    cx.add_action(Editor::go_to_prev_diagnostic);
-    cx.add_action(Editor::go_to_hunk);
-    cx.add_action(Editor::go_to_prev_hunk);
-    cx.add_action(Editor::go_to_definition);
-    cx.add_action(Editor::go_to_definition_split);
-    cx.add_action(Editor::go_to_type_definition);
-    cx.add_action(Editor::go_to_type_definition_split);
-    cx.add_action(Editor::fold);
-    cx.add_action(Editor::fold_at);
-    cx.add_action(Editor::unfold_lines);
-    cx.add_action(Editor::unfold_at);
-    cx.add_action(Editor::gutter_hover);
-    cx.add_action(Editor::fold_selected_ranges);
-    cx.add_action(Editor::show_completions);
-    cx.add_action(Editor::toggle_code_actions);
-    cx.add_action(Editor::open_excerpts);
-    cx.add_action(Editor::toggle_soft_wrap);
-    cx.add_action(Editor::toggle_inlay_hints);
-    cx.add_action(Editor::reveal_in_finder);
-    cx.add_action(Editor::copy_path);
-    cx.add_action(Editor::copy_relative_path);
-    cx.add_action(Editor::copy_highlight_json);
-    cx.add_async_action(Editor::format);
-    cx.add_action(Editor::restart_language_server);
-    cx.add_action(Editor::show_character_palette);
-    cx.add_async_action(Editor::confirm_completion);
-    cx.add_async_action(Editor::confirm_code_action);
-    cx.add_async_action(Editor::rename);
-    cx.add_async_action(Editor::confirm_rename);
-    cx.add_async_action(Editor::find_all_references);
-    cx.add_action(Editor::next_copilot_suggestion);
-    cx.add_action(Editor::previous_copilot_suggestion);
-    cx.add_action(Editor::copilot_suggest);
-    cx.add_action(Editor::context_menu_first);
-    cx.add_action(Editor::context_menu_prev);
-    cx.add_action(Editor::context_menu_next);
-    cx.add_action(Editor::context_menu_last);
+    // cx.add_action(Editor::new_file);
+    // cx.add_action(Editor::new_file_in_direction);
+    // cx.add_action(Editor::cancel);
+    // cx.add_action(Editor::newline);
+    // cx.add_action(Editor::newline_above);
+    // cx.add_action(Editor::newline_below);
+    // cx.add_action(Editor::backspace);
+    // cx.add_action(Editor::delete);
+    // cx.add_action(Editor::tab);
+    // cx.add_action(Editor::tab_prev);
+    // cx.add_action(Editor::indent);
+    // cx.add_action(Editor::outdent);
+    // cx.add_action(Editor::delete_line);
+    // cx.add_action(Editor::join_lines);
+    // cx.add_action(Editor::sort_lines_case_sensitive);
+    // cx.add_action(Editor::sort_lines_case_insensitive);
+    // cx.add_action(Editor::reverse_lines);
+    // cx.add_action(Editor::shuffle_lines);
+    // cx.add_action(Editor::convert_to_upper_case);
+    // cx.add_action(Editor::convert_to_lower_case);
+    // cx.add_action(Editor::convert_to_title_case);
+    // cx.add_action(Editor::convert_to_snake_case);
+    // cx.add_action(Editor::convert_to_kebab_case);
+    // cx.add_action(Editor::convert_to_upper_camel_case);
+    // cx.add_action(Editor::convert_to_lower_camel_case);
+    // cx.add_action(Editor::delete_to_previous_word_start);
+    // cx.add_action(Editor::delete_to_previous_subword_start);
+    // cx.add_action(Editor::delete_to_next_word_end);
+    // cx.add_action(Editor::delete_to_next_subword_end);
+    // cx.add_action(Editor::delete_to_beginning_of_line);
+    // cx.add_action(Editor::delete_to_end_of_line);
+    // cx.add_action(Editor::cut_to_end_of_line);
+    // cx.add_action(Editor::duplicate_line);
+    // cx.add_action(Editor::move_line_up);
+    // cx.add_action(Editor::move_line_down);
+    // cx.add_action(Editor::transpose);
+    // cx.add_action(Editor::cut);
+    // cx.add_action(Editor::copy);
+    // cx.add_action(Editor::paste);
+    // cx.add_action(Editor::undo);
+    // cx.add_action(Editor::redo);
+    // cx.add_action(Editor::move_up);
+    // cx.add_action(Editor::move_page_up);
+    // cx.add_action(Editor::move_down);
+    // cx.add_action(Editor::move_page_down);
+    // cx.add_action(Editor::next_screen);
+    // cx.add_action(Editor::move_left);
+    // cx.add_action(Editor::move_right);
+    // cx.add_action(Editor::move_to_previous_word_start);
+    // cx.add_action(Editor::move_to_previous_subword_start);
+    // cx.add_action(Editor::move_to_next_word_end);
+    // cx.add_action(Editor::move_to_next_subword_end);
+    // cx.add_action(Editor::move_to_beginning_of_line);
+    // cx.add_action(Editor::move_to_end_of_line);
+    // cx.add_action(Editor::move_to_start_of_paragraph);
+    // cx.add_action(Editor::move_to_end_of_paragraph);
+    // cx.add_action(Editor::move_to_beginning);
+    // cx.add_action(Editor::move_to_end);
+    // cx.add_action(Editor::select_up);
+    // cx.add_action(Editor::select_down);
+    // cx.add_action(Editor::select_left);
+    // cx.add_action(Editor::select_right);
+    // cx.add_action(Editor::select_to_previous_word_start);
+    // cx.add_action(Editor::select_to_previous_subword_start);
+    // cx.add_action(Editor::select_to_next_word_end);
+    // cx.add_action(Editor::select_to_next_subword_end);
+    // cx.add_action(Editor::select_to_beginning_of_line);
+    // cx.add_action(Editor::select_to_end_of_line);
+    // cx.add_action(Editor::select_to_start_of_paragraph);
+    // cx.add_action(Editor::select_to_end_of_paragraph);
+    // cx.add_action(Editor::select_to_beginning);
+    // cx.add_action(Editor::select_to_end);
+    // cx.add_action(Editor::select_all);
+    // cx.add_action(Editor::select_all_matches);
+    // cx.add_action(Editor::select_line);
+    // cx.add_action(Editor::split_selection_into_lines);
+    // cx.add_action(Editor::add_selection_above);
+    // cx.add_action(Editor::add_selection_below);
+    // cx.add_action(Editor::select_next);
+    // cx.add_action(Editor::select_previous);
+    // cx.add_action(Editor::toggle_comments);
+    // cx.add_action(Editor::select_larger_syntax_node);
+    // cx.add_action(Editor::select_smaller_syntax_node);
+    // cx.add_action(Editor::move_to_enclosing_bracket);
+    // cx.add_action(Editor::undo_selection);
+    // cx.add_action(Editor::redo_selection);
+    // cx.add_action(Editor::go_to_diagnostic);
+    // cx.add_action(Editor::go_to_prev_diagnostic);
+    // cx.add_action(Editor::go_to_hunk);
+    // cx.add_action(Editor::go_to_prev_hunk);
+    // cx.add_action(Editor::go_to_definition);
+    // cx.add_action(Editor::go_to_definition_split);
+    // cx.add_action(Editor::go_to_type_definition);
+    // cx.add_action(Editor::go_to_type_definition_split);
+    // cx.add_action(Editor::fold);
+    // cx.add_action(Editor::fold_at);
+    // cx.add_action(Editor::unfold_lines);
+    // cx.add_action(Editor::unfold_at);
+    // cx.add_action(Editor::gutter_hover);
+    // cx.add_action(Editor::fold_selected_ranges);
+    // cx.add_action(Editor::show_completions);
+    // cx.add_action(Editor::toggle_code_actions);
+    // cx.add_action(Editor::open_excerpts);
+    // cx.add_action(Editor::toggle_soft_wrap);
+    // cx.add_action(Editor::toggle_inlay_hints);
+    // cx.add_action(Editor::reveal_in_finder);
+    // cx.add_action(Editor::copy_path);
+    // cx.add_action(Editor::copy_relative_path);
+    // cx.add_action(Editor::copy_highlight_json);
+    // cx.add_async_action(Editor::format);
+    // cx.add_action(Editor::restart_language_server);
+    // cx.add_action(Editor::show_character_palette);
+    // cx.add_async_action(Editor::confirm_completion);
+    // cx.add_async_action(Editor::confirm_code_action);
+    // cx.add_async_action(Editor::rename);
+    // cx.add_async_action(Editor::confirm_rename);
+    // cx.add_async_action(Editor::find_all_references);
+    // cx.add_action(Editor::next_copilot_suggestion);
+    // cx.add_action(Editor::previous_copilot_suggestion);
+    // cx.add_action(Editor::copilot_suggest);
+    // cx.add_action(Editor::context_menu_first);
+    // cx.add_action(Editor::context_menu_prev);
+    // cx.add_action(Editor::context_menu_next);
+    // cx.add_action(Editor::context_menu_last);
 
     hover_popover::init(cx);
-    /scroll::actions::init(cx);
+    scroll::actions::init(cx);
 
     workspace::register_project_item::<Editor>(cx);
     workspace::register_followable_item::<Editor>(cx);
@@ -571,7 +574,7 @@ pub enum SelectPhase {
     Update {
         position: DisplayPoint,
         goal_column: u32,
-        scroll_position: Vector2F,
+        scroll_position: Point<Pixels>,
     },
     End,
 }
@@ -612,11 +615,11 @@ type CompletionId = usize;
 type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
 type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
 
-type BackgroundHighlight = (fn(&Theme) -> Color, Vec<Range<Anchor>>);
-type InlayBackgroundHighlight = (fn(&Theme) -> Color, Vec<InlayHighlight>);
+type BackgroundHighlight = (fn(&Theme) -> Hsla, Vec<Range<Anchor>>);
+type InlayBackgroundHighlight = (fn(&Theme) -> Hsla, Vec<InlayHighlight>);
 
 pub struct Editor {
-    handle: WeakViewHandle<Self>,
+    handle: WeakView<Self>,
     buffer: Model<MultiBuffer>,
     display_map: Model<DisplayMap>,
     pub selections: SelectionsCollection,
@@ -648,7 +651,7 @@ pub struct Editor {
     inlay_background_highlights: TreeMap<Option<TypeId>, InlayBackgroundHighlight>,
     nav_history: Option<ItemNavHistory>,
     context_menu: RwLock<Option<ContextMenu>>,
-    mouse_context_menu: ViewHandle<context_menu::ContextMenu>,
+    mouse_context_menu: View<context_menu::ContextMenu>,
     completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
     next_completion_id: CompletionId,
     available_code_actions: Option<(Model<Buffer>, Arc<[CodeAction]>)>,
@@ -659,7 +662,7 @@ pub struct Editor {
     cursor_shape: CursorShape,
     collapse_matches: bool,
     autoindent_mode: Option<AutoindentMode>,
-    workspace: Option<(WeakViewHandle<Workspace>, i64)>,
+    workspace: Option<(WeakView<Workspace>, i64)>,
     keymap_context_layers: BTreeMap<TypeId, KeymapContext>,
     input_enabled: bool,
     read_only: bool,
@@ -672,7 +675,7 @@ pub struct Editor {
     // inlay_hint_cache: InlayHintCache,
     next_inlay_id: usize,
     _subscriptions: Vec<Subscription>,
-    pixel_position_of_newest_cursor: Option<Vector2F>,
+    pixel_position_of_newest_cursor: Option<Point<Pixels>>,
 }
 
 pub struct EditorSnapshot {
@@ -828,7 +831,7 @@ struct SnippetState {
 pub struct RenameState {
     pub range: Range<Anchor>,
     pub old_name: Arc<str>,
-    pub editor: ViewHandle<Editor>,
+    pub editor: View<Editor>,
     block_id: BlockId,
 }
 
@@ -915,7 +918,7 @@ impl ContextMenu {
         &self,
         cursor_position: DisplayPoint,
         style: EditorStyle,
-        workspace: Option<WeakViewHandle<Workspace>>,
+        workspace: Option<WeakView<Workspace>>,
         cx: &mut ViewContext<Editor>,
     ) -> (DisplayPoint, AnyElement<Editor>) {
         match self {
@@ -938,22 +941,14 @@ struct CompletionsMenu {
 }
 
 impl CompletionsMenu {
-    fn select_first(
-        &mut self,
-        project: Option<&Model<Project>>,
-        cx: &mut ViewContext<Editor>,
-    ) {
+    fn select_first(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
         self.selected_item = 0;
         self.list.scroll_to(ScrollTarget::Show(self.selected_item));
         self.attempt_resolve_selected_completion_documentation(project, cx);
         cx.notify();
     }
 
-    fn select_prev(
-        &mut self,
-        project: Option<&Model<Project>>,
-        cx: &mut ViewContext<Editor>,
-    ) {
+    fn select_prev(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
         if self.selected_item > 0 {
             self.selected_item -= 1;
         } else {
@@ -964,11 +959,7 @@ impl CompletionsMenu {
         cx.notify();
     }
 
-    fn select_next(
-        &mut self,
-        project: Option<&Model<Project>>,
-        cx: &mut ViewContext<Editor>,
-    ) {
+    fn select_next(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
         if self.selected_item + 1 < self.matches.len() {
             self.selected_item += 1;
         } else {
@@ -979,11 +970,7 @@ impl CompletionsMenu {
         cx.notify();
     }
 
-    fn select_last(
-        &mut self,
-        project: Option<&Model<Project>>,
-        cx: &mut ViewContext<Editor>,
-    ) {
+    fn select_last(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
         self.selected_item = self.matches.len() - 1;
         self.list.scroll_to(ScrollTarget::Show(self.selected_item));
         self.attempt_resolve_selected_completion_documentation(project, cx);
@@ -1241,7 +1228,7 @@ impl CompletionsMenu {
     fn render(
         &self,
         style: EditorStyle,
-        workspace: Option<WeakViewHandle<Workspace>>,
+        workspace: Option<WeakView<Workspace>>,
         cx: &mut ViewContext<Editor>,
     ) -> AnyElement<Editor> {
         enum CompletionTag {}
@@ -1760,7 +1747,7 @@ pub struct NavigationData {
     scroll_top_row: u32,
 }
 
-pub struct EditorCreated(pub ViewHandle<Editor>);
+pub struct EditorCreated(pub View<Editor>);
 
 enum GotoDefinitionKind {
     Symbol,
@@ -3845,8 +3832,8 @@ impl InlayHintRefreshReason {
 //     }
 
 //     async fn open_project_transaction(
-//         this: &WeakViewHandle<Editor>,
-//         workspace: WeakViewHandle<Workspace>,
+//         this: &WeakViewHandle<Editor
+//         workspace: WeakViewHandle<Workspace
 //         transaction: ProjectTransaction,
 //         title: String,
 //         mut cx: AsyncAppContext,
@@ -9237,7 +9224,7 @@ impl EditorSnapshot {
         self.placeholder_text.as_ref()
     }
 
-    pub fn scroll_position(&self) -> Vector2F {
+    pub fn scroll_position(&self) -> Point<Pixels> {
         self.scroll_anchor.scroll_position(&self.display_snapshot)
     }
 }
@@ -9286,9 +9273,9 @@ pub enum Event {
     Closed,
 }
 
-pub struct EditorFocused(pub ViewHandle<Editor>);
-pub struct EditorBlurred(pub ViewHandle<Editor>);
-pub struct EditorReleased(pub WeakViewHandle<Editor>);
+pub struct EditorFocused(pub View<Editor>);
+pub struct EditorBlurred(pub View<Editor>);
+pub struct EditorReleased(pub WeakView<Editor>);
 
 impl Entity for Editor {
     type Event = Event;
@@ -9323,7 +9310,7 @@ impl View for Editor {
         "Editor"
     }
 
-    fn focus_in(&mut self, focused: AnyViewHandle, cx: &mut ViewContext<Self>) {
+    fn focus_in(&mut self, focused: AnyView, cx: &mut ViewContext<Self>) {
         if cx.is_self_focused() {
             let focused_event = EditorFocused(cx.handle());
             cx.emit(Event::Focused);
@@ -9350,7 +9337,7 @@ impl View for Editor {
         }
     }
 
-    fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
+    fn focus_out(&mut self, _: AnyView, cx: &mut ViewContext<Self>) {
         let blurred_event = EditorBlurred(cx.handle());
         cx.emit_global(blurred_event);
         self.focused = false;
@@ -9649,7 +9636,7 @@ fn build_style(
     settings: &ThemeSettings,
     get_field_editor_theme: Option<&GetFieldEditorTheme>,
     override_text_style: Option<&OverrideTextStyle>,
-    cx: &AppContext,
+    cx: &mut AppContext,
 ) -> EditorStyle {
     let font_cache = cx.font_cache();
     let line_height_scalar = settings.line_height();

crates/editor2/src/editor_tests.rs 🔗

@@ -1,8195 +1,8191 @@
-use super::*;
-use crate::{
-    scroll::scroll_amount::ScrollAmount,
-    test::{
-        assert_text_with_selections, build_editor, editor_lsp_test_context::EditorLspTestContext,
-        editor_test_context::EditorTestContext, select_ranges,
-    },
-    JoinLines,
-};
-use drag_and_drop::DragAndDrop;
-use futures::StreamExt;
-use gpui::{
-    executor::Deterministic,
-    geometry::{rect::RectF, vector::vec2f},
-    platform::{WindowBounds, WindowOptions},
-    serde_json::{self, json},
-    TestAppContext,
-};
-use indoc::indoc;
-use language::{
-    language_settings::{AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent},
-    BracketPairConfig, FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageRegistry,
-    Override, Point,
-};
-use parking_lot::Mutex;
-use project::project_settings::{LspSettings, ProjectSettings};
-use project::FakeFs;
-use std::sync::atomic;
-use std::sync::atomic::AtomicUsize;
-use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
-use unindent::Unindent;
-use util::{
-    assert_set_eq,
-    test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
-};
-use workspace::{
-    item::{FollowableItem, Item, ItemHandle},
-    NavigationEntry, ViewId,
-};
-
-#[gpui::test]
-fn test_edit_events(cx: &mut TestAppContext) {
-    init_test(cx, |_| {});
-
-    let buffer = cx.add_model(|cx| {
-        let mut buffer = language::Buffer::new(0, cx.model_id() as u64, "123456");
-        buffer.set_group_interval(Duration::from_secs(1));
-        buffer
-    });
-
-    let events = Rc::new(RefCell::new(Vec::new()));
-    let editor1 = cx
-        .add_window({
-            let events = events.clone();
-            |cx| {
-                cx.subscribe(&cx.handle(), move |_, _, event, _| {
-                    if matches!(
-                        event,
-                        Event::Edited | Event::BufferEdited | Event::DirtyChanged
-                    ) {
-                        events.borrow_mut().push(("editor1", event.clone()));
-                    }
-                })
-                .detach();
-                Editor::for_buffer(buffer.clone(), None, cx)
-            }
-        })
-        .root(cx);
-    let editor2 = cx
-        .add_window({
-            let events = events.clone();
-            |cx| {
-                cx.subscribe(&cx.handle(), move |_, _, event, _| {
-                    if matches!(
-                        event,
-                        Event::Edited | Event::BufferEdited | Event::DirtyChanged
-                    ) {
-                        events.borrow_mut().push(("editor2", event.clone()));
-                    }
-                })
-                .detach();
-                Editor::for_buffer(buffer.clone(), None, cx)
-            }
-        })
-        .root(cx);
-    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
-
-    // Mutating editor 1 will emit an `Edited` event only for that editor.
-    editor1.update(cx, |editor, cx| editor.insert("X", cx));
-    assert_eq!(
-        mem::take(&mut *events.borrow_mut()),
-        [
-            ("editor1", Event::Edited),
-            ("editor1", Event::BufferEdited),
-            ("editor2", Event::BufferEdited),
-            ("editor1", Event::DirtyChanged),
-            ("editor2", Event::DirtyChanged)
-        ]
-    );
-
-    // Mutating editor 2 will emit an `Edited` event only for that editor.
-    editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
-    assert_eq!(
-        mem::take(&mut *events.borrow_mut()),
-        [
-            ("editor2", Event::Edited),
-            ("editor1", Event::BufferEdited),
-            ("editor2", Event::BufferEdited),
-        ]
-    );
-
-    // Undoing on editor 1 will emit an `Edited` event only for that editor.
-    editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
-    assert_eq!(
-        mem::take(&mut *events.borrow_mut()),
-        [
-            ("editor1", Event::Edited),
-            ("editor1", Event::BufferEdited),
-            ("editor2", Event::BufferEdited),
-            ("editor1", Event::DirtyChanged),
-            ("editor2", Event::DirtyChanged),
-        ]
-    );
-
-    // Redoing on editor 1 will emit an `Edited` event only for that editor.
-    editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
-    assert_eq!(
-        mem::take(&mut *events.borrow_mut()),
-        [
-            ("editor1", Event::Edited),
-            ("editor1", Event::BufferEdited),
-            ("editor2", Event::BufferEdited),
-            ("editor1", Event::DirtyChanged),
-            ("editor2", Event::DirtyChanged),
-        ]
-    );
-
-    // Undoing on editor 2 will emit an `Edited` event only for that editor.
-    editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
-    assert_eq!(
-        mem::take(&mut *events.borrow_mut()),
-        [
-            ("editor2", Event::Edited),
-            ("editor1", Event::BufferEdited),
-            ("editor2", Event::BufferEdited),
-            ("editor1", Event::DirtyChanged),
-            ("editor2", Event::DirtyChanged),
-        ]
-    );
-
-    // Redoing on editor 2 will emit an `Edited` event only for that editor.
-    editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
-    assert_eq!(
-        mem::take(&mut *events.borrow_mut()),
-        [
-            ("editor2", Event::Edited),
-            ("editor1", Event::BufferEdited),
-            ("editor2", Event::BufferEdited),
-            ("editor1", Event::DirtyChanged),
-            ("editor2", Event::DirtyChanged),
-        ]
-    );
-
-    // No event is emitted when the mutation is a no-op.
-    editor2.update(cx, |editor, cx| {
-        editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
-
-        editor.backspace(&Backspace, cx);
-    });
-    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
-}
-
-#[gpui::test]
-fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
-    init_test(cx, |_| {});
-
-    let mut now = Instant::now();
-    let buffer = cx.add_model(|cx| language::Buffer::new(0, cx.model_id() as u64, "123456"));
-    let group_interval = buffer.read_with(cx, |buffer, _| buffer.transaction_group_interval());
-    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
-    let editor = cx
-        .add_window(|cx| build_editor(buffer.clone(), cx))
-        .root(cx);
-
-    editor.update(cx, |editor, cx| {
-        editor.start_transaction_at(now, cx);
-        editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
-
-        editor.insert("cd", cx);
-        editor.end_transaction_at(now, cx);
-        assert_eq!(editor.text(cx), "12cd56");
-        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
-
-        editor.start_transaction_at(now, cx);
-        editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
-        editor.insert("e", cx);
-        editor.end_transaction_at(now, cx);
-        assert_eq!(editor.text(cx), "12cde6");
-        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
-
-        now += group_interval + Duration::from_millis(1);
-        editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
-
-        // Simulate an edit in another editor
-        buffer.update(cx, |buffer, cx| {
-            buffer.start_transaction_at(now, cx);
-            buffer.edit([(0..1, "a")], None, cx);
-            buffer.edit([(1..1, "b")], None, cx);
-            buffer.end_transaction_at(now, cx);
-        });
-
-        assert_eq!(editor.text(cx), "ab2cde6");
-        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
-
-        // Last transaction happened past the group interval in a different editor.
-        // Undo it individually and don't restore selections.
-        editor.undo(&Undo, cx);
-        assert_eq!(editor.text(cx), "12cde6");
-        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
-
-        // First two transactions happened within the group interval in this editor.
-        // Undo them together and restore selections.
-        editor.undo(&Undo, cx);
-        editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
-        assert_eq!(editor.text(cx), "123456");
-        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
-
-        // Redo the first two transactions together.
-        editor.redo(&Redo, cx);
-        assert_eq!(editor.text(cx), "12cde6");
-        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
-
-        // Redo the last transaction on its own.
-        editor.redo(&Redo, cx);
-        assert_eq!(editor.text(cx), "ab2cde6");
-        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
-
-        // Test empty transactions.
-        editor.start_transaction_at(now, cx);
-        editor.end_transaction_at(now, cx);
-        editor.undo(&Undo, cx);
-        assert_eq!(editor.text(cx), "12cde6");
-    });
-}
-
-#[gpui::test]
-fn test_ime_composition(cx: &mut TestAppContext) {
-    init_test(cx, |_| {});
-
-    let buffer = cx.add_model(|cx| {
-        let mut buffer = language::Buffer::new(0, cx.model_id() as u64, "abcde");
-        // Ensure automatic grouping doesn't occur.
-        buffer.set_group_interval(Duration::ZERO);
-        buffer
-    });
-
-    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
-    cx.add_window(|cx| {
-        let mut editor = build_editor(buffer.clone(), cx);
-
-        // Start a new IME composition.
-        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
-        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, cx);
-        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, cx);
-        assert_eq!(editor.text(cx), "äbcde");
-        assert_eq!(
-            editor.marked_text_ranges(cx),
-            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
-        );
-
-        // Finalize IME composition.
-        editor.replace_text_in_range(None, "ā", cx);
-        assert_eq!(editor.text(cx), "ābcde");
-        assert_eq!(editor.marked_text_ranges(cx), None);
-
-        // IME composition edits are grouped and are undone/redone at once.
-        editor.undo(&Default::default(), cx);
-        assert_eq!(editor.text(cx), "abcde");
-        assert_eq!(editor.marked_text_ranges(cx), None);
-        editor.redo(&Default::default(), cx);
-        assert_eq!(editor.text(cx), "ābcde");
-        assert_eq!(editor.marked_text_ranges(cx), None);
-
-        // Start a new IME composition.
-        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
-        assert_eq!(
-            editor.marked_text_ranges(cx),
-            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
-        );
-
-        // Undoing during an IME composition cancels it.
-        editor.undo(&Default::default(), cx);
-        assert_eq!(editor.text(cx), "ābcde");
-        assert_eq!(editor.marked_text_ranges(cx), None);
-
-        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
-        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, cx);
-        assert_eq!(editor.text(cx), "ābcdè");
-        assert_eq!(
-            editor.marked_text_ranges(cx),
-            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
-        );
-
-        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
-        editor.replace_text_in_range(Some(4..999), "ę", cx);
-        assert_eq!(editor.text(cx), "ābcdę");
-        assert_eq!(editor.marked_text_ranges(cx), None);
-
-        // Start a new IME composition with multiple cursors.
-        editor.change_selections(None, cx, |s| {
-            s.select_ranges([
-                OffsetUtf16(1)..OffsetUtf16(1),
-                OffsetUtf16(3)..OffsetUtf16(3),
-                OffsetUtf16(5)..OffsetUtf16(5),
-            ])
-        });
-        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, cx);
-        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
-        assert_eq!(
-            editor.marked_text_ranges(cx),
-            Some(vec![
-                OffsetUtf16(0)..OffsetUtf16(3),
-                OffsetUtf16(4)..OffsetUtf16(7),
-                OffsetUtf16(8)..OffsetUtf16(11)
-            ])
-        );
-
-        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
-        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, cx);
-        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
-        assert_eq!(
-            editor.marked_text_ranges(cx),
-            Some(vec![
-                OffsetUtf16(1)..OffsetUtf16(2),
-                OffsetUtf16(5)..OffsetUtf16(6),
-                OffsetUtf16(9)..OffsetUtf16(10)
-            ])
-        );
-
-        // Finalize IME composition with multiple cursors.
-        editor.replace_text_in_range(Some(9..10), "2", cx);
-        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
-        assert_eq!(editor.marked_text_ranges(cx), None);
-
-        editor
-    });
-}
-
-#[gpui::test]
-fn test_selection_with_mouse(cx: &mut TestAppContext) {
-    init_test(cx, |_| {});
-
-    let editor = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
-            build_editor(buffer, cx)
-        })
-        .root(cx);
-    editor.update(cx, |view, cx| {
-        view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
-    });
-    assert_eq!(
-        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
-        [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
-    );
-
-    editor.update(cx, |view, cx| {
-        view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
-    });
-
-    assert_eq!(
-        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
-        [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
-    );
-
-    editor.update(cx, |view, cx| {
-        view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
-    });
-
-    assert_eq!(
-        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
-        [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
-    );
-
-    editor.update(cx, |view, cx| {
-        view.end_selection(cx);
-        view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
-    });
-
-    assert_eq!(
-        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
-        [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
-    );
-
-    editor.update(cx, |view, cx| {
-        view.begin_selection(DisplayPoint::new(3, 3), true, 1, cx);
-        view.update_selection(DisplayPoint::new(0, 0), 0, Vector2F::zero(), cx);
-    });
-
-    assert_eq!(
-        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
-        [
-            DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1),
-            DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)
-        ]
-    );
-
-    editor.update(cx, |view, cx| {
-        view.end_selection(cx);
-    });
-
-    assert_eq!(
-        editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
-        [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)]
-    );
-}
-
-#[gpui::test]
-fn test_canceling_pending_selection(cx: &mut TestAppContext) {
-    init_test(cx, |_| {});
-
-    let view = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
-            build_editor(buffer, cx)
-        })
-        .root(cx);
-
-    view.update(cx, |view, cx| {
-        view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
-        );
-    });
-
-    view.update(cx, |view, cx| {
-        view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
-        );
-    });
-
-    view.update(cx, |view, cx| {
-        view.cancel(&Cancel, cx);
-        view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
-        );
-    });
-}
-
-#[gpui::test]
-fn test_clone(cx: &mut TestAppContext) {
-    init_test(cx, |_| {});
-
-    let (text, selection_ranges) = marked_text_ranges(
-        indoc! {"
-            one
-            two
-            threeˇ
-            four
-            fiveˇ
-        "},
-        true,
-    );
-
-    let editor = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple(&text, cx);
-            build_editor(buffer, cx)
-        })
-        .root(cx);
-
-    editor.update(cx, |editor, cx| {
-        editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
-        editor.fold_ranges(
-            [
-                Point::new(1, 0)..Point::new(2, 0),
-                Point::new(3, 0)..Point::new(4, 0),
-            ],
-            true,
-            cx,
-        );
-    });
-
-    let cloned_editor = editor
-        .update(cx, |editor, cx| {
-            cx.add_window(Default::default(), |cx| editor.clone(cx))
-        })
-        .root(cx);
-
-    let snapshot = editor.update(cx, |e, cx| e.snapshot(cx));
-    let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx));
-
-    assert_eq!(
-        cloned_editor.update(cx, |e, cx| e.display_text(cx)),
-        editor.update(cx, |e, cx| e.display_text(cx))
-    );
-    assert_eq!(
-        cloned_snapshot
-            .folds_in_range(0..text.len())
-            .collect::<Vec<_>>(),
-        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
-    );
-    assert_set_eq!(
-        cloned_editor.read_with(cx, |editor, cx| editor.selections.ranges::<Point>(cx)),
-        editor.read_with(cx, |editor, cx| editor.selections.ranges(cx))
-    );
-    assert_set_eq!(
-        cloned_editor.update(cx, |e, cx| e.selections.display_ranges(cx)),
-        editor.update(cx, |e, cx| e.selections.display_ranges(cx))
-    );
-}
-
-#[gpui::test]
-async fn test_navigation_history(cx: &mut TestAppContext) {
-    init_test(cx, |_| {});
-
-    cx.set_global(DragAndDrop::<Workspace>::default());
-    use workspace::item::Item;
-
-    let fs = FakeFs::new(cx.background());
-    let project = Project::test(fs, [], cx).await;
-    let window = cx.add_window(|cx| Workspace::test_new(project, cx));
-    let workspace = window.root(cx);
-    let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
-    window.add_view(cx, |cx| {
-        let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
-        let mut editor = build_editor(buffer.clone(), cx);
-        let handle = cx.handle();
-        editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
-
-        fn pop_history(editor: &mut Editor, cx: &mut WindowContext) -> Option<NavigationEntry> {
-            editor.nav_history.as_mut().unwrap().pop_backward(cx)
-        }
-
-        // Move the cursor a small distance.
-        // Nothing is added to the navigation history.
-        editor.change_selections(None, cx, |s| {
-            s.select_display_ranges([DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)])
-        });
-        editor.change_selections(None, cx, |s| {
-            s.select_display_ranges([DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)])
-        });
-        assert!(pop_history(&mut editor, cx).is_none());
-
-        // Move the cursor a large distance.
-        // The history can jump back to the previous position.
-        editor.change_selections(None, cx, |s| {
-            s.select_display_ranges([DisplayPoint::new(13, 0)..DisplayPoint::new(13, 3)])
-        });
-        let nav_entry = pop_history(&mut editor, cx).unwrap();
-        editor.navigate(nav_entry.data.unwrap(), cx);
-        assert_eq!(nav_entry.item.id(), cx.view_id());
-        assert_eq!(
-            editor.selections.display_ranges(cx),
-            &[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)]
-        );
-        assert!(pop_history(&mut editor, cx).is_none());
-
-        // Move the cursor a small distance via the mouse.
-        // Nothing is added to the navigation history.
-        editor.begin_selection(DisplayPoint::new(5, 0), false, 1, cx);
-        editor.end_selection(cx);
-        assert_eq!(
-            editor.selections.display_ranges(cx),
-            &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
-        );
-        assert!(pop_history(&mut editor, cx).is_none());
-
-        // Move the cursor a large distance via the mouse.
-        // The history can jump back to the previous position.
-        editor.begin_selection(DisplayPoint::new(15, 0), false, 1, cx);
-        editor.end_selection(cx);
-        assert_eq!(
-            editor.selections.display_ranges(cx),
-            &[DisplayPoint::new(15, 0)..DisplayPoint::new(15, 0)]
-        );
-        let nav_entry = pop_history(&mut editor, cx).unwrap();
-        editor.navigate(nav_entry.data.unwrap(), cx);
-        assert_eq!(nav_entry.item.id(), cx.view_id());
-        assert_eq!(
-            editor.selections.display_ranges(cx),
-            &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
-        );
-        assert!(pop_history(&mut editor, cx).is_none());
-
-        // Set scroll position to check later
-        editor.set_scroll_position(Vector2F::new(5.5, 5.5), cx);
-        let original_scroll_position = editor.scroll_manager.anchor();
-
-        // Jump to the end of the document and adjust scroll
-        editor.move_to_end(&MoveToEnd, cx);
-        editor.set_scroll_position(Vector2F::new(-2.5, -0.5), cx);
-        assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
-
-        let nav_entry = pop_history(&mut editor, cx).unwrap();
-        editor.navigate(nav_entry.data.unwrap(), cx);
-        assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
-
-        // Ensure we don't panic when navigation data contains invalid anchors *and* points.
-        let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
-        invalid_anchor.text_anchor.buffer_id = Some(999);
-        let invalid_point = Point::new(9999, 0);
-        editor.navigate(
-            Box::new(NavigationData {
-                cursor_anchor: invalid_anchor,
-                cursor_position: invalid_point,
-                scroll_anchor: ScrollAnchor {
-                    anchor: invalid_anchor,
-                    offset: Default::default(),
-                },
-                scroll_top_row: invalid_point.row,
-            }),
-            cx,
-        );
-        assert_eq!(
-            editor.selections.display_ranges(cx),
-            &[editor.max_point(cx)..editor.max_point(cx)]
-        );
-        assert_eq!(
-            editor.scroll_position(cx),
-            vec2f(0., editor.max_point(cx).row() as f32)
-        );
-
-        editor
-    });
-}
-
-#[gpui::test]
-fn test_cancel(cx: &mut TestAppContext) {
-    init_test(cx, |_| {});
-
-    let view = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
-            build_editor(buffer, cx)
-        })
-        .root(cx);
-
-    view.update(cx, |view, cx| {
-        view.begin_selection(DisplayPoint::new(3, 4), false, 1, cx);
-        view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
-        view.end_selection(cx);
-
-        view.begin_selection(DisplayPoint::new(0, 1), true, 1, cx);
-        view.update_selection(DisplayPoint::new(0, 3), 0, Vector2F::zero(), cx);
-        view.end_selection(cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            [
-                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
-                DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1),
-            ]
-        );
-    });
-
-    view.update(cx, |view, cx| {
-        view.cancel(&Cancel, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            [DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1)]
-        );
-    });
-
-    view.update(cx, |view, cx| {
-        view.cancel(&Cancel, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            [DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1)]
-        );
-    });
-}
-
-#[gpui::test]
-fn test_fold_action(cx: &mut TestAppContext) {
-    init_test(cx, |_| {});
-
-    let view = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple(
-                &"
-                impl Foo {
-                    // Hello!
-
-                    fn a() {
-                        1
-                    }
-
-                    fn b() {
-                        2
-                    }
-
-                    fn c() {
-                        3
-                    }
-                }
-            "
-                .unindent(),
-                cx,
-            );
-            build_editor(buffer.clone(), cx)
-        })
-        .root(cx);
-
-    view.update(cx, |view, cx| {
-        view.change_selections(None, cx, |s| {
-            s.select_display_ranges([DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)]);
-        });
-        view.fold(&Fold, cx);
-        assert_eq!(
-            view.display_text(cx),
-            "
-                impl Foo {
-                    // Hello!
-
-                    fn a() {
-                        1
-                    }
-
-                    fn b() {⋯
-                    }
-
-                    fn c() {⋯
-                    }
-                }
-            "
-            .unindent(),
-        );
-
-        view.fold(&Fold, cx);
-        assert_eq!(
-            view.display_text(cx),
-            "
-                impl Foo {⋯
-                }
-            "
-            .unindent(),
-        );
-
-        view.unfold_lines(&UnfoldLines, cx);
-        assert_eq!(
-            view.display_text(cx),
-            "
-                impl Foo {
-                    // Hello!
-
-                    fn a() {
-                        1
-                    }
-
-                    fn b() {⋯
-                    }
-
-                    fn c() {⋯
-                    }
-                }
-            "
-            .unindent(),
-        );
-
-        view.unfold_lines(&UnfoldLines, cx);
-        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
-    });
-}
-
-#[gpui::test]
-fn test_move_cursor(cx: &mut TestAppContext) {
-    init_test(cx, |_| {});
-
-    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
-    let view = cx
-        .add_window(|cx| build_editor(buffer.clone(), cx))
-        .root(cx);
-
-    buffer.update(cx, |buffer, cx| {
-        buffer.edit(
-            vec![
-                (Point::new(1, 0)..Point::new(1, 0), "\t"),
-                (Point::new(1, 1)..Point::new(1, 1), "\t"),
-            ],
-            None,
-            cx,
-        );
-    });
-    view.update(cx, |view, cx| {
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
-        );
-
-        view.move_down(&MoveDown, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
-        );
-
-        view.move_right(&MoveRight, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)]
-        );
-
-        view.move_left(&MoveLeft, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
-        );
-
-        view.move_up(&MoveUp, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
-        );
-
-        view.move_to_end(&MoveToEnd, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[DisplayPoint::new(5, 6)..DisplayPoint::new(5, 6)]
-        );
-
-        view.move_to_beginning(&MoveToBeginning, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
-        );
-
-        view.change_selections(None, cx, |s| {
-            s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]);
-        });
-        view.select_to_beginning(&SelectToBeginning, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 0)]
-        );
-
-        view.select_to_end(&SelectToEnd, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[DisplayPoint::new(0, 1)..DisplayPoint::new(5, 6)]
-        );
-    });
-}
-
-#[gpui::test]
-fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
-    init_test(cx, |_| {});
-
-    let view = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε", cx);
-            build_editor(buffer.clone(), cx)
-        })
-        .root(cx);
-
-    assert_eq!('ⓐ'.len_utf8(), 3);
-    assert_eq!('α'.len_utf8(), 2);
-
-    view.update(cx, |view, cx| {
-        view.fold_ranges(
-            vec![
-                Point::new(0, 6)..Point::new(0, 12),
-                Point::new(1, 2)..Point::new(1, 4),
-                Point::new(2, 4)..Point::new(2, 8),
-            ],
-            true,
-            cx,
-        );
-        assert_eq!(view.display_text(cx), "ⓐⓑ⋯ⓔ\nab⋯e\nαβ⋯ε");
-
-        view.move_right(&MoveRight, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[empty_range(0, "ⓐ".len())]
-        );
-        view.move_right(&MoveRight, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[empty_range(0, "ⓐⓑ".len())]
-        );
-        view.move_right(&MoveRight, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[empty_range(0, "ⓐⓑ⋯".len())]
-        );
-
-        view.move_down(&MoveDown, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[empty_range(1, "ab⋯e".len())]
-        );
-        view.move_left(&MoveLeft, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[empty_range(1, "ab⋯".len())]
-        );
-        view.move_left(&MoveLeft, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[empty_range(1, "ab".len())]
-        );
-        view.move_left(&MoveLeft, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[empty_range(1, "a".len())]
-        );
-
-        view.move_down(&MoveDown, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[empty_range(2, "α".len())]
-        );
-        view.move_right(&MoveRight, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[empty_range(2, "αβ".len())]
-        );
-        view.move_right(&MoveRight, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[empty_range(2, "αβ⋯".len())]
-        );
-        view.move_right(&MoveRight, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[empty_range(2, "αβ⋯ε".len())]
-        );
-
-        view.move_up(&MoveUp, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[empty_range(1, "ab⋯e".len())]
-        );
-        view.move_down(&MoveDown, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[empty_range(2, "αβ⋯ε".len())]
-        );
-        view.move_up(&MoveUp, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[empty_range(1, "ab⋯e".len())]
-        );
-
-        view.move_up(&MoveUp, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[empty_range(0, "ⓐⓑ".len())]
-        );
-        view.move_left(&MoveLeft, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[empty_range(0, "ⓐ".len())]
-        );
-        view.move_left(&MoveLeft, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[empty_range(0, "".len())]
-        );
-    });
-}
-
-#[gpui::test]
-fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
-    init_test(cx, |_| {});
-
-    let view = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
-            build_editor(buffer.clone(), cx)
-        })
-        .root(cx);
-    view.update(cx, |view, cx| {
-        view.change_selections(None, cx, |s| {
-            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
-        });
-        view.move_down(&MoveDown, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[empty_range(1, "abcd".len())]
-        );
-
-        view.move_down(&MoveDown, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[empty_range(2, "αβγ".len())]
-        );
-
-        view.move_down(&MoveDown, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[empty_range(3, "abcd".len())]
-        );
-
-        view.move_down(&MoveDown, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
-        );
-
-        view.move_up(&MoveUp, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[empty_range(3, "abcd".len())]
-        );
-
-        view.move_up(&MoveUp, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[empty_range(2, "αβγ".len())]
-        );
-    });
-}
-
-#[gpui::test]
-fn test_beginning_end_of_line(cx: &mut TestAppContext) {
-    init_test(cx, |_| {});
-
-    let view = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple("abc\n  def", cx);
-            build_editor(buffer, cx)
-        })
-        .root(cx);
-    view.update(cx, |view, cx| {
-        view.change_selections(None, cx, |s| {
-            s.select_display_ranges([
-                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
-                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
-            ]);
-        });
-    });
-
-    view.update(cx, |view, cx| {
-        view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[
-                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
-                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
-            ]
-        );
-    });
-
-    view.update(cx, |view, cx| {
-        view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[
-                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
-                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
-            ]
-        );
-    });
-
-    view.update(cx, |view, cx| {
-        view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[
-                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
-                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
-            ]
-        );
-    });
-
-    view.update(cx, |view, cx| {
-        view.move_to_end_of_line(&MoveToEndOfLine, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[
-                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
-                DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
-            ]
-        );
-    });
-
-    // Moving to the end of line again is a no-op.
-    view.update(cx, |view, cx| {
-        view.move_to_end_of_line(&MoveToEndOfLine, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[
-                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
-                DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
-            ]
-        );
-    });
-
-    view.update(cx, |view, cx| {
-        view.move_left(&MoveLeft, cx);
-        view.select_to_beginning_of_line(
-            &SelectToBeginningOfLine {
-                stop_at_soft_wraps: true,
-            },
-            cx,
-        );
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[
-                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
-                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
-            ]
-        );
-    });
-
-    view.update(cx, |view, cx| {
-        view.select_to_beginning_of_line(
-            &SelectToBeginningOfLine {
-                stop_at_soft_wraps: true,
-            },
-            cx,
-        );
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[
-                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
-                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 0),
-            ]
-        );
-    });
-
-    view.update(cx, |view, cx| {
-        view.select_to_beginning_of_line(
-            &SelectToBeginningOfLine {
-                stop_at_soft_wraps: true,
-            },
-            cx,
-        );
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[
-                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
-                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
-            ]
-        );
-    });
-
-    view.update(cx, |view, cx| {
-        view.select_to_end_of_line(
-            &SelectToEndOfLine {
-                stop_at_soft_wraps: true,
-            },
-            cx,
-        );
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[
-                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
-                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 5),
-            ]
-        );
-    });
-
-    view.update(cx, |view, cx| {
-        view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
-        assert_eq!(view.display_text(cx), "ab\n  de");
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[
-                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
-                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
-            ]
-        );
-    });
-
-    view.update(cx, |view, cx| {
-        view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
-        assert_eq!(view.display_text(cx), "\n");
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[
-                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
-                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
-            ]
-        );
-    });
-}
-
-#[gpui::test]
-fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
-    init_test(cx, |_| {});
-
-    let view = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
-            build_editor(buffer, cx)
-        })
-        .root(cx);
-    view.update(cx, |view, cx| {
-        view.change_selections(None, cx, |s| {
-            s.select_display_ranges([
-                DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11),
-                DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4),
-            ])
-        });
-
-        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
-        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
-
-        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
-        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", view, cx);
-
-        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
-        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", view, cx);
-
-        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
-        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
-
-        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
-        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", view, cx);
-
-        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
-        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", view, cx);
-
-        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
-        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
-
-        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
-        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
-
-        view.move_right(&MoveRight, cx);
-        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
-        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
-
-        view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
-        assert_selection_ranges("use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}", view, cx);
-
-        view.select_to_next_word_end(&SelectToNextWordEnd, cx);
-        assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
-    });
-}
-
-#[gpui::test]
-fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
-    init_test(cx, |_| {});
-
-    let view = cx
-        .add_window(|cx| {
-            let buffer =
-                MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
-            build_editor(buffer, cx)
-        })
-        .root(cx);
-
-    view.update(cx, |view, cx| {
-        view.set_wrap_width(Some(140.), cx);
-        assert_eq!(
-            view.display_text(cx),
-            "use one::{\n    two::three::\n    four::five\n};"
-        );
-
-        view.change_selections(None, cx, |s| {
-            s.select_display_ranges([DisplayPoint::new(1, 7)..DisplayPoint::new(1, 7)]);
-        });
-
-        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[DisplayPoint::new(1, 9)..DisplayPoint::new(1, 9)]
-        );
-
-        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
-        );
-
-        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
-        );
-
-        view.move_to_next_word_end(&MoveToNextWordEnd, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[DisplayPoint::new(2, 8)..DisplayPoint::new(2, 8)]
-        );
-
-        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
-        );
-
-        view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
-        );
-    });
-}
-
-#[gpui::test]
-async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |_| {});
-    let mut cx = EditorTestContext::new(cx).await;
-
-    let line_height = cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache()));
-    let window = cx.window;
-    window.simulate_resize(vec2f(100., 4. * line_height), &mut cx);
-
-    cx.set_state(
-        &r#"ˇone
-        two
-
-        three
-        fourˇ
-        five
-
-        six"#
-            .unindent(),
-    );
-
-    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
-    cx.assert_editor_state(
-        &r#"one
-        two
-        ˇ
-        three
-        four
-        five
-        ˇ
-        six"#
-            .unindent(),
-    );
-
-    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
-    cx.assert_editor_state(
-        &r#"one
-        two
-
-        three
-        four
-        five
-        ˇ
-        sixˇ"#
-            .unindent(),
-    );
-
-    cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
-    cx.assert_editor_state(
-        &r#"one
-        two
-
-        three
-        four
-        five
-
-        sixˇ"#
-            .unindent(),
-    );
-
-    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
-    cx.assert_editor_state(
-        &r#"one
-        two
-
-        three
-        four
-        five
-        ˇ
-        six"#
-            .unindent(),
-    );
-
-    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
-    cx.assert_editor_state(
-        &r#"one
-        two
-        ˇ
-        three
-        four
-        five
-
-        six"#
-            .unindent(),
-    );
-
-    cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
-    cx.assert_editor_state(
-        &r#"ˇone
-        two
-
-        three
-        four
-        five
-
-        six"#
-            .unindent(),
-    );
-}
-
-#[gpui::test]
-async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |_| {});
-    let mut cx = EditorTestContext::new(cx).await;
-    let line_height = cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache()));
-    let window = cx.window;
-    window.simulate_resize(vec2f(1000., 4. * line_height + 0.5), &mut cx);
-
-    cx.set_state(
-        &r#"ˇone
-        two
-        three
-        four
-        five
-        six
-        seven
-        eight
-        nine
-        ten
-        "#,
-    );
-
-    cx.update_editor(|editor, cx| {
-        assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 0.));
-        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
-        assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 3.));
-        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
-        assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 6.));
-        editor.scroll_screen(&ScrollAmount::Page(-1.), cx);
-        assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 3.));
-
-        editor.scroll_screen(&ScrollAmount::Page(-0.5), cx);
-        assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 1.));
-        editor.scroll_screen(&ScrollAmount::Page(0.5), cx);
-        assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 3.));
-    });
-}
-
-#[gpui::test]
-async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |_| {});
-    let mut cx = EditorTestContext::new(cx).await;
-
-    let line_height = cx.update_editor(|editor, cx| {
-        editor.set_vertical_scroll_margin(2, cx);
-        editor.style(cx).text.line_height(cx.font_cache())
-    });
-
-    let window = cx.window;
-    window.simulate_resize(vec2f(1000., 6.0 * line_height), &mut cx);
-
-    cx.set_state(
-        &r#"ˇone
-            two
-            three
-            four
-            five
-            six
-            seven
-            eight
-            nine
-            ten
-        "#,
-    );
-    cx.update_editor(|editor, cx| {
-        assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 0.0));
-    });
-
-    // Add a cursor below the visible area. Since both cursors cannot fit
-    // on screen, the editor autoscrolls to reveal the newest cursor, and
-    // allows the vertical scroll margin below that cursor.
-    cx.update_editor(|editor, cx| {
-        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
-            selections.select_ranges([
-                Point::new(0, 0)..Point::new(0, 0),
-                Point::new(6, 0)..Point::new(6, 0),
-            ]);
-        })
-    });
-    cx.update_editor(|editor, cx| {
-        assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 3.0));
-    });
-
-    // Move down. The editor cursor scrolls down to track the newest cursor.
-    cx.update_editor(|editor, cx| {
-        editor.move_down(&Default::default(), cx);
-    });
-    cx.update_editor(|editor, cx| {
-        assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 4.0));
-    });
-
-    // Add a cursor above the visible area. Since both cursors fit on screen,
-    // the editor scrolls to show both.
-    cx.update_editor(|editor, cx| {
-        editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
-            selections.select_ranges([
-                Point::new(1, 0)..Point::new(1, 0),
-                Point::new(6, 0)..Point::new(6, 0),
-            ]);
-        })
-    });
-    cx.update_editor(|editor, cx| {
-        assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 1.0));
-    });
-}
-
-#[gpui::test]
-async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |_| {});
-    let mut cx = EditorTestContext::new(cx).await;
-
-    let line_height = cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache()));
-    let window = cx.window;
-    window.simulate_resize(vec2f(100., 4. * line_height), &mut cx);
-
-    cx.set_state(
-        &r#"
-        ˇone
-        two
-        threeˇ
-        four
-        five
-        six
-        seven
-        eight
-        nine
-        ten
-        "#
-        .unindent(),
-    );
-
-    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
-    cx.assert_editor_state(
-        &r#"
-        one
-        two
-        three
-        ˇfour
-        five
-        sixˇ
-        seven
-        eight
-        nine
-        ten
-        "#
-        .unindent(),
-    );
-
-    cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
-    cx.assert_editor_state(
-        &r#"
-        one
-        two
-        three
-        four
-        five
-        six
-        ˇseven
-        eight
-        nineˇ
-        ten
-        "#
-        .unindent(),
-    );
-
-    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
-    cx.assert_editor_state(
-        &r#"
-        one
-        two
-        three
-        ˇfour
-        five
-        sixˇ
-        seven
-        eight
-        nine
-        ten
-        "#
-        .unindent(),
-    );
-
-    cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
-    cx.assert_editor_state(
-        &r#"
-        ˇone
-        two
-        threeˇ
-        four
-        five
-        six
-        seven
-        eight
-        nine
-        ten
-        "#
-        .unindent(),
-    );
-
-    // Test select collapsing
-    cx.update_editor(|editor, cx| {
-        editor.move_page_down(&MovePageDown::default(), cx);
-        editor.move_page_down(&MovePageDown::default(), cx);
-        editor.move_page_down(&MovePageDown::default(), cx);
-    });
-    cx.assert_editor_state(
-        &r#"
-        one
-        two
-        three
-        four
-        five
-        six
-        seven
-        eight
-        nine
-        ˇten
-        ˇ"#
-        .unindent(),
-    );
-}
-
-#[gpui::test]
-async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |_| {});
-    let mut cx = EditorTestContext::new(cx).await;
-    cx.set_state("one «two threeˇ» four");
-    cx.update_editor(|editor, cx| {
-        editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
-        assert_eq!(editor.text(cx), " four");
-    });
-}
-
-#[gpui::test]
-fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
-    init_test(cx, |_| {});
-
-    let view = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple("one two three four", cx);
-            build_editor(buffer.clone(), cx)
-        })
-        .root(cx);
-
-    view.update(cx, |view, cx| {
-        view.change_selections(None, cx, |s| {
-            s.select_display_ranges([
-                // an empty selection - the preceding word fragment is deleted
-                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
-                // characters selected - they are deleted
-                DisplayPoint::new(0, 9)..DisplayPoint::new(0, 12),
-            ])
-        });
-        view.delete_to_previous_word_start(&DeleteToPreviousWordStart, cx);
-        assert_eq!(view.buffer.read(cx).read(cx).text(), "e two te four");
-    });
-
-    view.update(cx, |view, cx| {
-        view.change_selections(None, cx, |s| {
-            s.select_display_ranges([
-                // an empty selection - the following word fragment is deleted
-                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
-                // characters selected - they are deleted
-                DisplayPoint::new(0, 9)..DisplayPoint::new(0, 10),
-            ])
-        });
-        view.delete_to_next_word_end(&DeleteToNextWordEnd, cx);
-        assert_eq!(view.buffer.read(cx).read(cx).text(), "e t te our");
-    });
-}
-
-#[gpui::test]
-fn test_newline(cx: &mut TestAppContext) {
-    init_test(cx, |_| {});
-
-    let view = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
-            build_editor(buffer.clone(), cx)
-        })
-        .root(cx);
-
-    view.update(cx, |view, cx| {
-        view.change_selections(None, cx, |s| {
-            s.select_display_ranges([
-                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
-                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
-                DisplayPoint::new(1, 6)..DisplayPoint::new(1, 6),
-            ])
-        });
-
-        view.newline(&Newline, cx);
-        assert_eq!(view.text(cx), "aa\naa\n  \n    bb\n    bb\n");
-    });
-}
-
-#[gpui::test]
-fn test_newline_with_old_selections(cx: &mut TestAppContext) {
-    init_test(cx, |_| {});
-
-    let editor = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple(
-                "
-                a
-                b(
-                    X
-                )
-                c(
-                    X
-                )
-            "
-                .unindent()
-                .as_str(),
-                cx,
-            );
-            let mut editor = build_editor(buffer.clone(), cx);
-            editor.change_selections(None, cx, |s| {
-                s.select_ranges([
-                    Point::new(2, 4)..Point::new(2, 5),
-                    Point::new(5, 4)..Point::new(5, 5),
-                ])
-            });
-            editor
-        })
-        .root(cx);
-
-    editor.update(cx, |editor, cx| {
-        // Edit the buffer directly, deleting ranges surrounding the editor's selections
-        editor.buffer.update(cx, |buffer, cx| {
-            buffer.edit(
-                [
-                    (Point::new(1, 2)..Point::new(3, 0), ""),
-                    (Point::new(4, 2)..Point::new(6, 0), ""),
-                ],
-                None,
-                cx,
-            );
-            assert_eq!(
-                buffer.read(cx).text(),
-                "
-                    a
-                    b()
-                    c()
-                "
-                .unindent()
-            );
-        });
-        assert_eq!(
-            editor.selections.ranges(cx),
-            &[
-                Point::new(1, 2)..Point::new(1, 2),
-                Point::new(2, 2)..Point::new(2, 2),
-            ],
-        );
-
-        editor.newline(&Newline, cx);
-        assert_eq!(
-            editor.text(cx),
-            "
-                a
-                b(
-                )
-                c(
-                )
-            "
-            .unindent()
-        );
-
-        // The selections are moved after the inserted newlines
-        assert_eq!(
-            editor.selections.ranges(cx),
-            &[
-                Point::new(2, 0)..Point::new(2, 0),
-                Point::new(4, 0)..Point::new(4, 0),
-            ],
-        );
-    });
-}
-
-#[gpui::test]
-async fn test_newline_above(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |settings| {
-        settings.defaults.tab_size = NonZeroU32::new(4)
-    });
-
-    let language = Arc::new(
-        Language::new(
-            LanguageConfig::default(),
-            Some(tree_sitter_rust::language()),
-        )
-        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
-        .unwrap(),
-    );
-
-    let mut cx = EditorTestContext::new(cx).await;
-    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
-    cx.set_state(indoc! {"
-        const a: ˇA = (
-            (ˇ
-                «const_functionˇ»(ˇ),
-                so«mˇ»et«hˇ»ing_ˇelse,ˇ
-            )ˇ
-        ˇ);ˇ
-    "});
-
-    cx.update_editor(|e, cx| e.newline_above(&NewlineAbove, cx));
-    cx.assert_editor_state(indoc! {"
-        ˇ
-        const a: A = (
-            ˇ
-            (
-                ˇ
-                ˇ
-                const_function(),
-                ˇ
-                ˇ
-                ˇ
-                ˇ
-                something_else,
-                ˇ
-            )
-            ˇ
-            ˇ
-        );
-    "});
-}
-
-#[gpui::test]
-async fn test_newline_below(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |settings| {
-        settings.defaults.tab_size = NonZeroU32::new(4)
-    });
-
-    let language = Arc::new(
-        Language::new(
-            LanguageConfig::default(),
-            Some(tree_sitter_rust::language()),
-        )
-        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
-        .unwrap(),
-    );
-
-    let mut cx = EditorTestContext::new(cx).await;
-    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
-    cx.set_state(indoc! {"
-        const a: ˇA = (
-            (ˇ
-                «const_functionˇ»(ˇ),
-                so«mˇ»et«hˇ»ing_ˇelse,ˇ
-            )ˇ
-        ˇ);ˇ
-    "});
-
-    cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
-    cx.assert_editor_state(indoc! {"
-        const a: A = (
-            ˇ
-            (
-                ˇ
-                const_function(),
-                ˇ
-                ˇ
-                something_else,
-                ˇ
-                ˇ
-                ˇ
-                ˇ
-            )
-            ˇ
-        );
-        ˇ
-        ˇ
-    "});
-}
-
-#[gpui::test]
-async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |settings| {
-        settings.defaults.tab_size = NonZeroU32::new(4)
-    });
-
-    let language = Arc::new(Language::new(
-        LanguageConfig {
-            line_comment: Some("//".into()),
-            ..LanguageConfig::default()
-        },
-        None,
-    ));
-    {
-        let mut cx = EditorTestContext::new(cx).await;
-        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
-        cx.set_state(indoc! {"
-        // Fooˇ
-    "});
-
-        cx.update_editor(|e, cx| e.newline(&Newline, cx));
-        cx.assert_editor_state(indoc! {"
-        // Foo
-        //ˇ
-    "});
-        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
-        cx.set_state(indoc! {"
-        ˇ// Foo
-    "});
-        cx.update_editor(|e, cx| e.newline(&Newline, cx));
-        cx.assert_editor_state(indoc! {"
-
-        ˇ// Foo
-    "});
-    }
-    // Ensure that comment continuations can be disabled.
-    update_test_language_settings(cx, |settings| {
-        settings.defaults.extend_comment_on_newline = Some(false);
-    });
-    let mut cx = EditorTestContext::new(cx).await;
-    cx.set_state(indoc! {"
-        // Fooˇ
-    "});
-    cx.update_editor(|e, cx| e.newline(&Newline, cx));
-    cx.assert_editor_state(indoc! {"
-        // Foo
-        ˇ
-    "});
-}
-
-#[gpui::test]
-fn test_insert_with_old_selections(cx: &mut TestAppContext) {
-    init_test(cx, |_| {});
-
-    let editor = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
-            let mut editor = build_editor(buffer.clone(), cx);
-            editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
-            editor
-        })
-        .root(cx);
-
-    editor.update(cx, |editor, cx| {
-        // Edit the buffer directly, deleting ranges surrounding the editor's selections
-        editor.buffer.update(cx, |buffer, cx| {
-            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
-            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
-        });
-        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
-
-        editor.insert("Z", cx);
-        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
-
-        // The selections are moved after the inserted characters
-        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
-    });
-}
-
-#[gpui::test]
-async fn test_tab(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |settings| {
-        settings.defaults.tab_size = NonZeroU32::new(3)
-    });
-
-    let mut cx = EditorTestContext::new(cx).await;
-    cx.set_state(indoc! {"
-        ˇabˇc
-        ˇ🏀ˇ🏀ˇefg
-        dˇ
-    "});
-    cx.update_editor(|e, cx| e.tab(&Tab, cx));
-    cx.assert_editor_state(indoc! {"
-           ˇab ˇc
-           ˇ🏀  ˇ🏀  ˇefg
-        d  ˇ
-    "});
-
-    cx.set_state(indoc! {"
-        a
-        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
-    "});
-    cx.update_editor(|e, cx| e.tab(&Tab, cx));
-    cx.assert_editor_state(indoc! {"
-        a
-           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
-    "});
-}
-
-#[gpui::test]
-async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |_| {});
-
-    let mut cx = EditorTestContext::new(cx).await;
-    let language = Arc::new(
-        Language::new(
-            LanguageConfig::default(),
-            Some(tree_sitter_rust::language()),
-        )
-        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
-        .unwrap(),
-    );
-    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
-
-    // cursors that are already at the suggested indent level insert
-    // a soft tab. cursors that are to the left of the suggested indent
-    // auto-indent their line.
-    cx.set_state(indoc! {"
-        ˇ
-        const a: B = (
-            c(
-                d(
-        ˇ
-                )
-        ˇ
-        ˇ    )
-        );
-    "});
-    cx.update_editor(|e, cx| e.tab(&Tab, cx));
-    cx.assert_editor_state(indoc! {"
-            ˇ
-        const a: B = (
-            c(
-                d(
-                    ˇ
-                )
-                ˇ
-            ˇ)
-        );
-    "});
-
-    // handle auto-indent when there are multiple cursors on the same line
-    cx.set_state(indoc! {"
-        const a: B = (
-            c(
-        ˇ    ˇ
-        ˇ    )
-        );
-    "});
-    cx.update_editor(|e, cx| e.tab(&Tab, cx));
-    cx.assert_editor_state(indoc! {"
-        const a: B = (
-            c(
-                ˇ
-            ˇ)
-        );
-    "});
-}
-
-#[gpui::test]
-async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |settings| {
-        settings.defaults.tab_size = NonZeroU32::new(4)
-    });
-
-    let language = Arc::new(
-        Language::new(
-            LanguageConfig::default(),
-            Some(tree_sitter_rust::language()),
-        )
-        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
-        .unwrap(),
-    );
-
-    let mut cx = EditorTestContext::new(cx).await;
-    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
-    cx.set_state(indoc! {"
-        fn a() {
-            if b {
-        \t ˇc
-            }
-        }
-    "});
-
-    cx.update_editor(|e, cx| e.tab(&Tab, cx));
-    cx.assert_editor_state(indoc! {"
-        fn a() {
-            if b {
-                ˇc
-            }
-        }
-    "});
-}
-
-#[gpui::test]
-async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |settings| {
-        settings.defaults.tab_size = NonZeroU32::new(4);
-    });
-
-    let mut cx = EditorTestContext::new(cx).await;
-
-    cx.set_state(indoc! {"
-          «oneˇ» «twoˇ»
-        three
-         four
-    "});
-    cx.update_editor(|e, cx| e.tab(&Tab, cx));
-    cx.assert_editor_state(indoc! {"
-            «oneˇ» «twoˇ»
-        three
-         four
-    "});
-
-    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
-    cx.assert_editor_state(indoc! {"
-        «oneˇ» «twoˇ»
-        three
-         four
-    "});
-
-    // select across line ending
-    cx.set_state(indoc! {"
-        one two
-        t«hree
-        ˇ» four
-    "});
-    cx.update_editor(|e, cx| e.tab(&Tab, cx));
-    cx.assert_editor_state(indoc! {"
-        one two
-            t«hree
-        ˇ» four
-    "});
-
-    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
-    cx.assert_editor_state(indoc! {"
-        one two
-        t«hree
-        ˇ» four
-    "});
-
-    // Ensure that indenting/outdenting works when the cursor is at column 0.
-    cx.set_state(indoc! {"
-        one two
-        ˇthree
-            four
-    "});
-    cx.update_editor(|e, cx| e.tab(&Tab, cx));
-    cx.assert_editor_state(indoc! {"
-        one two
-            ˇthree
-            four
-    "});
-
-    cx.set_state(indoc! {"
-        one two
-        ˇ    three
-            four
-    "});
-    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
-    cx.assert_editor_state(indoc! {"
-        one two
-        ˇthree
-            four
-    "});
-}
-
-#[gpui::test]
-async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |settings| {
-        settings.defaults.hard_tabs = Some(true);
-    });
-
-    let mut cx = EditorTestContext::new(cx).await;
-
-    // select two ranges on one line
-    cx.set_state(indoc! {"
-        «oneˇ» «twoˇ»
-        three
-        four
-    "});
-    cx.update_editor(|e, cx| e.tab(&Tab, cx));
-    cx.assert_editor_state(indoc! {"
-        \t«oneˇ» «twoˇ»
-        three
-        four
-    "});
-    cx.update_editor(|e, cx| e.tab(&Tab, cx));
-    cx.assert_editor_state(indoc! {"
-        \t\t«oneˇ» «twoˇ»
-        three
-        four
-    "});
-    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
-    cx.assert_editor_state(indoc! {"
-        \t«oneˇ» «twoˇ»
-        three
-        four
-    "});
-    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
-    cx.assert_editor_state(indoc! {"
-        «oneˇ» «twoˇ»
-        three
-        four
-    "});
-
-    // select across a line ending
-    cx.set_state(indoc! {"
-        one two
-        t«hree
-        ˇ»four
-    "});
-    cx.update_editor(|e, cx| e.tab(&Tab, cx));
-    cx.assert_editor_state(indoc! {"
-        one two
-        \tt«hree
-        ˇ»four
-    "});
-    cx.update_editor(|e, cx| e.tab(&Tab, cx));
-    cx.assert_editor_state(indoc! {"
-        one two
-        \t\tt«hree
-        ˇ»four
-    "});
-    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
-    cx.assert_editor_state(indoc! {"
-        one two
-        \tt«hree
-        ˇ»four
-    "});
-    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
-    cx.assert_editor_state(indoc! {"
-        one two
-        t«hree
-        ˇ»four
-    "});
-
-    // Ensure that indenting/outdenting works when the cursor is at column 0.
-    cx.set_state(indoc! {"
-        one two
-        ˇthree
-        four
-    "});
-    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
-    cx.assert_editor_state(indoc! {"
-        one two
-        ˇthree
-        four
-    "});
-    cx.update_editor(|e, cx| e.tab(&Tab, cx));
-    cx.assert_editor_state(indoc! {"
-        one two
-        \tˇthree
-        four
-    "});
-    cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
-    cx.assert_editor_state(indoc! {"
-        one two
-        ˇthree
-        four
-    "});
-}
-
-#[gpui::test]
-fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
-    init_test(cx, |settings| {
-        settings.languages.extend([
-            (
-                "TOML".into(),
-                LanguageSettingsContent {
-                    tab_size: NonZeroU32::new(2),
-                    ..Default::default()
-                },
-            ),
-            (
-                "Rust".into(),
-                LanguageSettingsContent {
-                    tab_size: NonZeroU32::new(4),
-                    ..Default::default()
-                },
-            ),
-        ]);
-    });
-
-    let toml_language = Arc::new(Language::new(
-        LanguageConfig {
-            name: "TOML".into(),
-            ..Default::default()
-        },
-        None,
-    ));
-    let rust_language = Arc::new(Language::new(
-        LanguageConfig {
-            name: "Rust".into(),
-            ..Default::default()
-        },
-        None,
-    ));
-
-    let toml_buffer = cx.add_model(|cx| {
-        Buffer::new(0, cx.model_id() as u64, "a = 1\nb = 2\n").with_language(toml_language, cx)
-    });
-    let rust_buffer = cx.add_model(|cx| {
-        Buffer::new(0, cx.model_id() as u64, "const c: usize = 3;\n")
-            .with_language(rust_language, cx)
-    });
-    let multibuffer = cx.add_model(|cx| {
-        let mut multibuffer = MultiBuffer::new(0);
-        multibuffer.push_excerpts(
-            toml_buffer.clone(),
-            [ExcerptRange {
-                context: Point::new(0, 0)..Point::new(2, 0),
-                primary: None,
-            }],
-            cx,
-        );
-        multibuffer.push_excerpts(
-            rust_buffer.clone(),
-            [ExcerptRange {
-                context: Point::new(0, 0)..Point::new(1, 0),
-                primary: None,
-            }],
-            cx,
-        );
-        multibuffer
-    });
-
-    cx.add_window(|cx| {
-        let mut editor = build_editor(multibuffer, cx);
-
-        assert_eq!(
-            editor.text(cx),
-            indoc! {"
-                a = 1
-                b = 2
-
-                const c: usize = 3;
-            "}
-        );
-
-        select_ranges(
-            &mut editor,
-            indoc! {"
-                «aˇ» = 1
-                b = 2
-
-                «const c:ˇ» usize = 3;
-            "},
-            cx,
-        );
-
-        editor.tab(&Tab, cx);
-        assert_text_with_selections(
-            &mut editor,
-            indoc! {"
-                  «aˇ» = 1
-                b = 2
-
-                    «const c:ˇ» usize = 3;
-            "},
-            cx,
-        );
-        editor.tab_prev(&TabPrev, cx);
-        assert_text_with_selections(
-            &mut editor,
-            indoc! {"
-                «aˇ» = 1
-                b = 2
-
-                «const c:ˇ» usize = 3;
-            "},
-            cx,
-        );
-
-        editor
-    });
-}
-
-#[gpui::test]
-async fn test_backspace(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |_| {});
-
-    let mut cx = EditorTestContext::new(cx).await;
-
-    // Basic backspace
-    cx.set_state(indoc! {"
-        onˇe two three
-        fou«rˇ» five six
-        seven «ˇeight nine
-        »ten
-    "});
-    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
-    cx.assert_editor_state(indoc! {"
-        oˇe two three
-        fouˇ five six
-        seven ˇten
-    "});
-
-    // Test backspace inside and around indents
-    cx.set_state(indoc! {"
-        zero
-            ˇone
-                ˇtwo
-            ˇ ˇ ˇ  three
-        ˇ  ˇ  four
-    "});
-    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
-    cx.assert_editor_state(indoc! {"
-        zero
-        ˇone
-            ˇtwo
-        ˇ  threeˇ  four
-    "});
-
-    // Test backspace with line_mode set to true
-    cx.update_editor(|e, _| e.selections.line_mode = true);
-    cx.set_state(indoc! {"
-        The ˇquick ˇbrown
-        fox jumps over
-        the lazy dog
-        ˇThe qu«ick bˇ»rown"});
-    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
-    cx.assert_editor_state(indoc! {"
-        ˇfox jumps over
-        the lazy dogˇ"});
-}
-
-#[gpui::test]
-async fn test_delete(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |_| {});
-
-    let mut cx = EditorTestContext::new(cx).await;
-    cx.set_state(indoc! {"
-        onˇe two three
-        fou«rˇ» five six
-        seven «ˇeight nine
-        »ten
-    "});
-    cx.update_editor(|e, cx| e.delete(&Delete, cx));
-    cx.assert_editor_state(indoc! {"
-        onˇ two three
-        fouˇ five six
-        seven ˇten
-    "});
-
-    // Test backspace with line_mode set to true
-    cx.update_editor(|e, _| e.selections.line_mode = true);
-    cx.set_state(indoc! {"
-        The ˇquick ˇbrown
-        fox «ˇjum»ps over
-        the lazy dog
-        ˇThe qu«ick bˇ»rown"});
-    cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
-    cx.assert_editor_state("ˇthe lazy dogˇ");
-}
-
-#[gpui::test]
-fn test_delete_line(cx: &mut TestAppContext) {
-    init_test(cx, |_| {});
-
-    let view = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
-            build_editor(buffer, cx)
-        })
-        .root(cx);
-    view.update(cx, |view, cx| {
-        view.change_selections(None, cx, |s| {
-            s.select_display_ranges([
-                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
-                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
-                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
-            ])
-        });
-        view.delete_line(&DeleteLine, cx);
-        assert_eq!(view.display_text(cx), "ghi");
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            vec![
-                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
-                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)
-            ]
-        );
-    });
-
-    let view = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
-            build_editor(buffer, cx)
-        })
-        .root(cx);
-    view.update(cx, |view, cx| {
-        view.change_selections(None, cx, |s| {
-            s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)])
-        });
-        view.delete_line(&DeleteLine, cx);
-        assert_eq!(view.display_text(cx), "ghi\n");
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            vec![DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)]
-        );
-    });
-}
-
-#[gpui::test]
-fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
-    init_test(cx, |_| {});
-
-    cx.add_window(|cx| {
-        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
-        let mut editor = build_editor(buffer.clone(), cx);
-        let buffer = buffer.read(cx).as_singleton().unwrap();
-
-        assert_eq!(
-            editor.selections.ranges::<Point>(cx),
-            &[Point::new(0, 0)..Point::new(0, 0)]
-        );
-
-        // When on single line, replace newline at end by space
-        editor.join_lines(&JoinLines, cx);
-        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
-        assert_eq!(
-            editor.selections.ranges::<Point>(cx),
-            &[Point::new(0, 3)..Point::new(0, 3)]
-        );
-
-        // When multiple lines are selected, remove newlines that are spanned by the selection
-        editor.change_selections(None, cx, |s| {
-            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
-        });
-        editor.join_lines(&JoinLines, cx);
-        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
-        assert_eq!(
-            editor.selections.ranges::<Point>(cx),
-            &[Point::new(0, 11)..Point::new(0, 11)]
-        );
-
-        // Undo should be transactional
-        editor.undo(&Undo, cx);
-        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
-        assert_eq!(
-            editor.selections.ranges::<Point>(cx),
-            &[Point::new(0, 5)..Point::new(2, 2)]
-        );
-
-        // When joining an empty line don't insert a space
-        editor.change_selections(None, cx, |s| {
-            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
-        });
-        editor.join_lines(&JoinLines, cx);
-        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
-        assert_eq!(
-            editor.selections.ranges::<Point>(cx),
-            [Point::new(2, 3)..Point::new(2, 3)]
-        );
-
-        // We can remove trailing newlines
-        editor.join_lines(&JoinLines, cx);
-        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
-        assert_eq!(
-            editor.selections.ranges::<Point>(cx),
-            [Point::new(2, 3)..Point::new(2, 3)]
-        );
-
-        // We don't blow up on the last line
-        editor.join_lines(&JoinLines, cx);
-        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
-        assert_eq!(
-            editor.selections.ranges::<Point>(cx),
-            [Point::new(2, 3)..Point::new(2, 3)]
-        );
-
-        // reset to test indentation
-        editor.buffer.update(cx, |buffer, cx| {
-            buffer.edit(
-                [
-                    (Point::new(1, 0)..Point::new(1, 2), "  "),
-                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
-                ],
-                None,
-                cx,
-            )
-        });
-
-        // We remove any leading spaces
-        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
-        editor.change_selections(None, cx, |s| {
-            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
-        });
-        editor.join_lines(&JoinLines, cx);
-        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
-
-        // We don't insert a space for a line containing only spaces
-        editor.join_lines(&JoinLines, cx);
-        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
-
-        // We ignore any leading tabs
-        editor.join_lines(&JoinLines, cx);
-        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
-
-        editor
-    });
-}
-
-#[gpui::test]
-fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
-    init_test(cx, |_| {});
-
-    cx.add_window(|cx| {
-        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
-        let mut editor = build_editor(buffer.clone(), cx);
-        let buffer = buffer.read(cx).as_singleton().unwrap();
-
-        editor.change_selections(None, cx, |s| {
-            s.select_ranges([
-                Point::new(0, 2)..Point::new(1, 1),
-                Point::new(1, 2)..Point::new(1, 2),
-                Point::new(3, 1)..Point::new(3, 2),
-            ])
-        });
-
-        editor.join_lines(&JoinLines, cx);
-        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
-
-        assert_eq!(
-            editor.selections.ranges::<Point>(cx),
-            [
-                Point::new(0, 7)..Point::new(0, 7),
-                Point::new(1, 3)..Point::new(1, 3)
-            ]
-        );
-        editor
-    });
-}
-
-#[gpui::test]
-async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
-    init_test(cx, |_| {});
-
-    let mut cx = EditorTestContext::new(cx).await;
-
-    // Test sort_lines_case_insensitive()
-    cx.set_state(indoc! {"
-        «z
-        y
-        x
-        Z
-        Y
-        Xˇ»
-    "});
-    cx.update_editor(|e, cx| e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, cx));
-    cx.assert_editor_state(indoc! {"
-        «x
-        X
-        y
-        Y
-        z
-        Zˇ»
-    "});
-
-    // Test reverse_lines()
-    cx.set_state(indoc! {"
-        «5
-        4
-        3
-        2
-        1ˇ»
-    "});
-    cx.update_editor(|e, cx| e.reverse_lines(&ReverseLines, cx));
-    cx.assert_editor_state(indoc! {"
-        «1
-        2
-        3
-        4
-        5ˇ»
-    "});
-
-    // Skip testing shuffle_line()
-
-    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
-    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
-
-    // Don't manipulate when cursor is on single line, but expand the selection
-    cx.set_state(indoc! {"
-        ddˇdd
-        ccc
-        bb
-        a
-    "});
-    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
-    cx.assert_editor_state(indoc! {"
-        «ddddˇ»
-        ccc
-        bb
-        a
-    "});
-
-    // Basic manipulate case
-    // Start selection moves to column 0
-    // End of selection shrinks to fit shorter line
-    cx.set_state(indoc! {"
-        dd«d
-        ccc
-        bb
-        aaaaaˇ»
-    "});
-    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
-    cx.assert_editor_state(indoc! {"
-        «aaaaa
-        bb
-        ccc
-        dddˇ»
-    "});
-
-    // Manipulate case with newlines
-    cx.set_state(indoc! {"
-        dd«d
-        ccc
-
-        bb
-        aaaaa
-
-        ˇ»
-    "});
-    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
-    cx.assert_editor_state(indoc! {"
-        «
-
-        aaaaa
-        bb
-        ccc
-        dddˇ»
-
-    "});
-}
-
-#[gpui::test]
-async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
-    init_test(cx, |_| {});
-
-    let mut cx = EditorTestContext::new(cx).await;
-
-    // Manipulate with multiple selections on a single line
-    cx.set_state(indoc! {"
-        dd«dd
-        cˇ»c«c
-        bb
-        aaaˇ»aa
-    "});
-    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
-    cx.assert_editor_state(indoc! {"
-        «aaaaa
-        bb
-        ccc
-        ddddˇ»
-    "});
-
-    // Manipulate with multiple disjoin selections
-    cx.set_state(indoc! {"
-        5«
-        4
-        3
-        2
-        1ˇ»
-
-        dd«dd
-        ccc
-        bb
-        aaaˇ»aa
-    "});
-    cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
-    cx.assert_editor_state(indoc! {"
-        «1
-        2
-        3
-        4
-        5ˇ»
-
-        «aaaaa
-        bb
-        ccc
-        ddddˇ»
-    "});
-}
-
-#[gpui::test]
-async fn test_manipulate_text(cx: &mut TestAppContext) {
-    init_test(cx, |_| {});
-
-    let mut cx = EditorTestContext::new(cx).await;
-
-    // Test convert_to_upper_case()
-    cx.set_state(indoc! {"
-        «hello worldˇ»
-    "});
-    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
-    cx.assert_editor_state(indoc! {"
-        «HELLO WORLDˇ»
-    "});
-
-    // Test convert_to_lower_case()
-    cx.set_state(indoc! {"
-        «HELLO WORLDˇ»
-    "});
-    cx.update_editor(|e, cx| e.convert_to_lower_case(&ConvertToLowerCase, cx));
-    cx.assert_editor_state(indoc! {"
-        «hello worldˇ»
-    "});
-
-    // Test multiple line, single selection case
-    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
-    cx.set_state(indoc! {"
-        «The quick brown
-        fox jumps over
-        the lazy dogˇ»
-    "});
-    cx.update_editor(|e, cx| e.convert_to_title_case(&ConvertToTitleCase, cx));
-    cx.assert_editor_state(indoc! {"
-        «The Quick Brown
-        Fox Jumps Over
-        The Lazy Dogˇ»
-    "});
-
-    // Test multiple line, single selection case
-    // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
-    cx.set_state(indoc! {"
-        «The quick brown
-        fox jumps over
-        the lazy dogˇ»
-    "});
-    cx.update_editor(|e, cx| e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, cx));
-    cx.assert_editor_state(indoc! {"
-        «TheQuickBrown
-        FoxJumpsOver
-        TheLazyDogˇ»
-    "});
-
-    // From here on out, test more complex cases of manipulate_text()
-
-    // Test no selection case - should affect words cursors are in
-    // Cursor at beginning, middle, and end of word
-    cx.set_state(indoc! {"
-        ˇhello big beauˇtiful worldˇ
-    "});
-    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
-    cx.assert_editor_state(indoc! {"
-        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
-    "});
-
-    // Test multiple selections on a single line and across multiple lines
-    cx.set_state(indoc! {"
-        «Theˇ» quick «brown
-        foxˇ» jumps «overˇ»
-        the «lazyˇ» dog
-    "});
-    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
-    cx.assert_editor_state(indoc! {"
-        «THEˇ» quick «BROWN
-        FOXˇ» jumps «OVERˇ»
-        the «LAZYˇ» dog
-    "});
-
-    // Test case where text length grows
-    cx.set_state(indoc! {"
-        «tschüߡ»
-    "});
-    cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
-    cx.assert_editor_state(indoc! {"
-        «TSCHÜSSˇ»
-    "});
-
-    // Test to make sure we don't crash when text shrinks
-    cx.set_state(indoc! {"
-        aaa_bbbˇ
-    "});
-    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
-    cx.assert_editor_state(indoc! {"
-        «aaaBbbˇ»
-    "});
-
-    // Test to make sure we all aware of the fact that each word can grow and shrink
-    // Final selections should be aware of this fact
-    cx.set_state(indoc! {"
-        aaa_bˇbb bbˇb_ccc ˇccc_ddd
-    "});
-    cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
-    cx.assert_editor_state(indoc! {"
-        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
-    "});
-}
-
-#[gpui::test]
-fn test_duplicate_line(cx: &mut TestAppContext) {
-    init_test(cx, |_| {});
-
-    let view = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
-            build_editor(buffer, cx)
-        })
-        .root(cx);
-    view.update(cx, |view, cx| {
-        view.change_selections(None, cx, |s| {
-            s.select_display_ranges([
-                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
-                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
-                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
-                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
-            ])
-        });
-        view.duplicate_line(&DuplicateLine, cx);
-        assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            vec![
-                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
-                DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
-                DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
-                DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
-            ]
-        );
-    });
-
-    let view = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
-            build_editor(buffer, cx)
-        })
-        .root(cx);
-    view.update(cx, |view, cx| {
-        view.change_selections(None, cx, |s| {
-            s.select_display_ranges([
-                DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
-                DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
-            ])
-        });
-        view.duplicate_line(&DuplicateLine, cx);
-        assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            vec![
-                DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1),
-                DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1),
-            ]
-        );
-    });
-}
-
-#[gpui::test]
-fn test_move_line_up_down(cx: &mut TestAppContext) {
-    init_test(cx, |_| {});
-
-    let view = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
-            build_editor(buffer, cx)
-        })
-        .root(cx);
-    view.update(cx, |view, cx| {
-        view.fold_ranges(
-            vec![
-                Point::new(0, 2)..Point::new(1, 2),
-                Point::new(2, 3)..Point::new(4, 1),
-                Point::new(7, 0)..Point::new(8, 4),
-            ],
-            true,
-            cx,
-        );
-        view.change_selections(None, cx, |s| {
-            s.select_display_ranges([
-                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
-                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
-                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
-                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
-            ])
-        });
-        assert_eq!(
-            view.display_text(cx),
-            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
-        );
-
-        view.move_line_up(&MoveLineUp, cx);
-        assert_eq!(
-            view.display_text(cx),
-            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
-        );
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            vec![
-                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
-                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
-                DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
-                DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
-            ]
-        );
-    });
-
-    view.update(cx, |view, cx| {
-        view.move_line_down(&MoveLineDown, cx);
-        assert_eq!(
-            view.display_text(cx),
-            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
-        );
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            vec![
-                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
-                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
-                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
-                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
-            ]
-        );
-    });
-
-    view.update(cx, |view, cx| {
-        view.move_line_down(&MoveLineDown, cx);
-        assert_eq!(
-            view.display_text(cx),
-            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
-        );
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            vec![
-                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
-                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
-                DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
-                DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
-            ]
-        );
-    });
-
-    view.update(cx, |view, cx| {
-        view.move_line_up(&MoveLineUp, cx);
-        assert_eq!(
-            view.display_text(cx),
-            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
-        );
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            vec![
-                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
-                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
-                DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
-                DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
-            ]
-        );
-    });
-}
-
-#[gpui::test]
-fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
-    init_test(cx, |_| {});
-
-    let editor = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
-            build_editor(buffer, cx)
-        })
-        .root(cx);
-    editor.update(cx, |editor, cx| {
-        let snapshot = editor.buffer.read(cx).snapshot(cx);
-        editor.insert_blocks(
-            [BlockProperties {
-                style: BlockStyle::Fixed,
-                position: snapshot.anchor_after(Point::new(2, 0)),
-                disposition: BlockDisposition::Below,
-                height: 1,
-                render: Arc::new(|_| Empty::new().into_any()),
-            }],
-            Some(Autoscroll::fit()),
-            cx,
-        );
-        editor.change_selections(None, cx, |s| {
-            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
-        });
-        editor.move_line_down(&MoveLineDown, cx);
-    });
-}
-
-#[gpui::test]
-fn test_transpose(cx: &mut TestAppContext) {
-    init_test(cx, |_| {});
-
-    _ = cx.add_window(|cx| {
-        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
-
-        editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
-        editor.transpose(&Default::default(), cx);
-        assert_eq!(editor.text(cx), "bac");
-        assert_eq!(editor.selections.ranges(cx), [2..2]);
-
-        editor.transpose(&Default::default(), cx);
-        assert_eq!(editor.text(cx), "bca");
-        assert_eq!(editor.selections.ranges(cx), [3..3]);
-
-        editor.transpose(&Default::default(), cx);
-        assert_eq!(editor.text(cx), "bac");
-        assert_eq!(editor.selections.ranges(cx), [3..3]);
-
-        editor
-    });
-
-    _ = cx.add_window(|cx| {
-        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
-
-        editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
-        editor.transpose(&Default::default(), cx);
-        assert_eq!(editor.text(cx), "acb\nde");
-        assert_eq!(editor.selections.ranges(cx), [3..3]);
-
-        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
-        editor.transpose(&Default::default(), cx);
-        assert_eq!(editor.text(cx), "acbd\ne");
-        assert_eq!(editor.selections.ranges(cx), [5..5]);
-
-        editor.transpose(&Default::default(), cx);
-        assert_eq!(editor.text(cx), "acbde\n");
-        assert_eq!(editor.selections.ranges(cx), [6..6]);
-
-        editor.transpose(&Default::default(), cx);
-        assert_eq!(editor.text(cx), "acbd\ne");
-        assert_eq!(editor.selections.ranges(cx), [6..6]);
-
-        editor
-    });
-
-    _ = cx.add_window(|cx| {
-        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
-
-        editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
-        editor.transpose(&Default::default(), cx);
-        assert_eq!(editor.text(cx), "bacd\ne");
-        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
-
-        editor.transpose(&Default::default(), cx);
-        assert_eq!(editor.text(cx), "bcade\n");
-        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
-
-        editor.transpose(&Default::default(), cx);
-        assert_eq!(editor.text(cx), "bcda\ne");
-        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
-
-        editor.transpose(&Default::default(), cx);
-        assert_eq!(editor.text(cx), "bcade\n");
-        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
-
-        editor.transpose(&Default::default(), cx);
-        assert_eq!(editor.text(cx), "bcaed\n");
-        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
-
-        editor
-    });
-
-    _ = cx.add_window(|cx| {
-        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
-
-        editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
-        editor.transpose(&Default::default(), cx);
-        assert_eq!(editor.text(cx), "🏀🍐✋");
-        assert_eq!(editor.selections.ranges(cx), [8..8]);
-
-        editor.transpose(&Default::default(), cx);
-        assert_eq!(editor.text(cx), "🏀✋🍐");
-        assert_eq!(editor.selections.ranges(cx), [11..11]);
-
-        editor.transpose(&Default::default(), cx);
-        assert_eq!(editor.text(cx), "🏀🍐✋");
-        assert_eq!(editor.selections.ranges(cx), [11..11]);
-
-        editor
-    });
-}
-
-#[gpui::test]
-async fn test_clipboard(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |_| {});
-
-    let mut cx = EditorTestContext::new(cx).await;
-
-    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
-    cx.update_editor(|e, cx| e.cut(&Cut, cx));
-    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
-
-    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
-    cx.set_state("two ˇfour ˇsix ˇ");
-    cx.update_editor(|e, cx| e.paste(&Paste, cx));
-    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
-
-    // Paste again but with only two cursors. Since the number of cursors doesn't
-    // match the number of slices in the clipboard, the entire clipboard text
-    // is pasted at each cursor.
-    cx.set_state("ˇtwo one✅ four three six five ˇ");
-    cx.update_editor(|e, cx| {
-        e.handle_input("( ", cx);
-        e.paste(&Paste, cx);
-        e.handle_input(") ", cx);
-    });
-    cx.assert_editor_state(
-        &([
-            "( one✅ ",
-            "three ",
-            "five ) ˇtwo one✅ four three six five ( one✅ ",
-            "three ",
-            "five ) ˇ",
-        ]
-        .join("\n")),
-    );
-
-    // Cut with three selections, one of which is full-line.
-    cx.set_state(indoc! {"
-        1«2ˇ»3
-        4ˇ567
-        «8ˇ»9"});
-    cx.update_editor(|e, cx| e.cut(&Cut, cx));
-    cx.assert_editor_state(indoc! {"
-        1ˇ3
-        ˇ9"});
-
-    // Paste with three selections, noticing how the copied selection that was full-line
-    // gets inserted before the second cursor.
-    cx.set_state(indoc! {"
-        1ˇ3
-        9ˇ
-        «oˇ»ne"});
-    cx.update_editor(|e, cx| e.paste(&Paste, cx));
-    cx.assert_editor_state(indoc! {"
-        12ˇ3
-        4567
-        9ˇ
-        8ˇne"});
-
-    // Copy with a single cursor only, which writes the whole line into the clipboard.
-    cx.set_state(indoc! {"
-        The quick brown
-        fox juˇmps over
-        the lazy dog"});
-    cx.update_editor(|e, cx| e.copy(&Copy, cx));
-    cx.cx.assert_clipboard_content(Some("fox jumps over\n"));
-
-    // Paste with three selections, noticing how the copied full-line selection is inserted
-    // before the empty selections but replaces the selection that is non-empty.
-    cx.set_state(indoc! {"
-        Tˇhe quick brown
-        «foˇ»x jumps over
-        tˇhe lazy dog"});
-    cx.update_editor(|e, cx| e.paste(&Paste, cx));
-    cx.assert_editor_state(indoc! {"
-        fox jumps over
-        Tˇhe quick brown
-        fox jumps over
-        ˇx jumps over
-        fox jumps over
-        tˇhe lazy dog"});
-}
-
-#[gpui::test]
-async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |_| {});
-
-    let mut cx = EditorTestContext::new(cx).await;
-    let language = Arc::new(Language::new(
-        LanguageConfig::default(),
-        Some(tree_sitter_rust::language()),
-    ));
-    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
-
-    // Cut an indented block, without the leading whitespace.
-    cx.set_state(indoc! {"
-        const a: B = (
-            c(),
-            «d(
-                e,
-                f
-            )ˇ»
-        );
-    "});
-    cx.update_editor(|e, cx| e.cut(&Cut, cx));
-    cx.assert_editor_state(indoc! {"
-        const a: B = (
-            c(),
-            ˇ
-        );
-    "});
-
-    // Paste it at the same position.
-    cx.update_editor(|e, cx| e.paste(&Paste, cx));
-    cx.assert_editor_state(indoc! {"
-        const a: B = (
-            c(),
-            d(
-                e,
-                f
-            )ˇ
-        );
-    "});
-
-    // Paste it at a line with a lower indent level.
-    cx.set_state(indoc! {"
-        ˇ
-        const a: B = (
-            c(),
-        );
-    "});
-    cx.update_editor(|e, cx| e.paste(&Paste, cx));
-    cx.assert_editor_state(indoc! {"
-        d(
-            e,
-            f
-        )ˇ
-        const a: B = (
-            c(),
-        );
-    "});
-
-    // Cut an indented block, with the leading whitespace.
-    cx.set_state(indoc! {"
-        const a: B = (
-            c(),
-        «    d(
-                e,
-                f
-            )
-        ˇ»);
-    "});
-    cx.update_editor(|e, cx| e.cut(&Cut, cx));
-    cx.assert_editor_state(indoc! {"
-        const a: B = (
-            c(),
-        ˇ);
-    "});
-
-    // Paste it at the same position.
-    cx.update_editor(|e, cx| e.paste(&Paste, cx));
-    cx.assert_editor_state(indoc! {"
-        const a: B = (
-            c(),
-            d(
-                e,
-                f
-            )
-        ˇ);
-    "});
-
-    // Paste it at a line with a higher indent level.
-    cx.set_state(indoc! {"
-        const a: B = (
-            c(),
-            d(
-                e,
-                fˇ
-            )
-        );
-    "});
-    cx.update_editor(|e, cx| e.paste(&Paste, cx));
-    cx.assert_editor_state(indoc! {"
-        const a: B = (
-            c(),
-            d(
-                e,
-                f    d(
-                    e,
-                    f
-                )
-        ˇ
-            )
-        );
-    "});
-}
-
-#[gpui::test]
-fn test_select_all(cx: &mut TestAppContext) {
-    init_test(cx, |_| {});
-
-    let view = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
-            build_editor(buffer, cx)
-        })
-        .root(cx);
-    view.update(cx, |view, cx| {
-        view.select_all(&SelectAll, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
-        );
-    });
-}
-
-#[gpui::test]
-fn test_select_line(cx: &mut TestAppContext) {
-    init_test(cx, |_| {});
-
-    let view = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
-            build_editor(buffer, cx)
-        })
-        .root(cx);
-    view.update(cx, |view, cx| {
-        view.change_selections(None, cx, |s| {
-            s.select_display_ranges([
-                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
-                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
-                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
-                DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
-            ])
-        });
-        view.select_line(&SelectLine, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            vec![
-                DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
-                DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
-            ]
-        );
-    });
-
-    view.update(cx, |view, cx| {
-        view.select_line(&SelectLine, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            vec![
-                DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
-                DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
-            ]
-        );
-    });
-
-    view.update(cx, |view, cx| {
-        view.select_line(&SelectLine, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
-        );
-    });
-}
-
-#[gpui::test]
-fn test_split_selection_into_lines(cx: &mut TestAppContext) {
-    init_test(cx, |_| {});
-
-    let view = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
-            build_editor(buffer, cx)
-        })
-        .root(cx);
-    view.update(cx, |view, cx| {
-        view.fold_ranges(
-            vec![
-                Point::new(0, 2)..Point::new(1, 2),
-                Point::new(2, 3)..Point::new(4, 1),
-                Point::new(7, 0)..Point::new(8, 4),
-            ],
-            true,
-            cx,
-        );
-        view.change_selections(None, cx, |s| {
-            s.select_display_ranges([
-                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
-                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
-                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
-                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
-            ])
-        });
-        assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
-    });
-
-    view.update(cx, |view, cx| {
-        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
-        assert_eq!(
-            view.display_text(cx),
-            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
-        );
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            [
-                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
-                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
-                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
-                DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4)
-            ]
-        );
-    });
-
-    view.update(cx, |view, cx| {
-        view.change_selections(None, cx, |s| {
-            s.select_display_ranges([DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)])
-        });
-        view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
-        assert_eq!(
-            view.display_text(cx),
-            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
-        );
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            [
-                DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5),
-                DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
-                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
-                DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
-                DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
-                DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
-                DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
-                DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
-            ]
-        );
-    });
-}
-
-#[gpui::test]
-fn test_add_selection_above_below(cx: &mut TestAppContext) {
-    init_test(cx, |_| {});
-
-    let view = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
-            build_editor(buffer, cx)
-        })
-        .root(cx);
-
-    view.update(cx, |view, cx| {
-        view.change_selections(None, cx, |s| {
-            s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)])
-        });
-    });
-    view.update(cx, |view, cx| {
-        view.add_selection_above(&AddSelectionAbove, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            vec![
-                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
-                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
-            ]
-        );
-    });
-
-    view.update(cx, |view, cx| {
-        view.add_selection_above(&AddSelectionAbove, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            vec![
-                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
-                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
-            ]
-        );
-    });
-
-    view.update(cx, |view, cx| {
-        view.add_selection_below(&AddSelectionBelow, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
-        );
-
-        view.undo_selection(&UndoSelection, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            vec![
-                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
-                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
-            ]
-        );
-
-        view.redo_selection(&RedoSelection, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
-        );
-    });
-
-    view.update(cx, |view, cx| {
-        view.add_selection_below(&AddSelectionBelow, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            vec![
-                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
-                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
-            ]
-        );
-    });
-
-    view.update(cx, |view, cx| {
-        view.add_selection_below(&AddSelectionBelow, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            vec![
-                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
-                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
-            ]
-        );
-    });
-
-    view.update(cx, |view, cx| {
-        view.change_selections(None, cx, |s| {
-            s.select_display_ranges([DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)])
-        });
-    });
-    view.update(cx, |view, cx| {
-        view.add_selection_below(&AddSelectionBelow, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            vec![
-                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
-                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
-            ]
-        );
-    });
-
-    view.update(cx, |view, cx| {
-        view.add_selection_below(&AddSelectionBelow, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            vec![
-                DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
-                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
-            ]
-        );
-    });
-
-    view.update(cx, |view, cx| {
-        view.add_selection_above(&AddSelectionAbove, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
-        );
-    });
-
-    view.update(cx, |view, cx| {
-        view.add_selection_above(&AddSelectionAbove, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
-        );
-    });
-
-    view.update(cx, |view, cx| {
-        view.change_selections(None, cx, |s| {
-            s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)])
-        });
-        view.add_selection_below(&AddSelectionBelow, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            vec![
-                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
-                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
-                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
-            ]
-        );
-    });
-
-    view.update(cx, |view, cx| {
-        view.add_selection_below(&AddSelectionBelow, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            vec![
-                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
-                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
-                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
-                DisplayPoint::new(4, 1)..DisplayPoint::new(4, 4),
-            ]
-        );
-    });
-
-    view.update(cx, |view, cx| {
-        view.add_selection_above(&AddSelectionAbove, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            vec![
-                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
-                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
-                DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
-            ]
-        );
-    });
-
-    view.update(cx, |view, cx| {
-        view.change_selections(None, cx, |s| {
-            s.select_display_ranges([DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)])
-        });
-    });
-    view.update(cx, |view, cx| {
-        view.add_selection_above(&AddSelectionAbove, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            vec![
-                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1),
-                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
-                DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
-                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
-            ]
-        );
-    });
-
-    view.update(cx, |view, cx| {
-        view.add_selection_below(&AddSelectionBelow, cx);
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            vec![
-                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
-                DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
-                DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
-            ]
-        );
-    });
-}
-
-#[gpui::test]
-async fn test_select_next(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |_| {});
-
-    let mut cx = EditorTestContext::new(cx).await;
-    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
-
-    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
-        .unwrap();
-    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
-
-    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
-        .unwrap();
-    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
-
-    cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
-    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
-
-    cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
-    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
-
-    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
-        .unwrap();
-    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
-
-    cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
-        .unwrap();
-    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
-}
-
-#[gpui::test]
-async fn test_select_previous(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |_| {});
-    {
-        // `Select previous` without a selection (selects wordwise)
-        let mut cx = EditorTestContext::new(cx).await;
-        cx.set_state("abc\nˇabc abc\ndefabc\nabc");
-
-        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
-            .unwrap();
-        cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
-
-        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
-            .unwrap();
-        cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
-
-        cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
-        cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
-
-        cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
-        cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
-
-        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
-            .unwrap();
-        cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
-
-        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
-            .unwrap();
-        cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
-    }
-    {
-        // `Select previous` with a selection
-        let mut cx = EditorTestContext::new(cx).await;
-        cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
-
-        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
-            .unwrap();
-        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
-
-        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
-            .unwrap();
-        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
-
-        cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
-        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
-
-        cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
-        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
-
-        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
-            .unwrap();
-        cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
-
-        cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
-            .unwrap();
-        cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
-    }
-}
-
-#[gpui::test]
-async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |_| {});
-
-    let language = Arc::new(Language::new(
-        LanguageConfig::default(),
-        Some(tree_sitter_rust::language()),
-    ));
-
-    let text = r#"
-        use mod1::mod2::{mod3, mod4};
-
-        fn fn_1(param1: bool, param2: &str) {
-            let var1 = "text";
-        }
-    "#
-    .unindent();
-
-    let buffer =
-        cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, text).with_language(language, cx));
-    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
-    let view = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
-    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
-        .await;
-
-    view.update(cx, |view, cx| {
-        view.change_selections(None, cx, |s| {
-            s.select_display_ranges([
-                DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
-                DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
-                DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
-            ]);
-        });
-        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
-    });
-    assert_eq!(
-        view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
-        &[
-            DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
-            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
-            DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
-        ]
-    );
-
-    view.update(cx, |view, cx| {
-        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
-    });
-    assert_eq!(
-        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
-        &[
-            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
-            DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
-        ]
-    );
-
-    view.update(cx, |view, cx| {
-        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
-    });
-    assert_eq!(
-        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
-        &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
-    );
-
-    // Trying to expand the selected syntax node one more time has no effect.
-    view.update(cx, |view, cx| {
-        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
-    });
-    assert_eq!(
-        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
-        &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
-    );
-
-    view.update(cx, |view, cx| {
-        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
-    });
-    assert_eq!(
-        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
-        &[
-            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
-            DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
-        ]
-    );
-
-    view.update(cx, |view, cx| {
-        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
-    });
-    assert_eq!(
-        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
-        &[
-            DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
-            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
-            DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
-        ]
-    );
-
-    view.update(cx, |view, cx| {
-        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
-    });
-    assert_eq!(
-        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
-        &[
-            DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
-            DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
-            DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
-        ]
-    );
-
-    // Trying to shrink the selected syntax node one more time has no effect.
-    view.update(cx, |view, cx| {
-        view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
-    });
-    assert_eq!(
-        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
-        &[
-            DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
-            DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
-            DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
-        ]
-    );
-
-    // Ensure that we keep expanding the selection if the larger selection starts or ends within
-    // a fold.
-    view.update(cx, |view, cx| {
-        view.fold_ranges(
-            vec![
-                Point::new(0, 21)..Point::new(0, 24),
-                Point::new(3, 20)..Point::new(3, 22),
-            ],
-            true,
-            cx,
-        );
-        view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
-    });
-    assert_eq!(
-        view.update(cx, |view, cx| view.selections.display_ranges(cx)),
-        &[
-            DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
-            DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
-            DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
-        ]
-    );
-}
-
-#[gpui::test]
-async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |_| {});
-
-    let language = Arc::new(
-        Language::new(
-            LanguageConfig {
-                brackets: BracketPairConfig {
-                    pairs: vec![
-                        BracketPair {
-                            start: "{".to_string(),
-                            end: "}".to_string(),
-                            close: false,
-                            newline: true,
-                        },
-                        BracketPair {
-                            start: "(".to_string(),
-                            end: ")".to_string(),
-                            close: false,
-                            newline: true,
-                        },
-                    ],
-                    ..Default::default()
-                },
-                ..Default::default()
-            },
-            Some(tree_sitter_rust::language()),
-        )
-        .with_indents_query(
-            r#"
-                (_ "(" ")" @end) @indent
-                (_ "{" "}" @end) @indent
-            "#,
-        )
-        .unwrap(),
-    );
-
-    let text = "fn a() {}";
-
-    let buffer =
-        cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, text).with_language(language, cx));
-    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
-    let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
-    editor
-        .condition(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
-        .await;
-
-    editor.update(cx, |editor, cx| {
-        editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
-        editor.newline(&Newline, cx);
-        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
-        assert_eq!(
-            editor.selections.ranges(cx),
-            &[
-                Point::new(1, 4)..Point::new(1, 4),
-                Point::new(3, 4)..Point::new(3, 4),
-                Point::new(5, 0)..Point::new(5, 0)
-            ]
-        );
-    });
-}
-
-#[gpui::test]
-async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |_| {});
-
-    let mut cx = EditorTestContext::new(cx).await;
-
-    let language = Arc::new(Language::new(
-        LanguageConfig {
-            brackets: BracketPairConfig {
-                pairs: vec![
-                    BracketPair {
-                        start: "{".to_string(),
-                        end: "}".to_string(),
-                        close: true,
-                        newline: true,
-                    },
-                    BracketPair {
-                        start: "(".to_string(),
-                        end: ")".to_string(),
-                        close: true,
-                        newline: true,
-                    },
-                    BracketPair {
-                        start: "/*".to_string(),
-                        end: " */".to_string(),
-                        close: true,
-                        newline: true,
-                    },
-                    BracketPair {
-                        start: "[".to_string(),
-                        end: "]".to_string(),
-                        close: false,
-                        newline: true,
-                    },
-                    BracketPair {
-                        start: "\"".to_string(),
-                        end: "\"".to_string(),
-                        close: true,
-                        newline: false,
-                    },
-                ],
-                ..Default::default()
-            },
-            autoclose_before: "})]".to_string(),
-            ..Default::default()
-        },
-        Some(tree_sitter_rust::language()),
-    ));
-
-    let registry = Arc::new(LanguageRegistry::test());
-    registry.add(language.clone());
-    cx.update_buffer(|buffer, cx| {
-        buffer.set_language_registry(registry);
-        buffer.set_language(Some(language), cx);
-    });
-
-    cx.set_state(
-        &r#"
-            🏀ˇ
-            εˇ
-            ❤️ˇ
-        "#
-        .unindent(),
-    );
-
-    // autoclose multiple nested brackets at multiple cursors
-    cx.update_editor(|view, cx| {
-        view.handle_input("{", cx);
-        view.handle_input("{", cx);
-        view.handle_input("{", cx);
-    });
-    cx.assert_editor_state(
-        &"
-            🏀{{{ˇ}}}
-            ε{{{ˇ}}}
-            ❤️{{{ˇ}}}
-        "
-        .unindent(),
-    );
-
-    // insert a different closing bracket
-    cx.update_editor(|view, cx| {
-        view.handle_input(")", cx);
-    });
-    cx.assert_editor_state(
-        &"
-            🏀{{{)ˇ}}}
-            ε{{{)ˇ}}}
-            ❤️{{{)ˇ}}}
-        "
-        .unindent(),
-    );
-
-    // skip over the auto-closed brackets when typing a closing bracket
-    cx.update_editor(|view, cx| {
-        view.move_right(&MoveRight, cx);
-        view.handle_input("}", cx);
-        view.handle_input("}", cx);
-        view.handle_input("}", cx);
-    });
-    cx.assert_editor_state(
-        &"
-            🏀{{{)}}}}ˇ
-            ε{{{)}}}}ˇ
-            ❤️{{{)}}}}ˇ
-        "
-        .unindent(),
-    );
-
-    // autoclose multi-character pairs
-    cx.set_state(
-        &"
-            ˇ
-            ˇ
-        "
-        .unindent(),
-    );
-    cx.update_editor(|view, cx| {
-        view.handle_input("/", cx);
-        view.handle_input("*", cx);
-    });
-    cx.assert_editor_state(
-        &"
-            /*ˇ */
-            /*ˇ */
-        "
-        .unindent(),
-    );
-
-    // one cursor autocloses a multi-character pair, one cursor
-    // does not autoclose.
-    cx.set_state(
-        &"
-            /ˇ
-            ˇ
-        "
-        .unindent(),
-    );
-    cx.update_editor(|view, cx| view.handle_input("*", cx));
-    cx.assert_editor_state(
-        &"
-            /*ˇ */
-            *ˇ
-        "
-        .unindent(),
-    );
-
-    // Don't autoclose if the next character isn't whitespace and isn't
-    // listed in the language's "autoclose_before" section.
-    cx.set_state("ˇa b");
-    cx.update_editor(|view, cx| view.handle_input("{", cx));
-    cx.assert_editor_state("{ˇa b");
-
-    // Don't autoclose if `close` is false for the bracket pair
-    cx.set_state("ˇ");
-    cx.update_editor(|view, cx| view.handle_input("[", cx));
-    cx.assert_editor_state("[ˇ");
-
-    // Surround with brackets if text is selected
-    cx.set_state("«aˇ» b");
-    cx.update_editor(|view, cx| view.handle_input("{", cx));
-    cx.assert_editor_state("{«aˇ»} b");
-
-    // Autclose pair where the start and end characters are the same
-    cx.set_state("aˇ");
-    cx.update_editor(|view, cx| view.handle_input("\"", cx));
-    cx.assert_editor_state("a\"ˇ\"");
-    cx.update_editor(|view, cx| view.handle_input("\"", cx));
-    cx.assert_editor_state("a\"\"ˇ");
-}
-
-#[gpui::test]
-async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |_| {});
-
-    let mut cx = EditorTestContext::new(cx).await;
-
-    let html_language = Arc::new(
-        Language::new(
-            LanguageConfig {
-                name: "HTML".into(),
-                brackets: BracketPairConfig {
-                    pairs: vec![
-                        BracketPair {
-                            start: "<".into(),
-                            end: ">".into(),
-                            close: true,
-                            ..Default::default()
-                        },
-                        BracketPair {
-                            start: "{".into(),
-                            end: "}".into(),
-                            close: true,
-                            ..Default::default()
-                        },
-                        BracketPair {
-                            start: "(".into(),
-                            end: ")".into(),
-                            close: true,
-                            ..Default::default()
-                        },
-                    ],
-                    ..Default::default()
-                },
-                autoclose_before: "})]>".into(),
-                ..Default::default()
-            },
-            Some(tree_sitter_html::language()),
-        )
-        .with_injection_query(
-            r#"
-            (script_element
-                (raw_text) @content
-                (#set! "language" "javascript"))
-            "#,
-        )
-        .unwrap(),
-    );
-
-    let javascript_language = Arc::new(Language::new(
-        LanguageConfig {
-            name: "JavaScript".into(),
-            brackets: BracketPairConfig {
-                pairs: vec![
-                    BracketPair {
-                        start: "/*".into(),
-                        end: " */".into(),
-                        close: true,
-                        ..Default::default()
-                    },
-                    BracketPair {
-                        start: "{".into(),
-                        end: "}".into(),
-                        close: true,
-                        ..Default::default()
-                    },
-                    BracketPair {
-                        start: "(".into(),
-                        end: ")".into(),
-                        close: true,
-                        ..Default::default()
-                    },
-                ],
-                ..Default::default()
-            },
-            autoclose_before: "})]>".into(),
-            ..Default::default()
-        },
-        Some(tree_sitter_typescript::language_tsx()),
-    ));
-
-    let registry = Arc::new(LanguageRegistry::test());
-    registry.add(html_language.clone());
-    registry.add(javascript_language.clone());
-
-    cx.update_buffer(|buffer, cx| {
-        buffer.set_language_registry(registry);
-        buffer.set_language(Some(html_language), cx);
-    });
-
-    cx.set_state(
-        &r#"
-            <body>ˇ
-                <script>
-                    var x = 1;ˇ
-                </script>
-            </body>ˇ
-        "#
-        .unindent(),
-    );
-
-    // Precondition: different languages are active at different locations.
-    cx.update_editor(|editor, cx| {
-        let snapshot = editor.snapshot(cx);
-        let cursors = editor.selections.ranges::<usize>(cx);
-        let languages = cursors
-            .iter()
-            .map(|c| snapshot.language_at(c.start).unwrap().name())
-            .collect::<Vec<_>>();
-        assert_eq!(
-            languages,
-            &["HTML".into(), "JavaScript".into(), "HTML".into()]
-        );
-    });
-
-    // Angle brackets autoclose in HTML, but not JavaScript.
-    cx.update_editor(|editor, cx| {
-        editor.handle_input("<", cx);
-        editor.handle_input("a", cx);
-    });
-    cx.assert_editor_state(
-        &r#"
-            <body><aˇ>
-                <script>
-                    var x = 1;<aˇ
-                </script>
-            </body><aˇ>
-        "#
-        .unindent(),
-    );
-
-    // Curly braces and parens autoclose in both HTML and JavaScript.
-    cx.update_editor(|editor, cx| {
-        editor.handle_input(" b=", cx);
-        editor.handle_input("{", cx);
-        editor.handle_input("c", cx);
-        editor.handle_input("(", cx);
-    });
-    cx.assert_editor_state(
-        &r#"
-            <body><a b={c(ˇ)}>
-                <script>
-                    var x = 1;<a b={c(ˇ)}
-                </script>
-            </body><a b={c(ˇ)}>
-        "#
-        .unindent(),
-    );
-
-    // Brackets that were already autoclosed are skipped.
-    cx.update_editor(|editor, cx| {
-        editor.handle_input(")", cx);
-        editor.handle_input("d", cx);
-        editor.handle_input("}", cx);
-    });
-    cx.assert_editor_state(
-        &r#"
-            <body><a b={c()d}ˇ>
-                <script>
-                    var x = 1;<a b={c()d}ˇ
-                </script>
-            </body><a b={c()d}ˇ>
-        "#
-        .unindent(),
-    );
-    cx.update_editor(|editor, cx| {
-        editor.handle_input(">", cx);
-    });
-    cx.assert_editor_state(
-        &r#"
-            <body><a b={c()d}>ˇ
-                <script>
-                    var x = 1;<a b={c()d}>ˇ
-                </script>
-            </body><a b={c()d}>ˇ
-        "#
-        .unindent(),
-    );
-
-    // Reset
-    cx.set_state(
-        &r#"
-            <body>ˇ
-                <script>
-                    var x = 1;ˇ
-                </script>
-            </body>ˇ
-        "#
-        .unindent(),
-    );
-
-    cx.update_editor(|editor, cx| {
-        editor.handle_input("<", cx);
-    });
-    cx.assert_editor_state(
-        &r#"
-            <body><ˇ>
-                <script>
-                    var x = 1;<ˇ
-                </script>
-            </body><ˇ>
-        "#
-        .unindent(),
-    );
-
-    // When backspacing, the closing angle brackets are removed.
-    cx.update_editor(|editor, cx| {
-        editor.backspace(&Backspace, cx);
-    });
-    cx.assert_editor_state(
-        &r#"
-            <body>ˇ
-                <script>
-                    var x = 1;ˇ
-                </script>
-            </body>ˇ
-        "#
-        .unindent(),
-    );
-
-    // Block comments autoclose in JavaScript, but not HTML.
-    cx.update_editor(|editor, cx| {
-        editor.handle_input("/", cx);
-        editor.handle_input("*", cx);
-    });
-    cx.assert_editor_state(
-        &r#"
-            <body>/*ˇ
-                <script>
-                    var x = 1;/*ˇ */
-                </script>
-            </body>/*ˇ
-        "#
-        .unindent(),
-    );
-}
-
-#[gpui::test]
-async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |_| {});
-
-    let mut cx = EditorTestContext::new(cx).await;
-
-    let rust_language = Arc::new(
-        Language::new(
-            LanguageConfig {
-                name: "Rust".into(),
-                brackets: serde_json::from_value(json!([
-                    { "start": "{", "end": "}", "close": true, "newline": true },
-                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
-                ]))
-                .unwrap(),
-                autoclose_before: "})]>".into(),
-                ..Default::default()
-            },
-            Some(tree_sitter_rust::language()),
-        )
-        .with_override_query("(string_literal) @string")
-        .unwrap(),
-    );
-
-    let registry = Arc::new(LanguageRegistry::test());
-    registry.add(rust_language.clone());
-
-    cx.update_buffer(|buffer, cx| {
-        buffer.set_language_registry(registry);
-        buffer.set_language(Some(rust_language), cx);
-    });
-
-    cx.set_state(
-        &r#"
-            let x = ˇ
-        "#
-        .unindent(),
-    );
-
-    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
-    cx.update_editor(|editor, cx| {
-        editor.handle_input("\"", cx);
-    });
-    cx.assert_editor_state(
-        &r#"
-            let x = "ˇ"
-        "#
-        .unindent(),
-    );
-
-    // Inserting another quotation mark. The cursor moves across the existing
-    // automatically-inserted quotation mark.
-    cx.update_editor(|editor, cx| {
-        editor.handle_input("\"", cx);
-    });
-    cx.assert_editor_state(
-        &r#"
-            let x = ""ˇ
-        "#
-        .unindent(),
-    );
-
-    // Reset
-    cx.set_state(
-        &r#"
-            let x = ˇ
-        "#
-        .unindent(),
-    );
-
-    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
-    cx.update_editor(|editor, cx| {
-        editor.handle_input("\"", cx);
-        editor.handle_input(" ", cx);
-        editor.move_left(&Default::default(), cx);
-        editor.handle_input("\\", cx);
-        editor.handle_input("\"", cx);
-    });
-    cx.assert_editor_state(
-        &r#"
-            let x = "\"ˇ "
-        "#
-        .unindent(),
-    );
-
-    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
-    // mark. Nothing is inserted.
-    cx.update_editor(|editor, cx| {
-        editor.move_right(&Default::default(), cx);
-        editor.handle_input("\"", cx);
-    });
-    cx.assert_editor_state(
-        &r#"
-            let x = "\" "ˇ
-        "#
-        .unindent(),
-    );
-}
-
-#[gpui::test]
-async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |_| {});
-
-    let language = Arc::new(Language::new(
-        LanguageConfig {
-            brackets: BracketPairConfig {
-                pairs: vec![
-                    BracketPair {
-                        start: "{".to_string(),
-                        end: "}".to_string(),
-                        close: true,
-                        newline: true,
-                    },
-                    BracketPair {
-                        start: "/* ".to_string(),
-                        end: "*/".to_string(),
-                        close: true,
-                        ..Default::default()
-                    },
-                ],
-                ..Default::default()
-            },
-            ..Default::default()
-        },
-        Some(tree_sitter_rust::language()),
-    ));
-
-    let text = r#"
-        a
-        b
-        c
-    "#
-    .unindent();
-
-    let buffer =
-        cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, text).with_language(language, cx));
-    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
-    let view = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
-    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
-        .await;
-
-    view.update(cx, |view, cx| {
-        view.change_selections(None, cx, |s| {
-            s.select_display_ranges([
-                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
-                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
-                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
-            ])
-        });
-
-        view.handle_input("{", cx);
-        view.handle_input("{", cx);
-        view.handle_input("{", cx);
-        assert_eq!(
-            view.text(cx),
-            "
-                {{{a}}}
-                {{{b}}}
-                {{{c}}}
-            "
-            .unindent()
-        );
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            [
-                DisplayPoint::new(0, 3)..DisplayPoint::new(0, 4),
-                DisplayPoint::new(1, 3)..DisplayPoint::new(1, 4),
-                DisplayPoint::new(2, 3)..DisplayPoint::new(2, 4)
-            ]
-        );
-
-        view.undo(&Undo, cx);
-        view.undo(&Undo, cx);
-        view.undo(&Undo, cx);
-        assert_eq!(
-            view.text(cx),
-            "
-                a
-                b
-                c
-            "
-            .unindent()
-        );
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            [
-                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
-                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
-                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
-            ]
-        );
-
-        // Ensure inserting the first character of a multi-byte bracket pair
-        // doesn't surround the selections with the bracket.
-        view.handle_input("/", cx);
-        assert_eq!(
-            view.text(cx),
-            "
-                /
-                /
-                /
-            "
-            .unindent()
-        );
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            [
-                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
-                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
-                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1)
-            ]
-        );
-
-        view.undo(&Undo, cx);
-        assert_eq!(
-            view.text(cx),
-            "
-                a
-                b
-                c
-            "
-            .unindent()
-        );
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            [
-                DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
-                DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
-                DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
-            ]
-        );
-
-        // Ensure inserting the last character of a multi-byte bracket pair
-        // doesn't surround the selections with the bracket.
-        view.handle_input("*", cx);
-        assert_eq!(
-            view.text(cx),
-            "
-                *
-                *
-                *
-            "
-            .unindent()
-        );
-        assert_eq!(
-            view.selections.display_ranges(cx),
-            [
-                DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
-                DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
-                DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1)
-            ]
-        );
-    });
-}
-
-#[gpui::test]
-async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |_| {});
-
-    let language = Arc::new(Language::new(
-        LanguageConfig {
-            brackets: BracketPairConfig {
-                pairs: vec![BracketPair {
-                    start: "{".to_string(),
-                    end: "}".to_string(),
-                    close: true,
-                    newline: true,
-                }],
-                ..Default::default()
-            },
-            autoclose_before: "}".to_string(),
-            ..Default::default()
-        },
-        Some(tree_sitter_rust::language()),
-    ));
-
-    let text = r#"
-        a
-        b
-        c
-    "#
-    .unindent();
-
-    let buffer =
-        cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, text).with_language(language, cx));
-    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
-    let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
-    editor
-        .condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
-        .await;
-
-    editor.update(cx, |editor, cx| {
-        editor.change_selections(None, cx, |s| {
-            s.select_ranges([
-                Point::new(0, 1)..Point::new(0, 1),
-                Point::new(1, 1)..Point::new(1, 1),
-                Point::new(2, 1)..Point::new(2, 1),
-            ])
-        });
-
-        editor.handle_input("{", cx);
-        editor.handle_input("{", cx);
-        editor.handle_input("_", cx);
-        assert_eq!(
-            editor.text(cx),
-            "
-                a{{_}}
-                b{{_}}
-                c{{_}}
-            "
-            .unindent()
-        );
-        assert_eq!(
-            editor.selections.ranges::<Point>(cx),
-            [
-                Point::new(0, 4)..Point::new(0, 4),
-                Point::new(1, 4)..Point::new(1, 4),
-                Point::new(2, 4)..Point::new(2, 4)
-            ]
-        );
-
-        editor.backspace(&Default::default(), cx);
-        editor.backspace(&Default::default(), cx);
-        assert_eq!(
-            editor.text(cx),
-            "
-                a{}
-                b{}
-                c{}
-            "
-            .unindent()
-        );
-        assert_eq!(
-            editor.selections.ranges::<Point>(cx),
-            [
-                Point::new(0, 2)..Point::new(0, 2),
-                Point::new(1, 2)..Point::new(1, 2),
-                Point::new(2, 2)..Point::new(2, 2)
-            ]
-        );
-
-        editor.delete_to_previous_word_start(&Default::default(), cx);
-        assert_eq!(
-            editor.text(cx),
-            "
-                a
-                b
-                c
-            "
-            .unindent()
-        );
-        assert_eq!(
-            editor.selections.ranges::<Point>(cx),
-            [
-                Point::new(0, 1)..Point::new(0, 1),
-                Point::new(1, 1)..Point::new(1, 1),
-                Point::new(2, 1)..Point::new(2, 1)
-            ]
-        );
-    });
-}
-
-#[gpui::test]
-async fn test_snippets(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |_| {});
-
-    let (text, insertion_ranges) = marked_text_ranges(
-        indoc! {"
-            a.ˇ b
-            a.ˇ b
-            a.ˇ b
-        "},
-        false,
-    );
-
-    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
-    let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
-
-    editor.update(cx, |editor, cx| {
-        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
-
-        editor
-            .insert_snippet(&insertion_ranges, snippet, cx)
-            .unwrap();
-
-        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
-            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
-            assert_eq!(editor.text(cx), expected_text);
-            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
-        }
-
-        assert(
-            editor,
-            cx,
-            indoc! {"
-                a.f(«one», two, «three») b
-                a.f(«one», two, «three») b
-                a.f(«one», two, «three») b
-            "},
-        );
-
-        // Can't move earlier than the first tab stop
-        assert!(!editor.move_to_prev_snippet_tabstop(cx));
-        assert(
-            editor,
-            cx,
-            indoc! {"
-                a.f(«one», two, «three») b
-                a.f(«one», two, «three») b
-                a.f(«one», two, «three») b
-            "},
-        );
-
-        assert!(editor.move_to_next_snippet_tabstop(cx));
-        assert(
-            editor,
-            cx,
-            indoc! {"
-                a.f(one, «two», three) b
-                a.f(one, «two», three) b
-                a.f(one, «two», three) b
-            "},
-        );
-
-        editor.move_to_prev_snippet_tabstop(cx);
-        assert(
-            editor,
-            cx,
-            indoc! {"
-                a.f(«one», two, «three») b
-                a.f(«one», two, «three») b
-                a.f(«one», two, «three») b
-            "},
-        );
-
-        assert!(editor.move_to_next_snippet_tabstop(cx));
-        assert(
-            editor,
-            cx,
-            indoc! {"
-                a.f(one, «two», three) b
-                a.f(one, «two», three) b
-                a.f(one, «two», three) b
-            "},
-        );
-        assert!(editor.move_to_next_snippet_tabstop(cx));
-        assert(
-            editor,
-            cx,
-            indoc! {"
-                a.f(one, two, three)ˇ b
-                a.f(one, two, three)ˇ b
-                a.f(one, two, three)ˇ b
-            "},
-        );
-
-        // As soon as the last tab stop is reached, snippet state is gone
-        editor.move_to_prev_snippet_tabstop(cx);
-        assert(
-            editor,
-            cx,
-            indoc! {"
-                a.f(one, two, three)ˇ b
-                a.f(one, two, three)ˇ b
-                a.f(one, two, three)ˇ b
-            "},
-        );
-    });
-}
-
-#[gpui::test]
-async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |_| {});
-
-    let mut language = Language::new(
-        LanguageConfig {
-            name: "Rust".into(),
-            path_suffixes: vec!["rs".to_string()],
-            ..Default::default()
-        },
-        Some(tree_sitter_rust::language()),
-    );
-    let mut fake_servers = language
-        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
-            capabilities: lsp::ServerCapabilities {
-                document_formatting_provider: Some(lsp::OneOf::Left(true)),
-                ..Default::default()
-            },
-            ..Default::default()
-        }))
-        .await;
-
-    let fs = FakeFs::new(cx.background());
-    fs.insert_file("/file.rs", Default::default()).await;
-
-    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
-    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
-    let buffer = project
-        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
-        .await
-        .unwrap();
-
-    cx.foreground().start_waiting();
-    let fake_server = fake_servers.next().await.unwrap();
-
-    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
-    let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
-    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
-    assert!(cx.read(|cx| editor.is_dirty(cx)));
-
-    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
-    fake_server
-        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
-            assert_eq!(
-                params.text_document.uri,
-                lsp::Url::from_file_path("/file.rs").unwrap()
-            );
-            assert_eq!(params.options.tab_size, 4);
-            Ok(Some(vec![lsp::TextEdit::new(
-                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
-                ", ".to_string(),
-            )]))
-        })
-        .next()
-        .await;
-    cx.foreground().start_waiting();
-    save.await.unwrap();
-    assert_eq!(
-        editor.read_with(cx, |editor, cx| editor.text(cx)),
-        "one, two\nthree\n"
-    );
-    assert!(!cx.read(|cx| editor.is_dirty(cx)));
-
-    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
-    assert!(cx.read(|cx| editor.is_dirty(cx)));
-
-    // Ensure we can still save even if formatting hangs.
-    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
-        assert_eq!(
-            params.text_document.uri,
-            lsp::Url::from_file_path("/file.rs").unwrap()
-        );
-        futures::future::pending::<()>().await;
-        unreachable!()
-    });
-    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
-    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
-    cx.foreground().start_waiting();
-    save.await.unwrap();
-    assert_eq!(
-        editor.read_with(cx, |editor, cx| editor.text(cx)),
-        "one\ntwo\nthree\n"
-    );
-    assert!(!cx.read(|cx| editor.is_dirty(cx)));
-
-    // Set rust language override and assert overridden tabsize is sent to language server
-    update_test_language_settings(cx, |settings| {
-        settings.languages.insert(
-            "Rust".into(),
-            LanguageSettingsContent {
-                tab_size: NonZeroU32::new(8),
-                ..Default::default()
-            },
-        );
-    });
-
-    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
-    fake_server
-        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
-            assert_eq!(
-                params.text_document.uri,
-                lsp::Url::from_file_path("/file.rs").unwrap()
-            );
-            assert_eq!(params.options.tab_size, 8);
-            Ok(Some(vec![]))
-        })
-        .next()
-        .await;
-    cx.foreground().start_waiting();
-    save.await.unwrap();
-}
-
-#[gpui::test]
-async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |_| {});
-
-    let mut language = Language::new(
-        LanguageConfig {
-            name: "Rust".into(),
-            path_suffixes: vec!["rs".to_string()],
-            ..Default::default()
-        },
-        Some(tree_sitter_rust::language()),
-    );
-    let mut fake_servers = language
-        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
-            capabilities: lsp::ServerCapabilities {
-                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
-                ..Default::default()
-            },
-            ..Default::default()
-        }))
-        .await;
-
-    let fs = FakeFs::new(cx.background());
-    fs.insert_file("/file.rs", Default::default()).await;
-
-    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
-    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
-    let buffer = project
-        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
-        .await
-        .unwrap();
-
-    cx.foreground().start_waiting();
-    let fake_server = fake_servers.next().await.unwrap();
-
-    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
-    let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
-    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
-    assert!(cx.read(|cx| editor.is_dirty(cx)));
-
-    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
-    fake_server
-        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
-            assert_eq!(
-                params.text_document.uri,
-                lsp::Url::from_file_path("/file.rs").unwrap()
-            );
-            assert_eq!(params.options.tab_size, 4);
-            Ok(Some(vec![lsp::TextEdit::new(
-                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
-                ", ".to_string(),
-            )]))
-        })
-        .next()
-        .await;
-    cx.foreground().start_waiting();
-    save.await.unwrap();
-    assert_eq!(
-        editor.read_with(cx, |editor, cx| editor.text(cx)),
-        "one, two\nthree\n"
-    );
-    assert!(!cx.read(|cx| editor.is_dirty(cx)));
-
-    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
-    assert!(cx.read(|cx| editor.is_dirty(cx)));
-
-    // Ensure we can still save even if formatting hangs.
-    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
-        move |params, _| async move {
-            assert_eq!(
-                params.text_document.uri,
-                lsp::Url::from_file_path("/file.rs").unwrap()
-            );
-            futures::future::pending::<()>().await;
-            unreachable!()
-        },
-    );
-    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
-    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
-    cx.foreground().start_waiting();
-    save.await.unwrap();
-    assert_eq!(
-        editor.read_with(cx, |editor, cx| editor.text(cx)),
-        "one\ntwo\nthree\n"
-    );
-    assert!(!cx.read(|cx| editor.is_dirty(cx)));
-
-    // Set rust language override and assert overridden tabsize is sent to language server
-    update_test_language_settings(cx, |settings| {
-        settings.languages.insert(
-            "Rust".into(),
-            LanguageSettingsContent {
-                tab_size: NonZeroU32::new(8),
-                ..Default::default()
-            },
-        );
-    });
-
-    let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
-    fake_server
-        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
-            assert_eq!(
-                params.text_document.uri,
-                lsp::Url::from_file_path("/file.rs").unwrap()
-            );
-            assert_eq!(params.options.tab_size, 8);
-            Ok(Some(vec![]))
-        })
-        .next()
-        .await;
-    cx.foreground().start_waiting();
-    save.await.unwrap();
-}
-
-#[gpui::test]
-async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |settings| {
-        settings.defaults.formatter = Some(language_settings::Formatter::LanguageServer)
-    });
-
-    let mut language = Language::new(
-        LanguageConfig {
-            name: "Rust".into(),
-            path_suffixes: vec!["rs".to_string()],
-            // Enable Prettier formatting for the same buffer, and ensure
-            // LSP is called instead of Prettier.
-            prettier_parser_name: Some("test_parser".to_string()),
-            ..Default::default()
-        },
-        Some(tree_sitter_rust::language()),
-    );
-    let mut fake_servers = language
-        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
-            capabilities: lsp::ServerCapabilities {
-                document_formatting_provider: Some(lsp::OneOf::Left(true)),
-                ..Default::default()
-            },
-            ..Default::default()
-        }))
-        .await;
-
-    let fs = FakeFs::new(cx.background());
-    fs.insert_file("/file.rs", Default::default()).await;
-
-    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
-    project.update(cx, |project, _| {
-        project.languages().add(Arc::new(language));
-    });
-    let buffer = project
-        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
-        .await
-        .unwrap();
-
-    cx.foreground().start_waiting();
-    let fake_server = fake_servers.next().await.unwrap();
-
-    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
-    let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
-    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
-
-    let format = editor.update(cx, |editor, cx| {
-        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
-    });
-    fake_server
-        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
-            assert_eq!(
-                params.text_document.uri,
-                lsp::Url::from_file_path("/file.rs").unwrap()
-            );
-            assert_eq!(params.options.tab_size, 4);
-            Ok(Some(vec![lsp::TextEdit::new(
-                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
-                ", ".to_string(),
-            )]))
-        })
-        .next()
-        .await;
-    cx.foreground().start_waiting();
-    format.await.unwrap();
-    assert_eq!(
-        editor.read_with(cx, |editor, cx| editor.text(cx)),
-        "one, two\nthree\n"
-    );
-
-    editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
-    // Ensure we don't lock if formatting hangs.
-    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
-        assert_eq!(
-            params.text_document.uri,
-            lsp::Url::from_file_path("/file.rs").unwrap()
-        );
-        futures::future::pending::<()>().await;
-        unreachable!()
-    });
-    let format = editor.update(cx, |editor, cx| {
-        editor.perform_format(project, FormatTrigger::Manual, cx)
-    });
-    cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
-    cx.foreground().start_waiting();
-    format.await.unwrap();
-    assert_eq!(
-        editor.read_with(cx, |editor, cx| editor.text(cx)),
-        "one\ntwo\nthree\n"
-    );
-}
-
-#[gpui::test]
-async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |_| {});
-
-    let mut cx = EditorLspTestContext::new_rust(
-        lsp::ServerCapabilities {
-            document_formatting_provider: Some(lsp::OneOf::Left(true)),
-            ..Default::default()
-        },
-        cx,
-    )
-    .await;
-
-    cx.set_state(indoc! {"
-        one.twoˇ
-    "});
-
-    // The format request takes a long time. When it completes, it inserts
-    // a newline and an indent before the `.`
-    cx.lsp
-        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
-            let executor = cx.background();
-            async move {
-                executor.timer(Duration::from_millis(100)).await;
-                Ok(Some(vec![lsp::TextEdit {
-                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
-                    new_text: "\n    ".into(),
-                }]))
-            }
-        });
-
-    // Submit a format request.
-    let format_1 = cx
-        .update_editor(|editor, cx| editor.format(&Format, cx))
-        .unwrap();
-    cx.foreground().run_until_parked();
-
-    // Submit a second format request.
-    let format_2 = cx
-        .update_editor(|editor, cx| editor.format(&Format, cx))
-        .unwrap();
-    cx.foreground().run_until_parked();
-
-    // Wait for both format requests to complete
-    cx.foreground().advance_clock(Duration::from_millis(200));
-    cx.foreground().start_waiting();
-    format_1.await.unwrap();
-    cx.foreground().start_waiting();
-    format_2.await.unwrap();
-
-    // The formatting edits only happens once.
-    cx.assert_editor_state(indoc! {"
-        one
-            .twoˇ
-    "});
-}
-
-#[gpui::test]
-async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |settings| {
-        settings.defaults.formatter = Some(language_settings::Formatter::Auto)
-    });
-
-    let mut cx = EditorLspTestContext::new_rust(
-        lsp::ServerCapabilities {
-            document_formatting_provider: Some(lsp::OneOf::Left(true)),
-            ..Default::default()
-        },
-        cx,
-    )
-    .await;
-
-    // Set up a buffer white some trailing whitespace and no trailing newline.
-    cx.set_state(
-        &[
-            "one ",   //
-            "twoˇ",   //
-            "three ", //
-            "four",   //
-        ]
-        .join("\n"),
-    );
-
-    // Submit a format request.
-    let format = cx
-        .update_editor(|editor, cx| editor.format(&Format, cx))
-        .unwrap();
-
-    // Record which buffer changes have been sent to the language server
-    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
-    cx.lsp
-        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
-            let buffer_changes = buffer_changes.clone();
-            move |params, _| {
-                buffer_changes.lock().extend(
-                    params
-                        .content_changes
-                        .into_iter()
-                        .map(|e| (e.range.unwrap(), e.text)),
-                );
-            }
-        });
-
-    // Handle formatting requests to the language server.
-    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
-        let buffer_changes = buffer_changes.clone();
-        move |_, _| {
-            // When formatting is requested, trailing whitespace has already been stripped,
-            // and the trailing newline has already been added.
-            assert_eq!(
-                &buffer_changes.lock()[1..],
-                &[
-                    (
-                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
-                        "".into()
-                    ),
-                    (
-                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
-                        "".into()
-                    ),
-                    (
-                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
-                        "\n".into()
-                    ),
-                ]
-            );
-
-            // Insert blank lines between each line of the buffer.
-            async move {
-                Ok(Some(vec![
-                    lsp::TextEdit {
-                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
-                        new_text: "\n".into(),
-                    },
-                    lsp::TextEdit {
-                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
-                        new_text: "\n".into(),
-                    },
-                ]))
-            }
-        }
-    });
-
-    // After formatting the buffer, the trailing whitespace is stripped,
-    // a newline is appended, and the edits provided by the language server
-    // have been applied.
-    format.await.unwrap();
-    cx.assert_editor_state(
-        &[
-            "one",   //
-            "",      //
-            "twoˇ",  //
-            "",      //
-            "three", //
-            "four",  //
-            "",      //
-        ]
-        .join("\n"),
-    );
-
-    // Undoing the formatting undoes the trailing whitespace removal, the
-    // trailing newline, and the LSP edits.
-    cx.update_buffer(|buffer, cx| buffer.undo(cx));
-    cx.assert_editor_state(
-        &[
-            "one ",   //
-            "twoˇ",   //
-            "three ", //
-            "four",   //
-        ]
-        .join("\n"),
-    );
-}
-
-#[gpui::test]
-async fn test_completion(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |_| {});
-
-    let mut cx = EditorLspTestContext::new_rust(
-        lsp::ServerCapabilities {
-            completion_provider: Some(lsp::CompletionOptions {
-                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
-                resolve_provider: Some(true),
-                ..Default::default()
-            }),
-            ..Default::default()
-        },
-        cx,
-    )
-    .await;
-
-    cx.set_state(indoc! {"
-        oneˇ
-        two
-        three
-    "});
-    cx.simulate_keystroke(".");
-    handle_completion_request(
-        &mut cx,
-        indoc! {"
-            one.|<>
-            two
-            three
-        "},
-        vec!["first_completion", "second_completion"],
-    )
-    .await;
-    cx.condition(|editor, _| editor.context_menu_visible())
-        .await;
-    let apply_additional_edits = cx.update_editor(|editor, cx| {
-        editor.context_menu_next(&Default::default(), cx);
-        editor
-            .confirm_completion(&ConfirmCompletion::default(), cx)
-            .unwrap()
-    });
-    cx.assert_editor_state(indoc! {"
-        one.second_completionˇ
-        two
-        three
-    "});
-
-    handle_resolve_completion_request(
-        &mut cx,
-        Some(vec![
-            (
-                //This overlaps with the primary completion edit which is
-                //misbehavior from the LSP spec, test that we filter it out
-                indoc! {"
-                    one.second_ˇcompletion
-                    two
-                    threeˇ
-                "},
-                "overlapping additional edit",
-            ),
-            (
-                indoc! {"
-                    one.second_completion
-                    two
-                    threeˇ
-                "},
-                "\nadditional edit",
-            ),
-        ]),
-    )
-    .await;
-    apply_additional_edits.await.unwrap();
-    cx.assert_editor_state(indoc! {"
-        one.second_completionˇ
-        two
-        three
-        additional edit
-    "});
-
-    cx.set_state(indoc! {"
-        one.second_completion
-        twoˇ
-        threeˇ
-        additional edit
-    "});
-    cx.simulate_keystroke(" ");
-    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
-    cx.simulate_keystroke("s");
-    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
-
-    cx.assert_editor_state(indoc! {"
-        one.second_completion
-        two sˇ
-        three sˇ
-        additional edit
-    "});
-    handle_completion_request(
-        &mut cx,
-        indoc! {"
-            one.second_completion
-            two s
-            three <s|>
-            additional edit
-        "},
-        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
-    )
-    .await;
-    cx.condition(|editor, _| editor.context_menu_visible())
-        .await;
-
-    cx.simulate_keystroke("i");
-
-    handle_completion_request(
-        &mut cx,
-        indoc! {"
-            one.second_completion
-            two si
-            three <si|>
-            additional edit
-        "},
-        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
-    )
-    .await;
-    cx.condition(|editor, _| editor.context_menu_visible())
-        .await;
-
-    let apply_additional_edits = cx.update_editor(|editor, cx| {
-        editor
-            .confirm_completion(&ConfirmCompletion::default(), cx)
-            .unwrap()
-    });
-    cx.assert_editor_state(indoc! {"
-        one.second_completion
-        two sixth_completionˇ
-        three sixth_completionˇ
-        additional edit
-    "});
-
-    handle_resolve_completion_request(&mut cx, None).await;
-    apply_additional_edits.await.unwrap();
-
-    cx.update(|cx| {
-        cx.update_global::<SettingsStore, _, _>(|settings, cx| {
-            settings.update_user_settings::<EditorSettings>(cx, |settings| {
-                settings.show_completions_on_input = Some(false);
-            });
-        })
-    });
-    cx.set_state("editorˇ");
-    cx.simulate_keystroke(".");
-    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
-    cx.simulate_keystroke("c");
-    cx.simulate_keystroke("l");
-    cx.simulate_keystroke("o");
-    cx.assert_editor_state("editor.cloˇ");
-    assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
-    cx.update_editor(|editor, cx| {
-        editor.show_completions(&ShowCompletions, cx);
-    });
-    handle_completion_request(&mut cx, "editor.<clo|>", vec!["close", "clobber"]).await;
-    cx.condition(|editor, _| editor.context_menu_visible())
-        .await;
-    let apply_additional_edits = cx.update_editor(|editor, cx| {
-        editor
-            .confirm_completion(&ConfirmCompletion::default(), cx)
-            .unwrap()
-    });
-    cx.assert_editor_state("editor.closeˇ");
-    handle_resolve_completion_request(&mut cx, None).await;
-    apply_additional_edits.await.unwrap();
-}
-
-#[gpui::test]
-async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |_| {});
-    let mut cx = EditorTestContext::new(cx).await;
-    let language = Arc::new(Language::new(
-        LanguageConfig {
-            line_comment: Some("// ".into()),
-            ..Default::default()
-        },
-        Some(tree_sitter_rust::language()),
-    ));
-    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
-
-    // If multiple selections intersect a line, the line is only toggled once.
-    cx.set_state(indoc! {"
-        fn a() {
-            «//b();
-            ˇ»// «c();
-            //ˇ»  d();
-        }
-    "});
-
-    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
-
-    cx.assert_editor_state(indoc! {"
-        fn a() {
-            «b();
-            c();
-            ˇ» d();
-        }
-    "});
-
-    // The comment prefix is inserted at the same column for every line in a
-    // selection.
-    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
-
-    cx.assert_editor_state(indoc! {"
-        fn a() {
-            // «b();
-            // c();
-            ˇ»//  d();
-        }
-    "});
-
-    // If a selection ends at the beginning of a line, that line is not toggled.
-    cx.set_selections_state(indoc! {"
-        fn a() {
-            // b();
-            «// c();
-        ˇ»    //  d();
-        }
-    "});
-
-    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
-
-    cx.assert_editor_state(indoc! {"
-        fn a() {
-            // b();
-            «c();
-        ˇ»    //  d();
-        }
-    "});
-
-    // If a selection span a single line and is empty, the line is toggled.
-    cx.set_state(indoc! {"
-        fn a() {
-            a();
-            b();
-        ˇ
-        }
-    "});
-
-    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
-
-    cx.assert_editor_state(indoc! {"
-        fn a() {
-            a();
-            b();
-        //•ˇ
-        }
-    "});
-
-    // If a selection span multiple lines, empty lines are not toggled.
-    cx.set_state(indoc! {"
-        fn a() {
-            «a();
-
-            c();ˇ»
-        }
-    "});
-
-    cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
-
-    cx.assert_editor_state(indoc! {"
-        fn a() {
-            // «a();
-
-            // c();ˇ»
-        }
-    "});
-}
-
-#[gpui::test]
-async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |_| {});
-
-    let language = Arc::new(Language::new(
-        LanguageConfig {
-            line_comment: Some("// ".into()),
-            ..Default::default()
-        },
-        Some(tree_sitter_rust::language()),
-    ));
-
-    let registry = Arc::new(LanguageRegistry::test());
-    registry.add(language.clone());
-
-    let mut cx = EditorTestContext::new(cx).await;
-    cx.update_buffer(|buffer, cx| {
-        buffer.set_language_registry(registry);
-        buffer.set_language(Some(language), cx);
-    });
-
-    let toggle_comments = &ToggleComments {
-        advance_downwards: true,
-    };
-
-    // Single cursor on one line -> advance
-    // Cursor moves horizontally 3 characters as well on non-blank line
-    cx.set_state(indoc!(
-        "fn a() {
-             ˇdog();
-             cat();
-        }"
-    ));
-    cx.update_editor(|editor, cx| {
-        editor.toggle_comments(toggle_comments, cx);
-    });
-    cx.assert_editor_state(indoc!(
-        "fn a() {
-             // dog();
-             catˇ();
-        }"
-    ));
-
-    // Single selection on one line -> don't advance
-    cx.set_state(indoc!(
-        "fn a() {
-             «dog()ˇ»;
-             cat();
-        }"
-    ));
-    cx.update_editor(|editor, cx| {
-        editor.toggle_comments(toggle_comments, cx);
-    });
-    cx.assert_editor_state(indoc!(
-        "fn a() {
-             // «dog()ˇ»;
-             cat();
-        }"
-    ));
-
-    // Multiple cursors on one line -> advance
-    cx.set_state(indoc!(
-        "fn a() {
-             ˇdˇog();
-             cat();
-        }"
-    ));
-    cx.update_editor(|editor, cx| {
-        editor.toggle_comments(toggle_comments, cx);
-    });
-    cx.assert_editor_state(indoc!(
-        "fn a() {
-             // dog();
-             catˇ(ˇ);
-        }"
-    ));
-
-    // Multiple cursors on one line, with selection -> don't advance
-    cx.set_state(indoc!(
-        "fn a() {
-             ˇdˇog«()ˇ»;
-             cat();
-        }"
-    ));
-    cx.update_editor(|editor, cx| {
-        editor.toggle_comments(toggle_comments, cx);
-    });
-    cx.assert_editor_state(indoc!(
-        "fn a() {
-             // ˇdˇog«()ˇ»;
-             cat();
-        }"
-    ));
-
-    // Single cursor on one line -> advance
-    // Cursor moves to column 0 on blank line
-    cx.set_state(indoc!(
-        "fn a() {
-             ˇdog();
-
-             cat();
-        }"
-    ));
-    cx.update_editor(|editor, cx| {
-        editor.toggle_comments(toggle_comments, cx);
-    });
-    cx.assert_editor_state(indoc!(
-        "fn a() {
-             // dog();
-        ˇ
-             cat();
-        }"
-    ));
-
-    // Single cursor on one line -> advance
-    // Cursor starts and ends at column 0
-    cx.set_state(indoc!(
-        "fn a() {
-         ˇ    dog();
-             cat();
-        }"
-    ));
-    cx.update_editor(|editor, cx| {
-        editor.toggle_comments(toggle_comments, cx);
-    });
-    cx.assert_editor_state(indoc!(
-        "fn a() {
-             // dog();
-         ˇ    cat();
-        }"
-    ));
-}
-
-#[gpui::test]
-async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |_| {});
-
-    let mut cx = EditorTestContext::new(cx).await;
-
-    let html_language = Arc::new(
-        Language::new(
-            LanguageConfig {
-                name: "HTML".into(),
-                block_comment: Some(("<!-- ".into(), " -->".into())),
-                ..Default::default()
-            },
-            Some(tree_sitter_html::language()),
-        )
-        .with_injection_query(
-            r#"
-            (script_element
-                (raw_text) @content
-                (#set! "language" "javascript"))
-            "#,
-        )
-        .unwrap(),
-    );
-
-    let javascript_language = Arc::new(Language::new(
-        LanguageConfig {
-            name: "JavaScript".into(),
-            line_comment: Some("// ".into()),
-            ..Default::default()
-        },
-        Some(tree_sitter_typescript::language_tsx()),
-    ));
-
-    let registry = Arc::new(LanguageRegistry::test());
-    registry.add(html_language.clone());
-    registry.add(javascript_language.clone());
-
-    cx.update_buffer(|buffer, cx| {
-        buffer.set_language_registry(registry);
-        buffer.set_language(Some(html_language), cx);
-    });
-
-    // Toggle comments for empty selections
-    cx.set_state(
-        &r#"
-            <p>A</p>ˇ
-            <p>B</p>ˇ
-            <p>C</p>ˇ
-        "#
-        .unindent(),
-    );
-    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
-    cx.assert_editor_state(
-        &r#"
-            <!-- <p>A</p>ˇ -->
-            <!-- <p>B</p>ˇ -->
-            <!-- <p>C</p>ˇ -->
-        "#
-        .unindent(),
-    );
-    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
-    cx.assert_editor_state(
-        &r#"
-            <p>A</p>ˇ
-            <p>B</p>ˇ
-            <p>C</p>ˇ
-        "#
-        .unindent(),
-    );
-
-    // Toggle comments for mixture of empty and non-empty selections, where
-    // multiple selections occupy a given line.
-    cx.set_state(
-        &r#"
-            <p>A«</p>
-            <p>ˇ»B</p>ˇ
-            <p>C«</p>
-            <p>ˇ»D</p>ˇ
-        "#
-        .unindent(),
-    );
-
-    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
-    cx.assert_editor_state(
-        &r#"
-            <!-- <p>A«</p>
-            <p>ˇ»B</p>ˇ -->
-            <!-- <p>C«</p>
-            <p>ˇ»D</p>ˇ -->
-        "#
-        .unindent(),
-    );
-    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
-    cx.assert_editor_state(
-        &r#"
-            <p>A«</p>
-            <p>ˇ»B</p>ˇ
-            <p>C«</p>
-            <p>ˇ»D</p>ˇ
-        "#
-        .unindent(),
-    );
-
-    // Toggle comments when different languages are active for different
-    // selections.
-    cx.set_state(
-        &r#"
-            ˇ<script>
-                ˇvar x = new Y();
-            ˇ</script>
-        "#
-        .unindent(),
-    );
-    cx.foreground().run_until_parked();
-    cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
-    cx.assert_editor_state(
-        &r#"
-            <!-- ˇ<script> -->
-                // ˇvar x = new Y();
-            <!-- ˇ</script> -->
-        "#
-        .unindent(),
-    );
-}
-
-#[gpui::test]
-fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
-    init_test(cx, |_| {});
-
-    let buffer = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, sample_text(3, 4, 'a')));
-    let multibuffer = cx.add_model(|cx| {
-        let mut multibuffer = MultiBuffer::new(0);
-        multibuffer.push_excerpts(
-            buffer.clone(),
-            [
-                ExcerptRange {
-                    context: Point::new(0, 0)..Point::new(0, 4),
-                    primary: None,
-                },
-                ExcerptRange {
-                    context: Point::new(1, 0)..Point::new(1, 4),
-                    primary: None,
-                },
-            ],
-            cx,
-        );
-        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
-        multibuffer
-    });
-
-    let view = cx.add_window(|cx| build_editor(multibuffer, cx)).root(cx);
-    view.update(cx, |view, cx| {
-        assert_eq!(view.text(cx), "aaaa\nbbbb");
-        view.change_selections(None, cx, |s| {
-            s.select_ranges([
-                Point::new(0, 0)..Point::new(0, 0),
-                Point::new(1, 0)..Point::new(1, 0),
-            ])
-        });
-
-        view.handle_input("X", cx);
-        assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
-        assert_eq!(
-            view.selections.ranges(cx),
-            [
-                Point::new(0, 1)..Point::new(0, 1),
-                Point::new(1, 1)..Point::new(1, 1),
-            ]
-        );
-
-        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
-        view.change_selections(None, cx, |s| {
-            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
-        });
-        view.backspace(&Default::default(), cx);
-        assert_eq!(view.text(cx), "Xa\nbbb");
-        assert_eq!(
-            view.selections.ranges(cx),
-            [Point::new(1, 0)..Point::new(1, 0)]
-        );
-
-        view.change_selections(None, cx, |s| {
-            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
-        });
-        view.backspace(&Default::default(), cx);
-        assert_eq!(view.text(cx), "X\nbb");
-        assert_eq!(
-            view.selections.ranges(cx),
-            [Point::new(0, 1)..Point::new(0, 1)]
-        );
-    });
-}
-
-#[gpui::test]
-fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
-    init_test(cx, |_| {});
-
-    let markers = vec![('[', ']').into(), ('(', ')').into()];
-    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
-        indoc! {"
-            [aaaa
-            (bbbb]
-            cccc)",
-        },
-        markers.clone(),
-    );
-    let excerpt_ranges = markers.into_iter().map(|marker| {
-        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
-        ExcerptRange {
-            context,
-            primary: None,
-        }
-    });
-    let buffer = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, initial_text));
-    let multibuffer = cx.add_model(|cx| {
-        let mut multibuffer = MultiBuffer::new(0);
-        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
-        multibuffer
-    });
-
-    let view = cx.add_window(|cx| build_editor(multibuffer, cx)).root(cx);
-    view.update(cx, |view, cx| {
-        let (expected_text, selection_ranges) = marked_text_ranges(
-            indoc! {"
-                aaaa
-                bˇbbb
-                bˇbbˇb
-                cccc"
-            },
-            true,
-        );
-        assert_eq!(view.text(cx), expected_text);
-        view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
-
-        view.handle_input("X", cx);
-
-        let (expected_text, expected_selections) = marked_text_ranges(
-            indoc! {"
-                aaaa
-                bXˇbbXb
-                bXˇbbXˇb
-                cccc"
-            },
-            false,
-        );
-        assert_eq!(view.text(cx), expected_text);
-        assert_eq!(view.selections.ranges(cx), expected_selections);
-
-        view.newline(&Newline, cx);
-        let (expected_text, expected_selections) = marked_text_ranges(
-            indoc! {"
-                aaaa
-                bX
-                ˇbbX
-                b
-                bX
-                ˇbbX
-                ˇb
-                cccc"
-            },
-            false,
-        );
-        assert_eq!(view.text(cx), expected_text);
-        assert_eq!(view.selections.ranges(cx), expected_selections);
-    });
-}
-
-#[gpui::test]
-fn test_refresh_selections(cx: &mut TestAppContext) {
-    init_test(cx, |_| {});
-
-    let buffer = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, sample_text(3, 4, 'a')));
-    let mut excerpt1_id = None;
-    let multibuffer = cx.add_model(|cx| {
-        let mut multibuffer = MultiBuffer::new(0);
-        excerpt1_id = multibuffer
-            .push_excerpts(
-                buffer.clone(),
-                [
-                    ExcerptRange {
-                        context: Point::new(0, 0)..Point::new(1, 4),
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: Point::new(1, 0)..Point::new(2, 4),
-                        primary: None,
-                    },
-                ],
-                cx,
-            )
-            .into_iter()
-            .next();
-        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
-        multibuffer
-    });
-
-    let editor = cx
-        .add_window(|cx| {
-            let mut editor = build_editor(multibuffer.clone(), cx);
-            let snapshot = editor.snapshot(cx);
-            editor.change_selections(None, cx, |s| {
-                s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
-            });
-            editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
-            assert_eq!(
-                editor.selections.ranges(cx),
-                [
-                    Point::new(1, 3)..Point::new(1, 3),
-                    Point::new(2, 1)..Point::new(2, 1),
-                ]
-            );
-            editor
-        })
-        .root(cx);
-
-    // Refreshing selections is a no-op when excerpts haven't changed.
-    editor.update(cx, |editor, cx| {
-        editor.change_selections(None, cx, |s| s.refresh());
-        assert_eq!(
-            editor.selections.ranges(cx),
-            [
-                Point::new(1, 3)..Point::new(1, 3),
-                Point::new(2, 1)..Point::new(2, 1),
-            ]
-        );
-    });
-
-    multibuffer.update(cx, |multibuffer, cx| {
-        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
-    });
-    editor.update(cx, |editor, cx| {
-        // Removing an excerpt causes the first selection to become degenerate.
-        assert_eq!(
-            editor.selections.ranges(cx),
-            [
-                Point::new(0, 0)..Point::new(0, 0),
-                Point::new(0, 1)..Point::new(0, 1)
-            ]
-        );
-
-        // Refreshing selections will relocate the first selection to the original buffer
-        // location.
-        editor.change_selections(None, cx, |s| s.refresh());
-        assert_eq!(
-            editor.selections.ranges(cx),
-            [
-                Point::new(0, 1)..Point::new(0, 1),
-                Point::new(0, 3)..Point::new(0, 3)
-            ]
-        );
-        assert!(editor.selections.pending_anchor().is_some());
-    });
-}
-
-#[gpui::test]
-fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
-    init_test(cx, |_| {});
-
-    let buffer = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, sample_text(3, 4, 'a')));
-    let mut excerpt1_id = None;
-    let multibuffer = cx.add_model(|cx| {
-        let mut multibuffer = MultiBuffer::new(0);
-        excerpt1_id = multibuffer
-            .push_excerpts(
-                buffer.clone(),
-                [
-                    ExcerptRange {
-                        context: Point::new(0, 0)..Point::new(1, 4),
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: Point::new(1, 0)..Point::new(2, 4),
-                        primary: None,
-                    },
-                ],
-                cx,
-            )
-            .into_iter()
-            .next();
-        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
-        multibuffer
-    });
-
-    let editor = cx
-        .add_window(|cx| {
-            let mut editor = build_editor(multibuffer.clone(), cx);
-            let snapshot = editor.snapshot(cx);
-            editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
-            assert_eq!(
-                editor.selections.ranges(cx),
-                [Point::new(1, 3)..Point::new(1, 3)]
-            );
-            editor
-        })
-        .root(cx);
-
-    multibuffer.update(cx, |multibuffer, cx| {
-        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
-    });
-    editor.update(cx, |editor, cx| {
-        assert_eq!(
-            editor.selections.ranges(cx),
-            [Point::new(0, 0)..Point::new(0, 0)]
-        );
-
-        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
-        editor.change_selections(None, cx, |s| s.refresh());
-        assert_eq!(
-            editor.selections.ranges(cx),
-            [Point::new(0, 3)..Point::new(0, 3)]
-        );
-        assert!(editor.selections.pending_anchor().is_some());
-    });
-}
-
-#[gpui::test]
-async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |_| {});
-
-    let language = Arc::new(
-        Language::new(
-            LanguageConfig {
-                brackets: BracketPairConfig {
-                    pairs: vec![
-                        BracketPair {
-                            start: "{".to_string(),
-                            end: "}".to_string(),
-                            close: true,
-                            newline: true,
-                        },
-                        BracketPair {
-                            start: "/* ".to_string(),
-                            end: " */".to_string(),
-                            close: true,
-                            newline: true,
-                        },
-                    ],
-                    ..Default::default()
-                },
-                ..Default::default()
-            },
-            Some(tree_sitter_rust::language()),
-        )
-        .with_indents_query("")
-        .unwrap(),
-    );
-
-    let text = concat!(
-        "{   }\n",     //
-        "  x\n",       //
-        "  /*   */\n", //
-        "x\n",         //
-        "{{} }\n",     //
-    );
-
-    let buffer =
-        cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, text).with_language(language, cx));
-    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
-    let view = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
-    view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
-        .await;
-
-    view.update(cx, |view, cx| {
-        view.change_selections(None, cx, |s| {
-            s.select_display_ranges([
-                DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
-                DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
-                DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
-            ])
-        });
-        view.newline(&Newline, cx);
-
-        assert_eq!(
-            view.buffer().read(cx).read(cx).text(),
-            concat!(
-                "{ \n",    // Suppress rustfmt
-                "\n",      //
-                "}\n",     //
-                "  x\n",   //
-                "  /* \n", //
-                "  \n",    //
-                "  */\n",  //
-                "x\n",     //
-                "{{} \n",  //
-                "}\n",     //
-            )
-        );
-    });
-}
-
-#[gpui::test]
-fn test_highlighted_ranges(cx: &mut TestAppContext) {
-    init_test(cx, |_| {});
-
-    let editor = cx
-        .add_window(|cx| {
-            let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
-            build_editor(buffer.clone(), cx)
-        })
-        .root(cx);
-
-    editor.update(cx, |editor, cx| {
-        struct Type1;
-        struct Type2;
-
-        let buffer = editor.buffer.read(cx).snapshot(cx);
-
-        let anchor_range =
-            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
-
-        editor.highlight_background::<Type1>(
-            vec![
-                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
-                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
-                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
-                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
-            ],
-            |_| Color::red(),
-            cx,
-        );
-        editor.highlight_background::<Type2>(
-            vec![
-                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
-                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
-                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
-                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
-            ],
-            |_| Color::green(),
-            cx,
-        );
-
-        let snapshot = editor.snapshot(cx);
-        let mut highlighted_ranges = editor.background_highlights_in_range(
-            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
-            &snapshot,
-            theme::current(cx).as_ref(),
-        );
-        // Enforce a consistent ordering based on color without relying on the ordering of the
-        // highlight's `TypeId` which is non-deterministic.
-        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
-        assert_eq!(
-            highlighted_ranges,
-            &[
-                (
-                    DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
-                    Color::green(),
-                ),
-                (
-                    DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
-                    Color::green(),
-                ),
-                (
-                    DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
-                    Color::red(),
-                ),
-                (
-                    DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
-                    Color::red(),
-                ),
-            ]
-        );
-        assert_eq!(
-            editor.background_highlights_in_range(
-                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
-                &snapshot,
-                theme::current(cx).as_ref(),
-            ),
-            &[(
-                DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
-                Color::red(),
-            )]
-        );
-    });
-}
-
-#[gpui::test]
-async fn test_following(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |_| {});
-
-    let fs = FakeFs::new(cx.background());
-    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
-
-    let buffer = project.update(cx, |project, cx| {
-        let buffer = project
-            .create_buffer(&sample_text(16, 8, 'a'), None, cx)
-            .unwrap();
-        cx.add_model(|cx| MultiBuffer::singleton(buffer, cx))
-    });
-    let leader = cx
-        .add_window(|cx| build_editor(buffer.clone(), cx))
-        .root(cx);
-    let follower = cx
-        .update(|cx| {
-            cx.add_window(
-                WindowOptions {
-                    bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))),
-                    ..Default::default()
-                },
-                |cx| build_editor(buffer.clone(), cx),
-            )
-        })
-        .root(cx);
-
-    let is_still_following = Rc::new(RefCell::new(true));
-    let follower_edit_event_count = Rc::new(RefCell::new(0));
-    let pending_update = Rc::new(RefCell::new(None));
-    follower.update(cx, {
-        let update = pending_update.clone();
-        let is_still_following = is_still_following.clone();
-        let follower_edit_event_count = follower_edit_event_count.clone();
-        |_, cx| {
-            cx.subscribe(&leader, move |_, leader, event, cx| {
-                leader
-                    .read(cx)
-                    .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
-            })
-            .detach();
-
-            cx.subscribe(&follower, move |_, _, event, cx| {
-                if Editor::should_unfollow_on_event(event, cx) {
-                    *is_still_following.borrow_mut() = false;
-                }
-                if let Event::BufferEdited = event {
-                    *follower_edit_event_count.borrow_mut() += 1;
-                }
-            })
-            .detach();
-        }
-    });
-
-    // Update the selections only
-    leader.update(cx, |leader, cx| {
-        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
-    });
-    follower
-        .update(cx, |follower, cx| {
-            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
-        })
-        .await
-        .unwrap();
-    follower.read_with(cx, |follower, cx| {
-        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
-    });
-    assert_eq!(*is_still_following.borrow(), true);
-    assert_eq!(*follower_edit_event_count.borrow(), 0);
-
-    // Update the scroll position only
-    leader.update(cx, |leader, cx| {
-        leader.set_scroll_position(vec2f(1.5, 3.5), cx);
-    });
-    follower
-        .update(cx, |follower, cx| {
-            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
-        })
-        .await
-        .unwrap();
-    assert_eq!(
-        follower.update(cx, |follower, cx| follower.scroll_position(cx)),
-        vec2f(1.5, 3.5)
-    );
-    assert_eq!(*is_still_following.borrow(), true);
-    assert_eq!(*follower_edit_event_count.borrow(), 0);
-
-    // Update the selections and scroll position. The follower's scroll position is updated
-    // via autoscroll, not via the leader's exact scroll position.
-    leader.update(cx, |leader, cx| {
-        leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
-        leader.request_autoscroll(Autoscroll::newest(), cx);
-        leader.set_scroll_position(vec2f(1.5, 3.5), cx);
-    });
-    follower
-        .update(cx, |follower, cx| {
-            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
-        })
-        .await
-        .unwrap();
-    follower.update(cx, |follower, cx| {
-        assert_eq!(follower.scroll_position(cx), vec2f(1.5, 0.0));
-        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
-    });
-    assert_eq!(*is_still_following.borrow(), true);
-
-    // Creating a pending selection that precedes another selection
-    leader.update(cx, |leader, cx| {
-        leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
-        leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
-    });
-    follower
-        .update(cx, |follower, cx| {
-            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
-        })
-        .await
-        .unwrap();
-    follower.read_with(cx, |follower, cx| {
-        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
-    });
-    assert_eq!(*is_still_following.borrow(), true);
-
-    // Extend the pending selection so that it surrounds another selection
-    leader.update(cx, |leader, cx| {
-        leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
-    });
-    follower
-        .update(cx, |follower, cx| {
-            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
-        })
-        .await
-        .unwrap();
-    follower.read_with(cx, |follower, cx| {
-        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
-    });
-
-    // Scrolling locally breaks the follow
-    follower.update(cx, |follower, cx| {
-        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
-        follower.set_scroll_anchor(
-            ScrollAnchor {
-                anchor: top_anchor,
-                offset: vec2f(0.0, 0.5),
-            },
-            cx,
-        );
-    });
-    assert_eq!(*is_still_following.borrow(), false);
-}
-
-#[gpui::test]
-async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |_| {});
-
-    let fs = FakeFs::new(cx.background());
-    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
-    let workspace = cx
-        .add_window(|cx| Workspace::test_new(project.clone(), cx))
-        .root(cx);
-    let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
-
-    let leader = pane.update(cx, |_, cx| {
-        let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
-        cx.add_view(|cx| build_editor(multibuffer.clone(), cx))
-    });
-
-    // Start following the editor when it has no excerpts.
-    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
-    let follower_1 = cx
-        .update(|cx| {
-            Editor::from_state_proto(
-                pane.clone(),
-                workspace.clone(),
-                ViewId {
-                    creator: Default::default(),
-                    id: 0,
-                },
-                &mut state_message,
-                cx,
-            )
-        })
-        .unwrap()
-        .await
-        .unwrap();
-
-    let update_message = Rc::new(RefCell::new(None));
-    follower_1.update(cx, {
-        let update = update_message.clone();
-        |_, cx| {
-            cx.subscribe(&leader, move |_, leader, event, cx| {
-                leader
-                    .read(cx)
-                    .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
-            })
-            .detach();
-        }
-    });
-
-    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
-        (
-            project
-                .create_buffer("abc\ndef\nghi\njkl\n", None, cx)
-                .unwrap(),
-            project
-                .create_buffer("mno\npqr\nstu\nvwx\n", None, cx)
-                .unwrap(),
-        )
-    });
-
-    // Insert some excerpts.
-    leader.update(cx, |leader, cx| {
-        leader.buffer.update(cx, |multibuffer, cx| {
-            let excerpt_ids = multibuffer.push_excerpts(
-                buffer_1.clone(),
-                [
-                    ExcerptRange {
-                        context: 1..6,
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: 12..15,
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: 0..3,
-                        primary: None,
-                    },
-                ],
-                cx,
-            );
-            multibuffer.insert_excerpts_after(
-                excerpt_ids[0],
-                buffer_2.clone(),
-                [
-                    ExcerptRange {
-                        context: 8..12,
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: 0..6,
-                        primary: None,
-                    },
-                ],
-                cx,
-            );
-        });
-    });
-
-    // Apply the update of adding the excerpts.
-    follower_1
-        .update(cx, |follower, cx| {
-            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
-        })
-        .await
-        .unwrap();
-    assert_eq!(
-        follower_1.read_with(cx, |editor, cx| editor.text(cx)),
-        leader.read_with(cx, |editor, cx| editor.text(cx))
-    );
-    update_message.borrow_mut().take();
-
-    // Start following separately after it already has excerpts.
-    let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
-    let follower_2 = cx
-        .update(|cx| {
-            Editor::from_state_proto(
-                pane.clone(),
-                workspace.clone(),
-                ViewId {
-                    creator: Default::default(),
-                    id: 0,
-                },
-                &mut state_message,
-                cx,
-            )
-        })
-        .unwrap()
-        .await
-        .unwrap();
-    assert_eq!(
-        follower_2.read_with(cx, |editor, cx| editor.text(cx)),
-        leader.read_with(cx, |editor, cx| editor.text(cx))
-    );
-
-    // Remove some excerpts.
-    leader.update(cx, |leader, cx| {
-        leader.buffer.update(cx, |multibuffer, cx| {
-            let excerpt_ids = multibuffer.excerpt_ids();
-            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
-            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
-        });
-    });
-
-    // Apply the update of removing the excerpts.
-    follower_1
-        .update(cx, |follower, cx| {
-            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
-        })
-        .await
-        .unwrap();
-    follower_2
-        .update(cx, |follower, cx| {
-            follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
-        })
-        .await
-        .unwrap();
-    update_message.borrow_mut().take();
-    assert_eq!(
-        follower_1.read_with(cx, |editor, cx| editor.text(cx)),
-        leader.read_with(cx, |editor, cx| editor.text(cx))
-    );
-}
-
-#[test]
-fn test_combine_syntax_and_fuzzy_match_highlights() {
-    let string = "abcdefghijklmnop";
-    let syntax_ranges = [
-        (
-            0..3,
-            HighlightStyle {
-                color: Some(Color::red()),
-                ..Default::default()
-            },
-        ),
-        (
-            4..8,
-            HighlightStyle {
-                color: Some(Color::green()),
-                ..Default::default()
-            },
-        ),
-    ];
-    let match_indices = [4, 6, 7, 8];
-    assert_eq!(
-        combine_syntax_and_fuzzy_match_highlights(
-            string,
-            Default::default(),
-            syntax_ranges.into_iter(),
-            &match_indices,
-        ),
-        &[
-            (
-                0..3,
-                HighlightStyle {
-                    color: Some(Color::red()),
-                    ..Default::default()
-                },
-            ),
-            (
-                4..5,
-                HighlightStyle {
-                    color: Some(Color::green()),
-                    weight: Some(fonts::Weight::BOLD),
-                    ..Default::default()
-                },
-            ),
-            (
-                5..6,
-                HighlightStyle {
-                    color: Some(Color::green()),
-                    ..Default::default()
-                },
-            ),
-            (
-                6..8,
-                HighlightStyle {
-                    color: Some(Color::green()),
-                    weight: Some(fonts::Weight::BOLD),
-                    ..Default::default()
-                },
-            ),
-            (
-                8..9,
-                HighlightStyle {
-                    weight: Some(fonts::Weight::BOLD),
-                    ..Default::default()
-                },
-            ),
-        ]
-    );
-}
-
-#[gpui::test]
-async fn go_to_prev_overlapping_diagnostic(
-    deterministic: Arc<Deterministic>,
-    cx: &mut gpui::TestAppContext,
-) {
-    init_test(cx, |_| {});
-
-    let mut cx = EditorTestContext::new(cx).await;
-    let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
-
-    cx.set_state(indoc! {"
-        ˇfn func(abc def: i32) -> u32 {
-        }
-    "});
-
-    cx.update(|cx| {
-        project.update(cx, |project, cx| {
-            project
-                .update_diagnostics(
-                    LanguageServerId(0),
-                    lsp::PublishDiagnosticsParams {
-                        uri: lsp::Url::from_file_path("/root/file").unwrap(),
-                        version: None,
-                        diagnostics: vec![
-                            lsp::Diagnostic {
-                                range: lsp::Range::new(
-                                    lsp::Position::new(0, 11),
-                                    lsp::Position::new(0, 12),
-                                ),
-                                severity: Some(lsp::DiagnosticSeverity::ERROR),
-                                ..Default::default()
-                            },
-                            lsp::Diagnostic {
-                                range: lsp::Range::new(
-                                    lsp::Position::new(0, 12),
-                                    lsp::Position::new(0, 15),
-                                ),
-                                severity: Some(lsp::DiagnosticSeverity::ERROR),
-                                ..Default::default()
-                            },
-                            lsp::Diagnostic {
-                                range: lsp::Range::new(
-                                    lsp::Position::new(0, 25),
-                                    lsp::Position::new(0, 28),
-                                ),
-                                severity: Some(lsp::DiagnosticSeverity::ERROR),
-                                ..Default::default()
-                            },
-                        ],
-                    },
-                    &[],
-                    cx,
-                )
-                .unwrap()
-        });
-    });
-
-    deterministic.run_until_parked();
-
-    cx.update_editor(|editor, cx| {
-        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
-    });
-
-    cx.assert_editor_state(indoc! {"
-        fn func(abc def: i32) -> ˇu32 {
-        }
-    "});
-
-    cx.update_editor(|editor, cx| {
-        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
-    });
-
-    cx.assert_editor_state(indoc! {"
-        fn func(abc ˇdef: i32) -> u32 {
-        }
-    "});
-
-    cx.update_editor(|editor, cx| {
-        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
-    });
-
-    cx.assert_editor_state(indoc! {"
-        fn func(abcˇ def: i32) -> u32 {
-        }
-    "});
-
-    cx.update_editor(|editor, cx| {
-        editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
-    });
-
-    cx.assert_editor_state(indoc! {"
-        fn func(abc def: i32) -> ˇu32 {
-        }
-    "});
-}
-
-#[gpui::test]
-async fn go_to_hunk(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
-    init_test(cx, |_| {});
-
-    let mut cx = EditorTestContext::new(cx).await;
-
-    let diff_base = r#"
-        use some::mod;
-
-        const A: u32 = 42;
-
-        fn main() {
-            println!("hello");
-
-            println!("world");
-        }
-        "#
-    .unindent();
-
-    // Edits are modified, removed, modified, added
-    cx.set_state(
-        &r#"
-        use some::modified;
-
-        ˇ
-        fn main() {
-            println!("hello there");
-
-            println!("around the");
-            println!("world");
-        }
-        "#
-        .unindent(),
-    );
-
-    cx.set_diff_base(Some(&diff_base));
-    deterministic.run_until_parked();
-
-    cx.update_editor(|editor, cx| {
-        //Wrap around the bottom of the buffer
-        for _ in 0..3 {
-            editor.go_to_hunk(&GoToHunk, cx);
-        }
-    });
-
-    cx.assert_editor_state(
-        &r#"
-        ˇuse some::modified;
-
-
-        fn main() {
-            println!("hello there");
-
-            println!("around the");
-            println!("world");
-        }
-        "#
-        .unindent(),
-    );
-
-    cx.update_editor(|editor, cx| {
-        //Wrap around the top of the buffer
-        for _ in 0..2 {
-            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
-        }
-    });
-
-    cx.assert_editor_state(
-        &r#"
-        use some::modified;
-
-
-        fn main() {
-        ˇ    println!("hello there");
-
-            println!("around the");
-            println!("world");
-        }
-        "#
-        .unindent(),
-    );
-
-    cx.update_editor(|editor, cx| {
-        editor.go_to_prev_hunk(&GoToPrevHunk, cx);
-    });
-
-    cx.assert_editor_state(
-        &r#"
-        use some::modified;
-
-        ˇ
-        fn main() {
-            println!("hello there");
-
-            println!("around the");
-            println!("world");
-        }
-        "#
-        .unindent(),
-    );
-
-    cx.update_editor(|editor, cx| {
-        for _ in 0..3 {
-            editor.go_to_prev_hunk(&GoToPrevHunk, cx);
-        }
-    });
-
-    cx.assert_editor_state(
-        &r#"
-        use some::modified;
-
-
-        fn main() {
-        ˇ    println!("hello there");
-
-            println!("around the");
-            println!("world");
-        }
-        "#
-        .unindent(),
-    );
-
-    cx.update_editor(|editor, cx| {
-        editor.fold(&Fold, cx);
-
-        //Make sure that the fold only gets one hunk
-        for _ in 0..4 {
-            editor.go_to_hunk(&GoToHunk, cx);
-        }
-    });
-
-    cx.assert_editor_state(
-        &r#"
-        ˇuse some::modified;
-
-
-        fn main() {
-            println!("hello there");
-
-            println!("around the");
-            println!("world");
-        }
-        "#
-        .unindent(),
-    );
-}
-
-#[test]
-fn test_split_words() {
-    fn split<'a>(text: &'a str) -> Vec<&'a str> {
-        split_words(text).collect()
-    }
-
-    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
-    assert_eq!(split("hello_world"), &["hello_", "world"]);
-    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
-    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
-    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
-    assert_eq!(split("helloworld"), &["helloworld"]);
-}
-
-#[gpui::test]
-async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |_| {});
-
-    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
-    let mut assert = |before, after| {
-        let _state_context = cx.set_state(before);
-        cx.update_editor(|editor, cx| {
-            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
-        });
-        cx.assert_editor_state(after);
-    };
-
-    // Outside bracket jumps to outside of matching bracket
-    assert("console.logˇ(var);", "console.log(var)ˇ;");
-    assert("console.log(var)ˇ;", "console.logˇ(var);");
-
-    // Inside bracket jumps to inside of matching bracket
-    assert("console.log(ˇvar);", "console.log(varˇ);");
-    assert("console.log(varˇ);", "console.log(ˇvar);");
-
-    // When outside a bracket and inside, favor jumping to the inside bracket
-    assert(
-        "console.log('foo', [1, 2, 3]ˇ);",
-        "console.log(ˇ'foo', [1, 2, 3]);",
-    );
-    assert(
-        "console.log(ˇ'foo', [1, 2, 3]);",
-        "console.log('foo', [1, 2, 3]ˇ);",
-    );
-
-    // Bias forward if two options are equally likely
-    assert(
-        "let result = curried_fun()ˇ();",
-        "let result = curried_fun()()ˇ;",
-    );
-
-    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
-    assert(
-        indoc! {"
-            function test() {
-                console.log('test')ˇ
-            }"},
-        indoc! {"
-            function test() {
-                console.logˇ('test')
-            }"},
-    );
-}
-
-#[gpui::test(iterations = 10)]
-async fn test_copilot(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
-    init_test(cx, |_| {});
-
-    let (copilot, copilot_lsp) = Copilot::fake(cx);
-    cx.update(|cx| cx.set_global(copilot));
-    let mut cx = EditorLspTestContext::new_rust(
-        lsp::ServerCapabilities {
-            completion_provider: Some(lsp::CompletionOptions {
-                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
-                ..Default::default()
-            }),
-            ..Default::default()
-        },
-        cx,
-    )
-    .await;
-
-    // When inserting, ensure autocompletion is favored over Copilot suggestions.
-    cx.set_state(indoc! {"
-        oneˇ
-        two
-        three
-    "});
-    cx.simulate_keystroke(".");
-    let _ = handle_completion_request(
-        &mut cx,
-        indoc! {"
-            one.|<>
-            two
-            three
-        "},
-        vec!["completion_a", "completion_b"],
-    );
-    handle_copilot_completion_request(
-        &copilot_lsp,
-        vec![copilot::request::Completion {
-            text: "one.copilot1".into(),
-            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
-            ..Default::default()
-        }],
-        vec![],
-    );
-    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
-    cx.update_editor(|editor, cx| {
-        assert!(editor.context_menu_visible());
-        assert!(!editor.has_active_copilot_suggestion(cx));
-
-        // Confirming a completion inserts it and hides the context menu, without showing
-        // the copilot suggestion afterwards.
-        editor
-            .confirm_completion(&Default::default(), cx)
-            .unwrap()
-            .detach();
-        assert!(!editor.context_menu_visible());
-        assert!(!editor.has_active_copilot_suggestion(cx));
-        assert_eq!(editor.text(cx), "one.completion_a\ntwo\nthree\n");
-        assert_eq!(editor.display_text(cx), "one.completion_a\ntwo\nthree\n");
-    });
-
-    // Ensure Copilot suggestions are shown right away if no autocompletion is available.
-    cx.set_state(indoc! {"
-        oneˇ
-        two
-        three
-    "});
-    cx.simulate_keystroke(".");
-    let _ = handle_completion_request(
-        &mut cx,
-        indoc! {"
-            one.|<>
-            two
-            three
-        "},
-        vec![],
-    );
-    handle_copilot_completion_request(
-        &copilot_lsp,
-        vec![copilot::request::Completion {
-            text: "one.copilot1".into(),
-            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
-            ..Default::default()
-        }],
-        vec![],
-    );
-    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
-    cx.update_editor(|editor, cx| {
-        assert!(!editor.context_menu_visible());
-        assert!(editor.has_active_copilot_suggestion(cx));
-        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
-        assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
-    });
-
-    // Reset editor, and ensure autocompletion is still favored over Copilot suggestions.
-    cx.set_state(indoc! {"
-        oneˇ
-        two
-        three
-    "});
-    cx.simulate_keystroke(".");
-    let _ = handle_completion_request(
-        &mut cx,
-        indoc! {"
-            one.|<>
-            two
-            three
-        "},
-        vec!["completion_a", "completion_b"],
-    );
-    handle_copilot_completion_request(
-        &copilot_lsp,
-        vec![copilot::request::Completion {
-            text: "one.copilot1".into(),
-            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
-            ..Default::default()
-        }],
-        vec![],
-    );
-    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
-    cx.update_editor(|editor, cx| {
-        assert!(editor.context_menu_visible());
-        assert!(!editor.has_active_copilot_suggestion(cx));
-
-        // When hiding the context menu, the Copilot suggestion becomes visible.
-        editor.hide_context_menu(cx);
-        assert!(!editor.context_menu_visible());
-        assert!(editor.has_active_copilot_suggestion(cx));
-        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
-        assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
-    });
-
-    // Ensure existing completion is interpolated when inserting again.
-    cx.simulate_keystroke("c");
-    deterministic.run_until_parked();
-    cx.update_editor(|editor, cx| {
-        assert!(!editor.context_menu_visible());
-        assert!(editor.has_active_copilot_suggestion(cx));
-        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
-        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
-    });
-
-    // After debouncing, new Copilot completions should be requested.
-    handle_copilot_completion_request(
-        &copilot_lsp,
-        vec![copilot::request::Completion {
-            text: "one.copilot2".into(),
-            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 5)),
-            ..Default::default()
-        }],
-        vec![],
-    );
-    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
-    cx.update_editor(|editor, cx| {
-        assert!(!editor.context_menu_visible());
-        assert!(editor.has_active_copilot_suggestion(cx));
-        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
-        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
-
-        // Canceling should remove the active Copilot suggestion.
-        editor.cancel(&Default::default(), cx);
-        assert!(!editor.has_active_copilot_suggestion(cx));
-        assert_eq!(editor.display_text(cx), "one.c\ntwo\nthree\n");
-        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
-
-        // After canceling, tabbing shouldn't insert the previously shown suggestion.
-        editor.tab(&Default::default(), cx);
-        assert!(!editor.has_active_copilot_suggestion(cx));
-        assert_eq!(editor.display_text(cx), "one.c   \ntwo\nthree\n");
-        assert_eq!(editor.text(cx), "one.c   \ntwo\nthree\n");
-
-        // When undoing the previously active suggestion is shown again.
-        editor.undo(&Default::default(), cx);
-        assert!(editor.has_active_copilot_suggestion(cx));
-        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
-        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
-    });
-
-    // If an edit occurs outside of this editor, the suggestion is still correctly interpolated.
-    cx.update_buffer(|buffer, cx| buffer.edit([(5..5, "o")], None, cx));
-    cx.update_editor(|editor, cx| {
-        assert!(editor.has_active_copilot_suggestion(cx));
-        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
-        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
-
-        // Tabbing when there is an active suggestion inserts it.
-        editor.tab(&Default::default(), cx);
-        assert!(!editor.has_active_copilot_suggestion(cx));
-        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
-        assert_eq!(editor.text(cx), "one.copilot2\ntwo\nthree\n");
-
-        // When undoing the previously active suggestion is shown again.
-        editor.undo(&Default::default(), cx);
-        assert!(editor.has_active_copilot_suggestion(cx));
-        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
-        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
-
-        // Hide suggestion.
-        editor.cancel(&Default::default(), cx);
-        assert!(!editor.has_active_copilot_suggestion(cx));
-        assert_eq!(editor.display_text(cx), "one.co\ntwo\nthree\n");
-        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
-    });
-
-    // If an edit occurs outside of this editor but no suggestion is being shown,
-    // we won't make it visible.
-    cx.update_buffer(|buffer, cx| buffer.edit([(6..6, "p")], None, cx));
-    cx.update_editor(|editor, cx| {
-        assert!(!editor.has_active_copilot_suggestion(cx));
-        assert_eq!(editor.display_text(cx), "one.cop\ntwo\nthree\n");
-        assert_eq!(editor.text(cx), "one.cop\ntwo\nthree\n");
-    });
-
-    // Reset the editor to verify how suggestions behave when tabbing on leading indentation.
-    cx.update_editor(|editor, cx| {
-        editor.set_text("fn foo() {\n  \n}", cx);
-        editor.change_selections(None, cx, |s| {
-            s.select_ranges([Point::new(1, 2)..Point::new(1, 2)])
-        });
-    });
-    handle_copilot_completion_request(
-        &copilot_lsp,
-        vec![copilot::request::Completion {
-            text: "    let x = 4;".into(),
-            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
-            ..Default::default()
-        }],
-        vec![],
-    );
-
-    cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
-    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
-    cx.update_editor(|editor, cx| {
-        assert!(editor.has_active_copilot_suggestion(cx));
-        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
-        assert_eq!(editor.text(cx), "fn foo() {\n  \n}");
-
-        // Tabbing inside of leading whitespace inserts indentation without accepting the suggestion.
-        editor.tab(&Default::default(), cx);
-        assert!(editor.has_active_copilot_suggestion(cx));
-        assert_eq!(editor.text(cx), "fn foo() {\n    \n}");
-        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
-
-        // Tabbing again accepts the suggestion.
-        editor.tab(&Default::default(), cx);
-        assert!(!editor.has_active_copilot_suggestion(cx));
-        assert_eq!(editor.text(cx), "fn foo() {\n    let x = 4;\n}");
-        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
-    });
-}
-
-#[gpui::test]
-async fn test_copilot_completion_invalidation(
-    deterministic: Arc<Deterministic>,
-    cx: &mut gpui::TestAppContext,
-) {
-    init_test(cx, |_| {});
-
-    let (copilot, copilot_lsp) = Copilot::fake(cx);
-    cx.update(|cx| cx.set_global(copilot));
-    let mut cx = EditorLspTestContext::new_rust(
-        lsp::ServerCapabilities {
-            completion_provider: Some(lsp::CompletionOptions {
-                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
-                ..Default::default()
-            }),
-            ..Default::default()
-        },
-        cx,
-    )
-    .await;
-
-    cx.set_state(indoc! {"
-        one
-        twˇ
-        three
-    "});
-
-    handle_copilot_completion_request(
-        &copilot_lsp,
-        vec![copilot::request::Completion {
-            text: "two.foo()".into(),
-            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
-            ..Default::default()
-        }],
-        vec![],
-    );
-    cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
-    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
-    cx.update_editor(|editor, cx| {
-        assert!(editor.has_active_copilot_suggestion(cx));
-        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
-        assert_eq!(editor.text(cx), "one\ntw\nthree\n");
-
-        editor.backspace(&Default::default(), cx);
-        assert!(editor.has_active_copilot_suggestion(cx));
-        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
-        assert_eq!(editor.text(cx), "one\nt\nthree\n");
-
-        editor.backspace(&Default::default(), cx);
-        assert!(editor.has_active_copilot_suggestion(cx));
-        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
-        assert_eq!(editor.text(cx), "one\n\nthree\n");
-
-        // Deleting across the original suggestion range invalidates it.
-        editor.backspace(&Default::default(), cx);
-        assert!(!editor.has_active_copilot_suggestion(cx));
-        assert_eq!(editor.display_text(cx), "one\nthree\n");
-        assert_eq!(editor.text(cx), "one\nthree\n");
-
-        // Undoing the deletion restores the suggestion.
-        editor.undo(&Default::default(), cx);
-        assert!(editor.has_active_copilot_suggestion(cx));
-        assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
-        assert_eq!(editor.text(cx), "one\n\nthree\n");
-    });
-}
-
-#[gpui::test]
-async fn test_copilot_multibuffer(
-    deterministic: Arc<Deterministic>,
-    cx: &mut gpui::TestAppContext,
-) {
-    init_test(cx, |_| {});
-
-    let (copilot, copilot_lsp) = Copilot::fake(cx);
-    cx.update(|cx| cx.set_global(copilot));
-
-    let buffer_1 = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, "a = 1\nb = 2\n"));
-    let buffer_2 = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, "c = 3\nd = 4\n"));
-    let multibuffer = cx.add_model(|cx| {
-        let mut multibuffer = MultiBuffer::new(0);
-        multibuffer.push_excerpts(
-            buffer_1.clone(),
-            [ExcerptRange {
-                context: Point::new(0, 0)..Point::new(2, 0),
-                primary: None,
-            }],
-            cx,
-        );
-        multibuffer.push_excerpts(
-            buffer_2.clone(),
-            [ExcerptRange {
-                context: Point::new(0, 0)..Point::new(2, 0),
-                primary: None,
-            }],
-            cx,
-        );
-        multibuffer
-    });
-    let editor = cx.add_window(|cx| build_editor(multibuffer, cx)).root(cx);
-
-    handle_copilot_completion_request(
-        &copilot_lsp,
-        vec![copilot::request::Completion {
-            text: "b = 2 + a".into(),
-            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 5)),
-            ..Default::default()
-        }],
-        vec![],
-    );
-    editor.update(cx, |editor, cx| {
-        // Ensure copilot suggestions are shown for the first excerpt.
-        editor.change_selections(None, cx, |s| {
-            s.select_ranges([Point::new(1, 5)..Point::new(1, 5)])
-        });
-        editor.next_copilot_suggestion(&Default::default(), cx);
-    });
-    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
-    editor.update(cx, |editor, cx| {
-        assert!(editor.has_active_copilot_suggestion(cx));
-        assert_eq!(
-            editor.display_text(cx),
-            "\n\na = 1\nb = 2 + a\n\n\n\nc = 3\nd = 4\n"
-        );
-        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
-    });
-
-    handle_copilot_completion_request(
-        &copilot_lsp,
-        vec![copilot::request::Completion {
-            text: "d = 4 + c".into(),
-            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 6)),
-            ..Default::default()
-        }],
-        vec![],
-    );
-    editor.update(cx, |editor, cx| {
-        // Move to another excerpt, ensuring the suggestion gets cleared.
-        editor.change_selections(None, cx, |s| {
-            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
-        });
-        assert!(!editor.has_active_copilot_suggestion(cx));
-        assert_eq!(
-            editor.display_text(cx),
-            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4\n"
-        );
-        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
-
-        // Type a character, ensuring we don't even try to interpolate the previous suggestion.
-        editor.handle_input(" ", cx);
-        assert!(!editor.has_active_copilot_suggestion(cx));
-        assert_eq!(
-            editor.display_text(cx),
-            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 \n"
-        );
-        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
-    });
-
-    // Ensure the new suggestion is displayed when the debounce timeout expires.
-    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
-    editor.update(cx, |editor, cx| {
-        assert!(editor.has_active_copilot_suggestion(cx));
-        assert_eq!(
-            editor.display_text(cx),
-            "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 + c\n"
-        );
-        assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
-    });
-}
-
-#[gpui::test]
-async fn test_copilot_disabled_globs(
-    deterministic: Arc<Deterministic>,
-    cx: &mut gpui::TestAppContext,
-) {
-    init_test(cx, |settings| {
-        settings
-            .copilot
-            .get_or_insert(Default::default())
-            .disabled_globs = Some(vec![".env*".to_string()]);
-    });
-
-    let (copilot, copilot_lsp) = Copilot::fake(cx);
-    cx.update(|cx| cx.set_global(copilot));
-
-    let fs = FakeFs::new(cx.background());
-    fs.insert_tree(
-        "/test",
-        json!({
-            ".env": "SECRET=something\n",
-            "README.md": "hello\n"
-        }),
-    )
-    .await;
-    let project = Project::test(fs, ["/test".as_ref()], cx).await;
-
-    let private_buffer = project
-        .update(cx, |project, cx| {
-            project.open_local_buffer("/test/.env", cx)
-        })
-        .await
-        .unwrap();
-    let public_buffer = project
-        .update(cx, |project, cx| {
-            project.open_local_buffer("/test/README.md", cx)
-        })
-        .await
-        .unwrap();
-
-    let multibuffer = cx.add_model(|cx| {
-        let mut multibuffer = MultiBuffer::new(0);
-        multibuffer.push_excerpts(
-            private_buffer.clone(),
-            [ExcerptRange {
-                context: Point::new(0, 0)..Point::new(1, 0),
-                primary: None,
-            }],
-            cx,
-        );
-        multibuffer.push_excerpts(
-            public_buffer.clone(),
-            [ExcerptRange {
-                context: Point::new(0, 0)..Point::new(1, 0),
-                primary: None,
-            }],
-            cx,
-        );
-        multibuffer
-    });
-    let editor = cx.add_window(|cx| build_editor(multibuffer, cx)).root(cx);
-
-    let mut copilot_requests = copilot_lsp
-        .handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| async move {
-            Ok(copilot::request::GetCompletionsResult {
-                completions: vec![copilot::request::Completion {
-                    text: "next line".into(),
-                    range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
-                    ..Default::default()
-                }],
-            })
-        });
-
-    editor.update(cx, |editor, cx| {
-        editor.change_selections(None, cx, |selections| {
-            selections.select_ranges([Point::new(0, 0)..Point::new(0, 0)])
-        });
-        editor.next_copilot_suggestion(&Default::default(), cx);
-    });
-
-    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
-    assert!(copilot_requests.try_next().is_err());
-
-    editor.update(cx, |editor, cx| {
-        editor.change_selections(None, cx, |s| {
-            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
-        });
-        editor.next_copilot_suggestion(&Default::default(), cx);
-    });
-
-    deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
-    assert!(copilot_requests.try_next().is_ok());
-}
-
-#[gpui::test]
-async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |_| {});
-
-    let mut language = Language::new(
-        LanguageConfig {
-            name: "Rust".into(),
-            path_suffixes: vec!["rs".to_string()],
-            brackets: BracketPairConfig {
-                pairs: vec![BracketPair {
-                    start: "{".to_string(),
-                    end: "}".to_string(),
-                    close: true,
-                    newline: true,
-                }],
-                disabled_scopes_by_bracket_ix: Vec::new(),
-            },
-            ..Default::default()
-        },
-        Some(tree_sitter_rust::language()),
-    );
-    let mut fake_servers = language
-        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
-            capabilities: lsp::ServerCapabilities {
-                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
-                    first_trigger_character: "{".to_string(),
-                    more_trigger_character: None,
-                }),
-                ..Default::default()
-            },
-            ..Default::default()
-        }))
-        .await;
-
-    let fs = FakeFs::new(cx.background());
-    fs.insert_tree(
-        "/a",
-        json!({
-            "main.rs": "fn main() { let a = 5; }",
-            "other.rs": "// Test file",
-        }),
-    )
-    .await;
-    let project = Project::test(fs, ["/a".as_ref()], cx).await;
-    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
-    let workspace = cx
-        .add_window(|cx| Workspace::test_new(project.clone(), cx))
-        .root(cx);
-    let worktree_id = workspace.update(cx, |workspace, cx| {
-        workspace.project().read_with(cx, |project, cx| {
-            project.worktrees(cx).next().unwrap().read(cx).id()
-        })
-    });
-
-    let buffer = project
-        .update(cx, |project, cx| {
-            project.open_local_buffer("/a/main.rs", cx)
-        })
-        .await
-        .unwrap();
-    cx.foreground().run_until_parked();
-    cx.foreground().start_waiting();
-    let fake_server = fake_servers.next().await.unwrap();
-    let editor_handle = workspace
-        .update(cx, |workspace, cx| {
-            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
-        })
-        .await
-        .unwrap()
-        .downcast::<Editor>()
-        .unwrap();
-
-    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
-        assert_eq!(
-            params.text_document_position.text_document.uri,
-            lsp::Url::from_file_path("/a/main.rs").unwrap(),
-        );
-        assert_eq!(
-            params.text_document_position.position,
-            lsp::Position::new(0, 21),
-        );
-
-        Ok(Some(vec![lsp::TextEdit {
-            new_text: "]".to_string(),
-            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
-        }]))
-    });
-
-    editor_handle.update(cx, |editor, cx| {
-        cx.focus(&editor_handle);
-        editor.change_selections(None, cx, |s| {
-            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
-        });
-        editor.handle_input("{", cx);
-    });
-
-    cx.foreground().run_until_parked();
-
-    buffer.read_with(cx, |buffer, _| {
-        assert_eq!(
-            buffer.text(),
-            "fn main() { let a = {5}; }",
-            "No extra braces from on type formatting should appear in the buffer"
-        )
-    });
-}
-
-#[gpui::test]
-async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |_| {});
-
-    let language_name: Arc<str> = "Rust".into();
-    let mut language = Language::new(
-        LanguageConfig {
-            name: Arc::clone(&language_name),
-            path_suffixes: vec!["rs".to_string()],
-            ..Default::default()
-        },
-        Some(tree_sitter_rust::language()),
-    );
-
-    let server_restarts = Arc::new(AtomicUsize::new(0));
-    let closure_restarts = Arc::clone(&server_restarts);
-    let language_server_name = "test language server";
-    let mut fake_servers = language
-        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
-            name: language_server_name,
-            initialization_options: Some(json!({
-                "testOptionValue": true
-            })),
-            initializer: Some(Box::new(move |fake_server| {
-                let task_restarts = Arc::clone(&closure_restarts);
-                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
-                    task_restarts.fetch_add(1, atomic::Ordering::Release);
-                    futures::future::ready(Ok(()))
-                });
-            })),
-            ..Default::default()
-        }))
-        .await;
-
-    let fs = FakeFs::new(cx.background());
-    fs.insert_tree(
-        "/a",
-        json!({
-            "main.rs": "fn main() { let a = 5; }",
-            "other.rs": "// Test file",
-        }),
-    )
-    .await;
-    let project = Project::test(fs, ["/a".as_ref()], cx).await;
-    project.update(cx, |project, _| project.languages().add(Arc::new(language)));
-    let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
-    let _buffer = project
-        .update(cx, |project, cx| {
-            project.open_local_buffer("/a/main.rs", cx)
-        })
-        .await
-        .unwrap();
-    let _fake_server = fake_servers.next().await.unwrap();
-    update_test_language_settings(cx, |language_settings| {
-        language_settings.languages.insert(
-            Arc::clone(&language_name),
-            LanguageSettingsContent {
-                tab_size: NonZeroU32::new(8),
-                ..Default::default()
-            },
-        );
-    });
-    cx.foreground().run_until_parked();
-    assert_eq!(
-        server_restarts.load(atomic::Ordering::Acquire),
-        0,
-        "Should not restart LSP server on an unrelated change"
-    );
-
-    update_test_project_settings(cx, |project_settings| {
-        project_settings.lsp.insert(
-            "Some other server name".into(),
-            LspSettings {
-                initialization_options: Some(json!({
-                    "some other init value": false
-                })),
-            },
-        );
-    });
-    cx.foreground().run_until_parked();
-    assert_eq!(
-        server_restarts.load(atomic::Ordering::Acquire),
-        0,
-        "Should not restart LSP server on an unrelated LSP settings change"
-    );
-
-    update_test_project_settings(cx, |project_settings| {
-        project_settings.lsp.insert(
-            language_server_name.into(),
-            LspSettings {
-                initialization_options: Some(json!({
-                    "anotherInitValue": false
-                })),
-            },
-        );
-    });
-    cx.foreground().run_until_parked();
-    assert_eq!(
-        server_restarts.load(atomic::Ordering::Acquire),
-        1,
-        "Should restart LSP server on a related LSP settings change"
-    );
-
-    update_test_project_settings(cx, |project_settings| {
-        project_settings.lsp.insert(
-            language_server_name.into(),
-            LspSettings {
-                initialization_options: Some(json!({
-                    "anotherInitValue": false
-                })),
-            },
-        );
-    });
-    cx.foreground().run_until_parked();
-    assert_eq!(
-        server_restarts.load(atomic::Ordering::Acquire),
-        1,
-        "Should not restart LSP server on a related LSP settings change that is the same"
-    );
-
-    update_test_project_settings(cx, |project_settings| {
-        project_settings.lsp.insert(
-            language_server_name.into(),
-            LspSettings {
-                initialization_options: None,
-            },
-        );
-    });
-    cx.foreground().run_until_parked();
-    assert_eq!(
-        server_restarts.load(atomic::Ordering::Acquire),
-        2,
-        "Should restart LSP server on another related LSP settings change"
-    );
-}
-
-#[gpui::test]
-async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |_| {});
-
-    let mut cx = EditorLspTestContext::new_rust(
-        lsp::ServerCapabilities {
-            completion_provider: Some(lsp::CompletionOptions {
-                trigger_characters: Some(vec![".".to_string()]),
-                resolve_provider: Some(true),
-                ..Default::default()
-            }),
-            ..Default::default()
-        },
-        cx,
-    )
-    .await;
-
-    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
-    cx.simulate_keystroke(".");
-    let completion_item = lsp::CompletionItem {
-        label: "some".into(),
-        kind: Some(lsp::CompletionItemKind::SNIPPET),
-        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
-        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
-            kind: lsp::MarkupKind::Markdown,
-            value: "```rust\nSome(2)\n```".to_string(),
-        })),
-        deprecated: Some(false),
-        sort_text: Some("fffffff2".to_string()),
-        filter_text: Some("some".to_string()),
-        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
-        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
-            range: lsp::Range {
-                start: lsp::Position {
-                    line: 0,
-                    character: 22,
-                },
-                end: lsp::Position {
-                    line: 0,
-                    character: 22,
-                },
-            },
-            new_text: "Some(2)".to_string(),
-        })),
-        additional_text_edits: Some(vec![lsp::TextEdit {
-            range: lsp::Range {
-                start: lsp::Position {
-                    line: 0,
-                    character: 20,
-                },
-                end: lsp::Position {
-                    line: 0,
-                    character: 22,
-                },
-            },
-            new_text: "".to_string(),
-        }]),
-        ..Default::default()
-    };
-
-    let closure_completion_item = completion_item.clone();
-    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
-        let task_completion_item = closure_completion_item.clone();
-        async move {
-            Ok(Some(lsp::CompletionResponse::Array(vec![
-                task_completion_item,
-            ])))
-        }
-    });
-
-    request.next().await;
-
-    cx.condition(|editor, _| editor.context_menu_visible())
-        .await;
-    let apply_additional_edits = cx.update_editor(|editor, cx| {
-        editor
-            .confirm_completion(&ConfirmCompletion::default(), cx)
-            .unwrap()
-    });
-    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
-
-    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
-        let task_completion_item = completion_item.clone();
-        async move { Ok(task_completion_item) }
-    })
-    .next()
-    .await
-    .unwrap();
-    apply_additional_edits.await.unwrap();
-    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
-}
-
-#[gpui::test]
-async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |_| {});
-
-    let mut cx = EditorLspTestContext::new(
-        Language::new(
-            LanguageConfig {
-                path_suffixes: vec!["jsx".into()],
-                overrides: [(
-                    "element".into(),
-                    LanguageConfigOverride {
-                        word_characters: Override::Set(['-'].into_iter().collect()),
-                        ..Default::default()
-                    },
-                )]
-                .into_iter()
-                .collect(),
-                ..Default::default()
-            },
-            Some(tree_sitter_typescript::language_tsx()),
-        )
-        .with_override_query("(jsx_self_closing_element) @element")
-        .unwrap(),
-        lsp::ServerCapabilities {
-            completion_provider: Some(lsp::CompletionOptions {
-                trigger_characters: Some(vec![":".to_string()]),
-                ..Default::default()
-            }),
-            ..Default::default()
-        },
-        cx,
-    )
-    .await;
-
-    cx.lsp
-        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
-            Ok(Some(lsp::CompletionResponse::Array(vec![
-                lsp::CompletionItem {
-                    label: "bg-blue".into(),
-                    ..Default::default()
-                },
-                lsp::CompletionItem {
-                    label: "bg-red".into(),
-                    ..Default::default()
-                },
-                lsp::CompletionItem {
-                    label: "bg-yellow".into(),
-                    ..Default::default()
-                },
-            ])))
-        });
-
-    cx.set_state(r#"<p class="bgˇ" />"#);
-
-    // Trigger completion when typing a dash, because the dash is an extra
-    // word character in the 'element' scope, which contains the cursor.
-    cx.simulate_keystroke("-");
-    cx.foreground().run_until_parked();
-    cx.update_editor(|editor, _| {
-        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
-            assert_eq!(
-                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
-                &["bg-red", "bg-blue", "bg-yellow"]
-            );
-        } else {
-            panic!("expected completion menu to be open");
-        }
-    });
-
-    cx.simulate_keystroke("l");
-    cx.foreground().run_until_parked();
-    cx.update_editor(|editor, _| {
-        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
-            assert_eq!(
-                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
-                &["bg-blue", "bg-yellow"]
-            );
-        } else {
-            panic!("expected completion menu to be open");
-        }
-    });
-
-    // When filtering completions, consider the character after the '-' to
-    // be the start of a subword.
-    cx.set_state(r#"<p class="yelˇ" />"#);
-    cx.simulate_keystroke("l");
-    cx.foreground().run_until_parked();
-    cx.update_editor(|editor, _| {
-        if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
-            assert_eq!(
-                menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
-                &["bg-yellow"]
-            );
-        } else {
-            panic!("expected completion menu to be open");
-        }
-    });
-}
-
-#[gpui::test]
-async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |settings| {
-        settings.defaults.formatter = Some(language_settings::Formatter::Prettier)
-    });
-
-    let mut language = Language::new(
-        LanguageConfig {
-            name: "Rust".into(),
-            path_suffixes: vec!["rs".to_string()],
-            prettier_parser_name: Some("test_parser".to_string()),
-            ..Default::default()
-        },
-        Some(tree_sitter_rust::language()),
-    );
-
-    let test_plugin = "test_plugin";
-    let _ = language
-        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
-            prettier_plugins: vec![test_plugin],
-            ..Default::default()
-        }))
-        .await;
-
-    let fs = FakeFs::new(cx.background());
-    fs.insert_file("/file.rs", Default::default()).await;
-
-    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
-    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
-    project.update(cx, |project, _| {
-        project.languages().add(Arc::new(language));
-    });
-    let buffer = project
-        .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
-        .await
-        .unwrap();
-
-    let buffer_text = "one\ntwo\nthree\n";
-    let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
-    let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
-    editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
-
-    let format = editor.update(cx, |editor, cx| {
-        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
-    });
-    format.await.unwrap();
-    assert_eq!(
-        editor.read_with(cx, |editor, cx| editor.text(cx)),
-        buffer_text.to_string() + prettier_format_suffix,
-        "Test prettier formatting was not applied to the original buffer text",
-    );
-
-    update_test_language_settings(cx, |settings| {
-        settings.defaults.formatter = Some(language_settings::Formatter::Auto)
-    });
-    let format = editor.update(cx, |editor, cx| {
-        editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
-    });
-    format.await.unwrap();
-    assert_eq!(
-        editor.read_with(cx, |editor, cx| editor.text(cx)),
-        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
-        "Autoformatting (via test prettier) was not applied to the original buffer text",
-    );
-}
-
-fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
-    let point = DisplayPoint::new(row as u32, column as u32);
-    point..point
-}
-
-fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
-    let (text, ranges) = marked_text_ranges(marked_text, true);
-    assert_eq!(view.text(cx), text);
-    assert_eq!(
-        view.selections.ranges(cx),
-        ranges,
-        "Assert selections are {}",
-        marked_text
-    );
-}
-
-/// Handle completion request passing a marked string specifying where the completion
-/// should be triggered from using '|' character, what range should be replaced, and what completions
-/// should be returned using '<' and '>' to delimit the range
-pub fn handle_completion_request<'a>(
-    cx: &mut EditorLspTestContext<'a>,
-    marked_string: &str,
-    completions: Vec<&'static str>,
-) -> impl Future<Output = ()> {
-    let complete_from_marker: TextRangeMarker = '|'.into();
-    let replace_range_marker: TextRangeMarker = ('<', '>').into();
-    let (_, mut marked_ranges) = marked_text_ranges_by(
-        marked_string,
-        vec![complete_from_marker.clone(), replace_range_marker.clone()],
-    );
-
-    let complete_from_position =
-        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
-    let replace_range =
-        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
-
-    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
-        let completions = completions.clone();
-        async move {
-            assert_eq!(params.text_document_position.text_document.uri, url.clone());
-            assert_eq!(
-                params.text_document_position.position,
-                complete_from_position
-            );
-            Ok(Some(lsp::CompletionResponse::Array(
-                completions
-                    .iter()
-                    .map(|completion_text| lsp::CompletionItem {
-                        label: completion_text.to_string(),
-                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
-                            range: replace_range,
-                            new_text: completion_text.to_string(),
-                        })),
-                        ..Default::default()
-                    })
-                    .collect(),
-            )))
-        }
-    });
-
-    async move {
-        request.next().await;
-    }
-}
-
-fn handle_resolve_completion_request<'a>(
-    cx: &mut EditorLspTestContext<'a>,
-    edits: Option<Vec<(&'static str, &'static str)>>,
-) -> impl Future<Output = ()> {
-    let edits = edits.map(|edits| {
-        edits
-            .iter()
-            .map(|(marked_string, new_text)| {
-                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
-                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
-                lsp::TextEdit::new(replace_range, new_text.to_string())
-            })
-            .collect::<Vec<_>>()
-    });
-
-    let mut request =
-        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
-            let edits = edits.clone();
-            async move {
-                Ok(lsp::CompletionItem {
-                    additional_text_edits: edits,
-                    ..Default::default()
-                })
-            }
-        });
-
-    async move {
-        request.next().await;
-    }
-}
-
-fn handle_copilot_completion_request(
-    lsp: &lsp::FakeLanguageServer,
-    completions: Vec<copilot::request::Completion>,
-    completions_cycling: Vec<copilot::request::Completion>,
-) {
-    lsp.handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| {
-        let completions = completions.clone();
-        async move {
-            Ok(copilot::request::GetCompletionsResult {
-                completions: completions.clone(),
-            })
-        }
-    });
-    lsp.handle_request::<copilot::request::GetCompletionsCycling, _, _>(move |_params, _cx| {
-        let completions_cycling = completions_cycling.clone();
-        async move {
-            Ok(copilot::request::GetCompletionsResult {
-                completions: completions_cycling.clone(),
-            })
-        }
-    });
-}
-
-pub(crate) fn update_test_language_settings(
-    cx: &mut TestAppContext,
-    f: impl Fn(&mut AllLanguageSettingsContent),
-) {
-    cx.update(|cx| {
-        cx.update_global::<SettingsStore, _, _>(|store, cx| {
-            store.update_user_settings::<AllLanguageSettings>(cx, f);
-        });
-    });
-}
-
-pub(crate) fn update_test_project_settings(
-    cx: &mut TestAppContext,
-    f: impl Fn(&mut ProjectSettings),
-) {
-    cx.update(|cx| {
-        cx.update_global::<SettingsStore, _, _>(|store, cx| {
-            store.update_user_settings::<ProjectSettings>(cx, f);
-        });
-    });
-}
-
-pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
-    cx.foreground().forbid_parking();
-
-    cx.update(|cx| {
-        cx.set_global(SettingsStore::test(cx));
-        theme::init((), cx);
-        client::init_settings(cx);
-        language::init(cx);
-        Project::init_settings(cx);
-        workspace::init_settings(cx);
-        crate::init(cx);
-    });
-
-    update_test_language_settings(cx, f);
-}
+// use super::*;
+// use crate::{
+//     scroll::scroll_amount::ScrollAmount,
+//     test::{
+//         assert_text_with_selections, build_editor, editor_lsp_test_context::EditorLspTestContext,
+//         editor_test_context::EditorTestContext, select_ranges,
+//     },
+//     JoinLines,
+// };
+// use drag_and_drop::DragAndDrop;
+// use futures::StreamExt;
+// use gpui::{
+//     executor::Deterministic,
+//     geometry::{rect::RectF, vector::vec2f},
+//     platform::{WindowBounds, WindowOptions},
+//     serde_json::{self, json},
+//     TestAppContext,
+// };
+// use indoc::indoc;
+// use language::{
+//     language_settings::{AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent},
+//     BracketPairConfig, FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageRegistry,
+//     Override, Point,
+// };
+// use parking_lot::Mutex;
+// use project::project_settings::{LspSettings, ProjectSettings};
+// use project::FakeFs;
+// use std::sync::atomic;
+// use std::sync::atomic::AtomicUsize;
+// use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
+// use unindent::Unindent;
+// use util::{
+//     assert_set_eq,
+//     test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
+// };
+// use workspace::{
+//     item::{FollowableItem, Item, ItemHandle},
+//     NavigationEntry, ViewId,
+// };
+
+// #[gpui::test]
+// fn test_edit_events(cx: &mut TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let buffer = cx.add_model(|cx| {
+//         let mut buffer = language::Buffer::new(0, cx.model_id() as u64, "123456");
+//         buffer.set_group_interval(Duration::from_secs(1));
+//         buffer
+//     });
+
+//     let events = Rc::new(RefCell::new(Vec::new()));
+//     let editor1 = cx
+//         .add_window({
+//             let events = events.clone();
+//             |cx| {
+//                 cx.subscribe(&cx.handle(), move |_, _, event, _| {
+//                     if matches!(
+//                         event,
+//                         Event::Edited | Event::BufferEdited | Event::DirtyChanged
+//                     ) {
+//                         events.borrow_mut().push(("editor1", event.clone()));
+//                     }
+//                 })
+//                 .detach();
+//                 Editor::for_buffer(buffer.clone(), None, cx)
+//             }
+//         })
+//         .root(cx);
+//     let editor2 = cx
+//         .add_window({
+//             let events = events.clone();
+//             |cx| {
+//                 cx.subscribe(&cx.handle(), move |_, _, event, _| {
+//                     if matches!(
+//                         event,
+//                         Event::Edited | Event::BufferEdited | Event::DirtyChanged
+//                     ) {
+//                         events.borrow_mut().push(("editor2", event.clone()));
+//                     }
+//                 })
+//                 .detach();
+//                 Editor::for_buffer(buffer.clone(), None, cx)
+//             }
+//         })
+//         .root(cx);
+//     assert_eq!(mem::take(&mut *events.borrow_mut()), []);
+
+//     // Mutating editor 1 will emit an `Edited` event only for that editor.
+//     editor1.update(cx, |editor, cx| editor.insert("X", cx));
+//     assert_eq!(
+//         mem::take(&mut *events.borrow_mut()),
+//         [
+//             ("editor1", Event::Edited),
+//             ("editor1", Event::BufferEdited),
+//             ("editor2", Event::BufferEdited),
+//             ("editor1", Event::DirtyChanged),
+//             ("editor2", Event::DirtyChanged)
+//         ]
+//     );
+
+//     // Mutating editor 2 will emit an `Edited` event only for that editor.
+//     editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
+//     assert_eq!(
+//         mem::take(&mut *events.borrow_mut()),
+//         [
+//             ("editor2", Event::Edited),
+//             ("editor1", Event::BufferEdited),
+//             ("editor2", Event::BufferEdited),
+//         ]
+//     );
+
+//     // Undoing on editor 1 will emit an `Edited` event only for that editor.
+//     editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
+//     assert_eq!(
+//         mem::take(&mut *events.borrow_mut()),
+//         [
+//             ("editor1", Event::Edited),
+//             ("editor1", Event::BufferEdited),
+//             ("editor2", Event::BufferEdited),
+//             ("editor1", Event::DirtyChanged),
+//             ("editor2", Event::DirtyChanged),
+//         ]
+//     );
+
+//     // Redoing on editor 1 will emit an `Edited` event only for that editor.
+//     editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
+//     assert_eq!(
+//         mem::take(&mut *events.borrow_mut()),
+//         [
+//             ("editor1", Event::Edited),
+//             ("editor1", Event::BufferEdited),
+//             ("editor2", Event::BufferEdited),
+//             ("editor1", Event::DirtyChanged),
+//             ("editor2", Event::DirtyChanged),
+//         ]
+//     );
+
+//     // Undoing on editor 2 will emit an `Edited` event only for that editor.
+//     editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
+//     assert_eq!(
+//         mem::take(&mut *events.borrow_mut()),
+//         [
+//             ("editor2", Event::Edited),
+//             ("editor1", Event::BufferEdited),
+//             ("editor2", Event::BufferEdited),
+//             ("editor1", Event::DirtyChanged),
+//             ("editor2", Event::DirtyChanged),
+//         ]
+//     );
+
+//     // Redoing on editor 2 will emit an `Edited` event only for that editor.
+//     editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
+//     assert_eq!(
+//         mem::take(&mut *events.borrow_mut()),
+//         [
+//             ("editor2", Event::Edited),
+//             ("editor1", Event::BufferEdited),
+//             ("editor2", Event::BufferEdited),
+//             ("editor1", Event::DirtyChanged),
+//             ("editor2", Event::DirtyChanged),
+//         ]
+//     );
+
+//     // No event is emitted when the mutation is a no-op.
+//     editor2.update(cx, |editor, cx| {
+//         editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
+
+//         editor.backspace(&Backspace, cx);
+//     });
+//     assert_eq!(mem::take(&mut *events.borrow_mut()), []);
+// }
+
+// #[gpui::test]
+// fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let mut now = Instant::now();
+//     let buffer = cx.add_model(|cx| language::Buffer::new(0, cx.model_id() as u64, "123456"));
+//     let group_interval = buffer.read_with(cx, |buffer, _| buffer.transaction_group_interval());
+//     let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
+//     let editor = cx
+//         .add_window(|cx| build_editor(buffer.clone(), cx))
+//         .root(cx);
+
+//     editor.update(cx, |editor, cx| {
+//         editor.start_transaction_at(now, cx);
+//         editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
+
+//         editor.insert("cd", cx);
+//         editor.end_transaction_at(now, cx);
+//         assert_eq!(editor.text(cx), "12cd56");
+//         assert_eq!(editor.selections.ranges(cx), vec![4..4]);
+
+//         editor.start_transaction_at(now, cx);
+//         editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
+//         editor.insert("e", cx);
+//         editor.end_transaction_at(now, cx);
+//         assert_eq!(editor.text(cx), "12cde6");
+//         assert_eq!(editor.selections.ranges(cx), vec![5..5]);
+
+//         now += group_interval + Duration::from_millis(1);
+//         editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
+
+//         // Simulate an edit in another editor
+//         buffer.update(cx, |buffer, cx| {
+//             buffer.start_transaction_at(now, cx);
+//             buffer.edit([(0..1, "a")], None, cx);
+//             buffer.edit([(1..1, "b")], None, cx);
+//             buffer.end_transaction_at(now, cx);
+//         });
+
+//         assert_eq!(editor.text(cx), "ab2cde6");
+//         assert_eq!(editor.selections.ranges(cx), vec![3..3]);
+
+//         // Last transaction happened past the group interval in a different editor.
+//         // Undo it individually and don't restore selections.
+//         editor.undo(&Undo, cx);
+//         assert_eq!(editor.text(cx), "12cde6");
+//         assert_eq!(editor.selections.ranges(cx), vec![2..2]);
+
+//         // First two transactions happened within the group interval in this editor.
+//         // Undo them together and restore selections.
+//         editor.undo(&Undo, cx);
+//         editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
+//         assert_eq!(editor.text(cx), "123456");
+//         assert_eq!(editor.selections.ranges(cx), vec![0..0]);
+
+//         // Redo the first two transactions together.
+//         editor.redo(&Redo, cx);
+//         assert_eq!(editor.text(cx), "12cde6");
+//         assert_eq!(editor.selections.ranges(cx), vec![5..5]);
+
+//         // Redo the last transaction on its own.
+//         editor.redo(&Redo, cx);
+//         assert_eq!(editor.text(cx), "ab2cde6");
+//         assert_eq!(editor.selections.ranges(cx), vec![6..6]);
+
+//         // Test empty transactions.
+//         editor.start_transaction_at(now, cx);
+//         editor.end_transaction_at(now, cx);
+//         editor.undo(&Undo, cx);
+//         assert_eq!(editor.text(cx), "12cde6");
+//     });
+// }
+
+// #[gpui::test]
+// fn test_ime_composition(cx: &mut TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let buffer = cx.add_model(|cx| {
+//         let mut buffer = language::Buffer::new(0, cx.model_id() as u64, "abcde");
+//         // Ensure automatic grouping doesn't occur.
+//         buffer.set_group_interval(Duration::ZERO);
+//         buffer
+//     });
+
+//     let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
+//     cx.add_window(|cx| {
+//         let mut editor = build_editor(buffer.clone(), cx);
+
+//         // Start a new IME composition.
+//         editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
+//         editor.replace_and_mark_text_in_range(Some(0..1), "á", None, cx);
+//         editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, cx);
+//         assert_eq!(editor.text(cx), "äbcde");
+//         assert_eq!(
+//             editor.marked_text_ranges(cx),
+//             Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
+//         );
+
+//         // Finalize IME composition.
+//         editor.replace_text_in_range(None, "ā", cx);
+//         assert_eq!(editor.text(cx), "ābcde");
+//         assert_eq!(editor.marked_text_ranges(cx), None);
+
+//         // IME composition edits are grouped and are undone/redone at once.
+//         editor.undo(&Default::default(), cx);
+//         assert_eq!(editor.text(cx), "abcde");
+//         assert_eq!(editor.marked_text_ranges(cx), None);
+//         editor.redo(&Default::default(), cx);
+//         assert_eq!(editor.text(cx), "ābcde");
+//         assert_eq!(editor.marked_text_ranges(cx), None);
+
+//         // Start a new IME composition.
+//         editor.replace_and_mark_text_in_range(Some(0..1), "à", None, cx);
+//         assert_eq!(
+//             editor.marked_text_ranges(cx),
+//             Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
+//         );
+
+//         // Undoing during an IME composition cancels it.
+//         editor.undo(&Default::default(), cx);
+//         assert_eq!(editor.text(cx), "ābcde");
+//         assert_eq!(editor.marked_text_ranges(cx), None);
+
+//         // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
+//         editor.replace_and_mark_text_in_range(Some(4..999), "è", None, cx);
+//         assert_eq!(editor.text(cx), "ābcdè");
+//         assert_eq!(
+//             editor.marked_text_ranges(cx),
+//             Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
+//         );
+
+//         // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
+//         editor.replace_text_in_range(Some(4..999), "ę", cx);
+//         assert_eq!(editor.text(cx), "ābcdę");
+//         assert_eq!(editor.marked_text_ranges(cx), None);
+
+//         // Start a new IME composition with multiple cursors.
+//         editor.change_selections(None, cx, |s| {
+//             s.select_ranges([
+//                 OffsetUtf16(1)..OffsetUtf16(1),
+//                 OffsetUtf16(3)..OffsetUtf16(3),
+//                 OffsetUtf16(5)..OffsetUtf16(5),
+//             ])
+//         });
+//         editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, cx);
+//         assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
+//         assert_eq!(
+//             editor.marked_text_ranges(cx),
+//             Some(vec![
+//                 OffsetUtf16(0)..OffsetUtf16(3),
+//                 OffsetUtf16(4)..OffsetUtf16(7),
+//                 OffsetUtf16(8)..OffsetUtf16(11)
+//             ])
+//         );
+
+//         // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
+//         editor.replace_and_mark_text_in_range(Some(1..2), "1", None, cx);
+//         assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
+//         assert_eq!(
+//             editor.marked_text_ranges(cx),
+//             Some(vec![
+//                 OffsetUtf16(1)..OffsetUtf16(2),
+//                 OffsetUtf16(5)..OffsetUtf16(6),
+//                 OffsetUtf16(9)..OffsetUtf16(10)
+//             ])
+//         );
+
+//         // Finalize IME composition with multiple cursors.
+//         editor.replace_text_in_range(Some(9..10), "2", cx);
+//         assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
+//         assert_eq!(editor.marked_text_ranges(cx), None);
+
+//         editor
+//     });
+// }
+
+// #[gpui::test]
+// fn test_selection_with_mouse(cx: &mut TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let editor = cx
+//         .add_window(|cx| {
+//             let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
+//             build_editor(buffer, cx)
+//         })
+//         .root(cx);
+//     editor.update(cx, |view, cx| {
+//         view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
+//     });
+//     assert_eq!(
+//         editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
+//         [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
+//     );
+
+//     editor.update(cx, |view, cx| {
+//         view.update_selection(DisplayPoint::new(3, 3), 0, Point<Pixels>::zero(), cx);
+//     });
+
+//     assert_eq!(
+//         editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
+//         [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
+//     );
+
+//     editor.update(cx, |view, cx| {
+//         view.update_selection(DisplayPoint::new(1, 1), 0, Point<Pixels>::zero(), cx);
+//     });
+
+//     assert_eq!(
+//         editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
+//         [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
+//     );
+
+//     editor.update(cx, |view, cx| {
+//         view.end_selection(cx);
+//         view.update_selection(DisplayPoint::new(3, 3), 0, Point<Pixels>::zero(), cx);
+//     });
+
+//     assert_eq!(
+//         editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
+//         [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
+//     );
+
+//     editor.update(cx, |view, cx| {
+//         view.begin_selection(DisplayPoint::new(3, 3), true, 1, cx);
+//         view.update_selection(DisplayPoint::new(0, 0), 0, Point<Pixels>::zero(), cx);
+//     });
+
+//     assert_eq!(
+//         editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
+//         [
+//             DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1),
+//             DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)
+//         ]
+//     );
+
+//     editor.update(cx, |view, cx| {
+//         view.end_selection(cx);
+//     });
+
+//     assert_eq!(
+//         editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
+//         [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)]
+//     );
+// }
+
+// #[gpui::test]
+// fn test_canceling_pending_selection(cx: &mut TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let view = cx
+//         .add_window(|cx| {
+//             let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
+//             build_editor(buffer, cx)
+//         })
+//         .root(cx);
+
+//     view.update(cx, |view, cx| {
+//         view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
+//         );
+//     });
+
+//     view.update(cx, |view, cx| {
+//         view.update_selection(DisplayPoint::new(3, 3), 0, Point<Pixels>::zero(), cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
+//         );
+//     });
+
+//     view.update(cx, |view, cx| {
+//         view.cancel(&Cancel, cx);
+//         view.update_selection(DisplayPoint::new(1, 1), 0, Point<Pixels>::zero(), cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
+//         );
+//     });
+// }
+
+// #[gpui::test]
+// fn test_clone(cx: &mut TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let (text, selection_ranges) = marked_text_ranges(
+//         indoc! {"
+//             one
+//             two
+//             threeˇ
+//             four
+//             fiveˇ
+//         "},
+//         true,
+//     );
+
+//     let editor = cx
+//         .add_window(|cx| {
+//             let buffer = MultiBuffer::build_simple(&text, cx);
+//             build_editor(buffer, cx)
+//         })
+//         .root(cx);
+
+//     editor.update(cx, |editor, cx| {
+//         editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
+//         editor.fold_ranges(
+//             [
+//                 Point::new(1, 0)..Point::new(2, 0),
+//                 Point::new(3, 0)..Point::new(4, 0),
+//             ],
+//             true,
+//             cx,
+//         );
+//     });
+
+//     let cloned_editor = editor
+//         .update(cx, |editor, cx| {
+//             cx.add_window(Default::default(), |cx| editor.clone(cx))
+//         })
+//         .root(cx);
+
+//     let snapshot = editor.update(cx, |e, cx| e.snapshot(cx));
+//     let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx));
+
+//     assert_eq!(
+//         cloned_editor.update(cx, |e, cx| e.display_text(cx)),
+//         editor.update(cx, |e, cx| e.display_text(cx))
+//     );
+//     assert_eq!(
+//         cloned_snapshot
+//             .folds_in_range(0..text.len())
+//             .collect::<Vec<_>>(),
+//         snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
+//     );
+//     assert_set_eq!(
+//         cloned_editor.read_with(cx, |editor, cx| editor.selections.ranges::<Point>(cx)),
+//         editor.read_with(cx, |editor, cx| editor.selections.ranges(cx))
+//     );
+//     assert_set_eq!(
+//         cloned_editor.update(cx, |e, cx| e.selections.display_ranges(cx)),
+//         editor.update(cx, |e, cx| e.selections.display_ranges(cx))
+//     );
+// }
+
+// #[gpui::test]
+// async fn test_navigation_history(cx: &mut TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     cx.set_global(DragAndDrop::<Workspace>::default());
+//     use workspace::item::Item;
+
+//     let fs = FakeFs::new(cx.background());
+//     let project = Project::test(fs, [], cx).await;
+//     let window = cx.add_window(|cx| Workspace::test_new(project, cx));
+//     let workspace = window.root(cx);
+//     let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
+//     window.add_view(cx, |cx| {
+//         let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
+//         let mut editor = build_editor(buffer.clone(), cx);
+//         let handle = cx.handle();
+//         editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
+
+//         fn pop_history(editor: &mut Editor, cx: &mut WindowContext) -> Option<NavigationEntry> {
+//             editor.nav_history.as_mut().unwrap().pop_backward(cx)
+//         }
+
+//         // Move the cursor a small distance.
+//         // Nothing is added to the navigation history.
+//         editor.change_selections(None, cx, |s| {
+//             s.select_display_ranges([DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)])
+//         });
+//         editor.change_selections(None, cx, |s| {
+//             s.select_display_ranges([DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)])
+//         });
+//         assert!(pop_history(&mut editor, cx).is_none());
+
+//         // Move the cursor a large distance.
+//         // The history can jump back to the previous position.
+//         editor.change_selections(None, cx, |s| {
+//             s.select_display_ranges([DisplayPoint::new(13, 0)..DisplayPoint::new(13, 3)])
+//         });
+//         let nav_entry = pop_history(&mut editor, cx).unwrap();
+//         editor.navigate(nav_entry.data.unwrap(), cx);
+//         assert_eq!(nav_entry.item.id(), cx.view_id());
+//         assert_eq!(
+//             editor.selections.display_ranges(cx),
+//             &[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)]
+//         );
+//         assert!(pop_history(&mut editor, cx).is_none());
+
+//         // Move the cursor a small distance via the mouse.
+//         // Nothing is added to the navigation history.
+//         editor.begin_selection(DisplayPoint::new(5, 0), false, 1, cx);
+//         editor.end_selection(cx);
+//         assert_eq!(
+//             editor.selections.display_ranges(cx),
+//             &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
+//         );
+//         assert!(pop_history(&mut editor, cx).is_none());
+
+//         // Move the cursor a large distance via the mouse.
+//         // The history can jump back to the previous position.
+//         editor.begin_selection(DisplayPoint::new(15, 0), false, 1, cx);
+//         editor.end_selection(cx);
+//         assert_eq!(
+//             editor.selections.display_ranges(cx),
+//             &[DisplayPoint::new(15, 0)..DisplayPoint::new(15, 0)]
+//         );
+//         let nav_entry = pop_history(&mut editor, cx).unwrap();
+//         editor.navigate(nav_entry.data.unwrap(), cx);
+//         assert_eq!(nav_entry.item.id(), cx.view_id());
+//         assert_eq!(
+//             editor.selections.display_ranges(cx),
+//             &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
+//         );
+//         assert!(pop_history(&mut editor, cx).is_none());
+
+//         // Set scroll position to check later
+//         editor.set_scroll_position(Point<Pixels>::new(5.5, 5.5), cx);
+//         let original_scroll_position = editor.scroll_manager.anchor();
+
+//         // Jump to the end of the document and adjust scroll
+//         editor.move_to_end(&MoveToEnd, cx);
+//         editor.set_scroll_position(Point<Pixels>::new(-2.5, -0.5), cx);
+//         assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
+
+//         let nav_entry = pop_history(&mut editor, cx).unwrap();
+//         editor.navigate(nav_entry.data.unwrap(), cx);
+//         assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
+
+//         // Ensure we don't panic when navigation data contains invalid anchors *and* points.
+//         let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
+//         invalid_anchor.text_anchor.buffer_id = Some(999);
+//         let invalid_point = Point::new(9999, 0);
+//         editor.navigate(
+//             Box::new(NavigationData {
+//                 cursor_anchor: invalid_anchor,
+//                 cursor_position: invalid_point,
+//                 scroll_anchor: ScrollAnchor {
+//                     anchor: invalid_anchor,
+//                     offset: Default::default(),
+//                 },
+//                 scroll_top_row: invalid_point.row,
+//             }),
+//             cx,
+//         );
+//         assert_eq!(
+//             editor.selections.display_ranges(cx),
+//             &[editor.max_point(cx)..editor.max_point(cx)]
+//         );
+//         assert_eq!(
+//             editor.scroll_position(cx),
+//             vec2f(0., editor.max_point(cx).row() as f32)
+//         );
+
+//         editor
+//     });
+// }
+
+// #[gpui::test]
+// fn test_cancel(cx: &mut TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let view = cx
+//         .add_window(|cx| {
+//             let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
+//             build_editor(buffer, cx)
+//         })
+//         .root(cx);
+
+//     view.update(cx, |view, cx| {
+//         view.begin_selection(DisplayPoint::new(3, 4), false, 1, cx);
+//         view.update_selection(DisplayPoint::new(1, 1), 0, Point<Pixels>::zero(), cx);
+//         view.end_selection(cx);
+
+//         view.begin_selection(DisplayPoint::new(0, 1), true, 1, cx);
+//         view.update_selection(DisplayPoint::new(0, 3), 0, Point<Pixels>::zero(), cx);
+//         view.end_selection(cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             [
+//                 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
+//                 DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1),
+//             ]
+//         );
+//     });
+
+//     view.update(cx, |view, cx| {
+//         view.cancel(&Cancel, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             [DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1)]
+//         );
+//     });
+
+//     view.update(cx, |view, cx| {
+//         view.cancel(&Cancel, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             [DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1)]
+//         );
+//     });
+// }
+
+// #[gpui::test]
+// fn test_fold_action(cx: &mut TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let view = cx
+//         .add_window(|cx| {
+//             let buffer = MultiBuffer::build_simple(
+//                 &"
+//                 impl Foo {
+//                     // Hello!
+
+//                     fn a() {
+//                         1
+//                     }
+
+//                     fn b() {
+//                         2
+//                     }
+
+//                     fn c() {
+//                         3
+//                     }
+//                 }
+//             "
+//                 .unindent(),
+//                 cx,
+//             );
+//             build_editor(buffer.clone(), cx)
+//         })
+//         .root(cx);
+
+//     view.update(cx, |view, cx| {
+//         view.change_selections(None, cx, |s| {
+//             s.select_display_ranges([DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)]);
+//         });
+//         view.fold(&Fold, cx);
+//         assert_eq!(
+//             view.display_text(cx),
+//             "
+//                 impl Foo {
+//                     // Hello!
+
+//                     fn a() {
+//                         1
+//                     }
+
+//                     fn b() {⋯
+//                     }
+
+//                     fn c() {⋯
+//                     }
+//                 }
+//             "
+//             .unindent(),
+//         );
+
+//         view.fold(&Fold, cx);
+//         assert_eq!(
+//             view.display_text(cx),
+//             "
+//                 impl Foo {⋯
+//                 }
+//             "
+//             .unindent(),
+//         );
+
+//         view.unfold_lines(&UnfoldLines, cx);
+//         assert_eq!(
+//             view.display_text(cx),
+//             "
+//                 impl Foo {
+//                     // Hello!
+
+//                     fn a() {
+//                         1
+//                     }
+
+//                     fn b() {⋯
+//                     }
+
+//                     fn c() {⋯
+//                     }
+//                 }
+//             "
+//             .unindent(),
+//         );
+
+//         view.unfold_lines(&UnfoldLines, cx);
+//         assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
+//     });
+// }
+
+// #[gpui::test]
+// fn test_move_cursor(cx: &mut TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
+//     let view = cx
+//         .add_window(|cx| build_editor(buffer.clone(), cx))
+//         .root(cx);
+
+//     buffer.update(cx, |buffer, cx| {
+//         buffer.edit(
+//             vec![
+//                 (Point::new(1, 0)..Point::new(1, 0), "\t"),
+//                 (Point::new(1, 1)..Point::new(1, 1), "\t"),
+//             ],
+//             None,
+//             cx,
+//         );
+//     });
+//     view.update(cx, |view, cx| {
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
+//         );
+
+//         view.move_down(&MoveDown, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
+//         );
+
+//         view.move_right(&MoveRight, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)]
+//         );
+
+//         view.move_left(&MoveLeft, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
+//         );
+
+//         view.move_up(&MoveUp, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
+//         );
+
+//         view.move_to_end(&MoveToEnd, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[DisplayPoint::new(5, 6)..DisplayPoint::new(5, 6)]
+//         );
+
+//         view.move_to_beginning(&MoveToBeginning, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
+//         );
+
+//         view.change_selections(None, cx, |s| {
+//             s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]);
+//         });
+//         view.select_to_beginning(&SelectToBeginning, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 0)]
+//         );
+
+//         view.select_to_end(&SelectToEnd, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[DisplayPoint::new(0, 1)..DisplayPoint::new(5, 6)]
+//         );
+//     });
+// }
+
+// #[gpui::test]
+// fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let view = cx
+//         .add_window(|cx| {
+//             let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε", cx);
+//             build_editor(buffer.clone(), cx)
+//         })
+//         .root(cx);
+
+//     assert_eq!('ⓐ'.len_utf8(), 3);
+//     assert_eq!('α'.len_utf8(), 2);
+
+//     view.update(cx, |view, cx| {
+//         view.fold_ranges(
+//             vec![
+//                 Point::new(0, 6)..Point::new(0, 12),
+//                 Point::new(1, 2)..Point::new(1, 4),
+//                 Point::new(2, 4)..Point::new(2, 8),
+//             ],
+//             true,
+//             cx,
+//         );
+//         assert_eq!(view.display_text(cx), "ⓐⓑ⋯ⓔ\nab⋯e\nαβ⋯ε");
+
+//         view.move_right(&MoveRight, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[empty_range(0, "ⓐ".len())]
+//         );
+//         view.move_right(&MoveRight, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[empty_range(0, "ⓐⓑ".len())]
+//         );
+//         view.move_right(&MoveRight, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[empty_range(0, "ⓐⓑ⋯".len())]
+//         );
+
+//         view.move_down(&MoveDown, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[empty_range(1, "ab⋯e".len())]
+//         );
+//         view.move_left(&MoveLeft, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[empty_range(1, "ab⋯".len())]
+//         );
+//         view.move_left(&MoveLeft, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[empty_range(1, "ab".len())]
+//         );
+//         view.move_left(&MoveLeft, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[empty_range(1, "a".len())]
+//         );
+
+//         view.move_down(&MoveDown, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[empty_range(2, "α".len())]
+//         );
+//         view.move_right(&MoveRight, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[empty_range(2, "αβ".len())]
+//         );
+//         view.move_right(&MoveRight, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[empty_range(2, "αβ⋯".len())]
+//         );
+//         view.move_right(&MoveRight, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[empty_range(2, "αβ⋯ε".len())]
+//         );
+
+//         view.move_up(&MoveUp, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[empty_range(1, "ab⋯e".len())]
+//         );
+//         view.move_down(&MoveDown, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[empty_range(2, "αβ⋯ε".len())]
+//         );
+//         view.move_up(&MoveUp, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[empty_range(1, "ab⋯e".len())]
+//         );
+
+//         view.move_up(&MoveUp, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[empty_range(0, "ⓐⓑ".len())]
+//         );
+//         view.move_left(&MoveLeft, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[empty_range(0, "ⓐ".len())]
+//         );
+//         view.move_left(&MoveLeft, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[empty_range(0, "".len())]
+//         );
+//     });
+// }
+
+// #[gpui::test]
+// fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let view = cx
+//         .add_window(|cx| {
+//             let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
+//             build_editor(buffer.clone(), cx)
+//         })
+//         .root(cx);
+//     view.update(cx, |view, cx| {
+//         view.change_selections(None, cx, |s| {
+//             s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
+//         });
+//         view.move_down(&MoveDown, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[empty_range(1, "abcd".len())]
+//         );
+
+//         view.move_down(&MoveDown, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[empty_range(2, "αβγ".len())]
+//         );
+
+//         view.move_down(&MoveDown, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[empty_range(3, "abcd".len())]
+//         );
+
+//         view.move_down(&MoveDown, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
+//         );
+
+//         view.move_up(&MoveUp, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[empty_range(3, "abcd".len())]
+//         );
+
+//         view.move_up(&MoveUp, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[empty_range(2, "αβγ".len())]
+//         );
+//     });
+// }
+
+// #[gpui::test]
+// fn test_beginning_end_of_line(cx: &mut TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let view = cx
+//         .add_window(|cx| {
+//             let buffer = MultiBuffer::build_simple("abc\n  def", cx);
+//             build_editor(buffer, cx)
+//         })
+//         .root(cx);
+//     view.update(cx, |view, cx| {
+//         view.change_selections(None, cx, |s| {
+//             s.select_display_ranges([
+//                 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
+//                 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
+//             ]);
+//         });
+//     });
+
+//     view.update(cx, |view, cx| {
+//         view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[
+//                 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
+//                 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
+//             ]
+//         );
+//     });
+
+//     view.update(cx, |view, cx| {
+//         view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[
+//                 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
+//                 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
+//             ]
+//         );
+//     });
+
+//     view.update(cx, |view, cx| {
+//         view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[
+//                 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
+//                 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
+//             ]
+//         );
+//     });
+
+//     view.update(cx, |view, cx| {
+//         view.move_to_end_of_line(&MoveToEndOfLine, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[
+//                 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
+//                 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
+//             ]
+//         );
+//     });
+
+//     // Moving to the end of line again is a no-op.
+//     view.update(cx, |view, cx| {
+//         view.move_to_end_of_line(&MoveToEndOfLine, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[
+//                 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
+//                 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
+//             ]
+//         );
+//     });
+
+//     view.update(cx, |view, cx| {
+//         view.move_left(&MoveLeft, cx);
+//         view.select_to_beginning_of_line(
+//             &SelectToBeginningOfLine {
+//                 stop_at_soft_wraps: true,
+//             },
+//             cx,
+//         );
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[
+//                 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
+//                 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
+//             ]
+//         );
+//     });
+
+//     view.update(cx, |view, cx| {
+//         view.select_to_beginning_of_line(
+//             &SelectToBeginningOfLine {
+//                 stop_at_soft_wraps: true,
+//             },
+//             cx,
+//         );
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[
+//                 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
+//                 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 0),
+//             ]
+//         );
+//     });
+
+//     view.update(cx, |view, cx| {
+//         view.select_to_beginning_of_line(
+//             &SelectToBeginningOfLine {
+//                 stop_at_soft_wraps: true,
+//             },
+//             cx,
+//         );
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[
+//                 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
+//                 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
+//             ]
+//         );
+//     });
+
+//     view.update(cx, |view, cx| {
+//         view.select_to_end_of_line(
+//             &SelectToEndOfLine {
+//                 stop_at_soft_wraps: true,
+//             },
+//             cx,
+//         );
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[
+//                 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
+//                 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 5),
+//             ]
+//         );
+//     });
+
+//     view.update(cx, |view, cx| {
+//         view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
+//         assert_eq!(view.display_text(cx), "ab\n  de");
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[
+//                 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
+//                 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
+//             ]
+//         );
+//     });
+
+//     view.update(cx, |view, cx| {
+//         view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
+//         assert_eq!(view.display_text(cx), "\n");
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[
+//                 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
+//                 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
+//             ]
+//         );
+//     });
+// }
+
+// #[gpui::test]
+// fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let view = cx
+//         .add_window(|cx| {
+//             let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
+//             build_editor(buffer, cx)
+//         })
+//         .root(cx);
+//     view.update(cx, |view, cx| {
+//         view.change_selections(None, cx, |s| {
+//             s.select_display_ranges([
+//                 DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11),
+//                 DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4),
+//             ])
+//         });
+
+//         view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
+//         assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
+
+//         view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
+//         assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", view, cx);
+
+//         view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
+//         assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", view, cx);
+
+//         view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
+//         assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
+
+//         view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
+//         assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", view, cx);
+
+//         view.move_to_next_word_end(&MoveToNextWordEnd, cx);
+//         assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", view, cx);
+
+//         view.move_to_next_word_end(&MoveToNextWordEnd, cx);
+//         assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", view, cx);
+
+//         view.move_to_next_word_end(&MoveToNextWordEnd, cx);
+//         assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", view, cx);
+
+//         view.move_right(&MoveRight, cx);
+//         view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
+//         assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
+
+//         view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
+//         assert_selection_ranges("use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}", view, cx);
+
+//         view.select_to_next_word_end(&SelectToNextWordEnd, cx);
+//         assert_selection_ranges("use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}", view, cx);
+//     });
+// }
+
+// #[gpui::test]
+// fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let view = cx
+//         .add_window(|cx| {
+//             let buffer =
+//                 MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
+//             build_editor(buffer, cx)
+//         })
+//         .root(cx);
+
+//     view.update(cx, |view, cx| {
+//         view.set_wrap_width(Some(140.), cx);
+//         assert_eq!(
+//             view.display_text(cx),
+//             "use one::{\n    two::three::\n    four::five\n};"
+//         );
+
+//         view.change_selections(None, cx, |s| {
+//             s.select_display_ranges([DisplayPoint::new(1, 7)..DisplayPoint::new(1, 7)]);
+//         });
+
+//         view.move_to_next_word_end(&MoveToNextWordEnd, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[DisplayPoint::new(1, 9)..DisplayPoint::new(1, 9)]
+//         );
+
+//         view.move_to_next_word_end(&MoveToNextWordEnd, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
+//         );
+
+//         view.move_to_next_word_end(&MoveToNextWordEnd, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
+//         );
+
+//         view.move_to_next_word_end(&MoveToNextWordEnd, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[DisplayPoint::new(2, 8)..DisplayPoint::new(2, 8)]
+//         );
+
+//         view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
+//         );
+
+//         view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
+//         );
+//     });
+// }
+
+// #[gpui::test]
+// async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |_| {});
+//     let mut cx = EditorTestContext::new(cx).await;
+
+//     let line_height = cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache()));
+//     let window = cx.window;
+//     window.simulate_resize(vec2f(100., 4. * line_height), &mut cx);
+
+//     cx.set_state(
+//         &r#"ˇone
+//         two
+
+//         three
+//         fourˇ
+//         five
+
+//         six"#
+//             .unindent(),
+//     );
+
+//     cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
+//     cx.assert_editor_state(
+//         &r#"one
+//         two
+//         ˇ
+//         three
+//         four
+//         five
+//         ˇ
+//         six"#
+//             .unindent(),
+//     );
+
+//     cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
+//     cx.assert_editor_state(
+//         &r#"one
+//         two
+
+//         three
+//         four
+//         five
+//         ˇ
+//         sixˇ"#
+//             .unindent(),
+//     );
+
+//     cx.update_editor(|editor, cx| editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, cx));
+//     cx.assert_editor_state(
+//         &r#"one
+//         two
+
+//         three
+//         four
+//         five
+
+//         sixˇ"#
+//             .unindent(),
+//     );
+
+//     cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
+//     cx.assert_editor_state(
+//         &r#"one
+//         two
+
+//         three
+//         four
+//         five
+//         ˇ
+//         six"#
+//             .unindent(),
+//     );
+
+//     cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
+//     cx.assert_editor_state(
+//         &r#"one
+//         two
+//         ˇ
+//         three
+//         four
+//         five
+
+//         six"#
+//             .unindent(),
+//     );
+
+//     cx.update_editor(|editor, cx| editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, cx));
+//     cx.assert_editor_state(
+//         &r#"ˇone
+//         two
+
+//         three
+//         four
+//         five
+
+//         six"#
+//             .unindent(),
+//     );
+// }
+
+// #[gpui::test]
+// async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |_| {});
+//     let mut cx = EditorTestContext::new(cx).await;
+//     let line_height = cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache()));
+//     let window = cx.window;
+//     window.simulate_resize(vec2f(1000., 4. * line_height + 0.5), &mut cx);
+
+//     cx.set_state(
+//         &r#"ˇone
+//         two
+//         three
+//         four
+//         five
+//         six
+//         seven
+//         eight
+//         nine
+//         ten
+//         "#,
+//     );
+
+//     cx.update_editor(|editor, cx| {
+//         assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 0.));
+//         editor.scroll_screen(&ScrollAmount::Page(1.), cx);
+//         assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 3.));
+//         editor.scroll_screen(&ScrollAmount::Page(1.), cx);
+//         assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 6.));
+//         editor.scroll_screen(&ScrollAmount::Page(-1.), cx);
+//         assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 3.));
+
+//         editor.scroll_screen(&ScrollAmount::Page(-0.5), cx);
+//         assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 1.));
+//         editor.scroll_screen(&ScrollAmount::Page(0.5), cx);
+//         assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 3.));
+//     });
+// }
+
+// #[gpui::test]
+// async fn test_autoscroll(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |_| {});
+//     let mut cx = EditorTestContext::new(cx).await;
+
+//     let line_height = cx.update_editor(|editor, cx| {
+//         editor.set_vertical_scroll_margin(2, cx);
+//         editor.style(cx).text.line_height(cx.font_cache())
+//     });
+
+//     let window = cx.window;
+//     window.simulate_resize(vec2f(1000., 6.0 * line_height), &mut cx);
+
+//     cx.set_state(
+//         &r#"ˇone
+//             two
+//             three
+//             four
+//             five
+//             six
+//             seven
+//             eight
+//             nine
+//             ten
+//         "#,
+//     );
+//     cx.update_editor(|editor, cx| {
+//         assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 0.0));
+//     });
+
+//     // Add a cursor below the visible area. Since both cursors cannot fit
+//     // on screen, the editor autoscrolls to reveal the newest cursor, and
+//     // allows the vertical scroll margin below that cursor.
+//     cx.update_editor(|editor, cx| {
+//         editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
+//             selections.select_ranges([
+//                 Point::new(0, 0)..Point::new(0, 0),
+//                 Point::new(6, 0)..Point::new(6, 0),
+//             ]);
+//         })
+//     });
+//     cx.update_editor(|editor, cx| {
+//         assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 3.0));
+//     });
+
+//     // Move down. The editor cursor scrolls down to track the newest cursor.
+//     cx.update_editor(|editor, cx| {
+//         editor.move_down(&Default::default(), cx);
+//     });
+//     cx.update_editor(|editor, cx| {
+//         assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 4.0));
+//     });
+
+//     // Add a cursor above the visible area. Since both cursors fit on screen,
+//     // the editor scrolls to show both.
+//     cx.update_editor(|editor, cx| {
+//         editor.change_selections(Some(Autoscroll::fit()), cx, |selections| {
+//             selections.select_ranges([
+//                 Point::new(1, 0)..Point::new(1, 0),
+//                 Point::new(6, 0)..Point::new(6, 0),
+//             ]);
+//         })
+//     });
+//     cx.update_editor(|editor, cx| {
+//         assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 1.0));
+//     });
+// }
+
+// #[gpui::test]
+// async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |_| {});
+//     let mut cx = EditorTestContext::new(cx).await;
+
+//     let line_height = cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache()));
+//     let window = cx.window;
+//     window.simulate_resize(vec2f(100., 4. * line_height), &mut cx);
+
+//     cx.set_state(
+//         &r#"
+//         ˇone
+//         two
+//         threeˇ
+//         four
+//         five
+//         six
+//         seven
+//         eight
+//         nine
+//         ten
+//         "#
+//         .unindent(),
+//     );
+
+//     cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
+//     cx.assert_editor_state(
+//         &r#"
+//         one
+//         two
+//         three
+//         ˇfour
+//         five
+//         sixˇ
+//         seven
+//         eight
+//         nine
+//         ten
+//         "#
+//         .unindent(),
+//     );
+
+//     cx.update_editor(|editor, cx| editor.move_page_down(&MovePageDown::default(), cx));
+//     cx.assert_editor_state(
+//         &r#"
+//         one
+//         two
+//         three
+//         four
+//         five
+//         six
+//         ˇseven
+//         eight
+//         nineˇ
+//         ten
+//         "#
+//         .unindent(),
+//     );
+
+//     cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
+//     cx.assert_editor_state(
+//         &r#"
+//         one
+//         two
+//         three
+//         ˇfour
+//         five
+//         sixˇ
+//         seven
+//         eight
+//         nine
+//         ten
+//         "#
+//         .unindent(),
+//     );
+
+//     cx.update_editor(|editor, cx| editor.move_page_up(&MovePageUp::default(), cx));
+//     cx.assert_editor_state(
+//         &r#"
+//         ˇone
+//         two
+//         threeˇ
+//         four
+//         five
+//         six
+//         seven
+//         eight
+//         nine
+//         ten
+//         "#
+//         .unindent(),
+//     );
+
+//     // Test select collapsing
+//     cx.update_editor(|editor, cx| {
+//         editor.move_page_down(&MovePageDown::default(), cx);
+//         editor.move_page_down(&MovePageDown::default(), cx);
+//         editor.move_page_down(&MovePageDown::default(), cx);
+//     });
+//     cx.assert_editor_state(
+//         &r#"
+//         one
+//         two
+//         three
+//         four
+//         five
+//         six
+//         seven
+//         eight
+//         nine
+//         ˇten
+//         ˇ"#
+//         .unindent(),
+//     );
+// }
+
+// #[gpui::test]
+// async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |_| {});
+//     let mut cx = EditorTestContext::new(cx).await;
+//     cx.set_state("one «two threeˇ» four");
+//     cx.update_editor(|editor, cx| {
+//         editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
+//         assert_eq!(editor.text(cx), " four");
+//     });
+// }
+
+// #[gpui::test]
+// fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let view = cx
+//         .add_window(|cx| {
+//             let buffer = MultiBuffer::build_simple("one two three four", cx);
+//             build_editor(buffer.clone(), cx)
+//         })
+//         .root(cx);
+
+//     view.update(cx, |view, cx| {
+//         view.change_selections(None, cx, |s| {
+//             s.select_display_ranges([
+//                 // an empty selection - the preceding word fragment is deleted
+//                 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
+//                 // characters selected - they are deleted
+//                 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 12),
+//             ])
+//         });
+//         view.delete_to_previous_word_start(&DeleteToPreviousWordStart, cx);
+//         assert_eq!(view.buffer.read(cx).read(cx).text(), "e two te four");
+//     });
+
+//     view.update(cx, |view, cx| {
+//         view.change_selections(None, cx, |s| {
+//             s.select_display_ranges([
+//                 // an empty selection - the following word fragment is deleted
+//                 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
+//                 // characters selected - they are deleted
+//                 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 10),
+//             ])
+//         });
+//         view.delete_to_next_word_end(&DeleteToNextWordEnd, cx);
+//         assert_eq!(view.buffer.read(cx).read(cx).text(), "e t te our");
+//     });
+// }
+
+// #[gpui::test]
+// fn test_newline(cx: &mut TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let view = cx
+//         .add_window(|cx| {
+//             let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
+//             build_editor(buffer.clone(), cx)
+//         })
+//         .root(cx);
+
+//     view.update(cx, |view, cx| {
+//         view.change_selections(None, cx, |s| {
+//             s.select_display_ranges([
+//                 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
+//                 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
+//                 DisplayPoint::new(1, 6)..DisplayPoint::new(1, 6),
+//             ])
+//         });
+
+//         view.newline(&Newline, cx);
+//         assert_eq!(view.text(cx), "aa\naa\n  \n    bb\n    bb\n");
+//     });
+// }
+
+// #[gpui::test]
+// fn test_newline_with_old_selections(cx: &mut TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let editor = cx
+//         .add_window(|cx| {
+//             let buffer = MultiBuffer::build_simple(
+//                 "
+//                 a
+//                 b(
+//                     X
+//                 )
+//                 c(
+//                     X
+//                 )
+//             "
+//                 .unindent()
+//                 .as_str(),
+//                 cx,
+//             );
+//             let mut editor = build_editor(buffer.clone(), cx);
+//             editor.change_selections(None, cx, |s| {
+//                 s.select_ranges([
+//                     Point::new(2, 4)..Point::new(2, 5),
+//                     Point::new(5, 4)..Point::new(5, 5),
+//                 ])
+//             });
+//             editor
+//         })
+//         .root(cx);
+
+//     editor.update(cx, |editor, cx| {
+//         // Edit the buffer directly, deleting ranges surrounding the editor's selections
+//         editor.buffer.update(cx, |buffer, cx| {
+//             buffer.edit(
+//                 [
+//                     (Point::new(1, 2)..Point::new(3, 0), ""),
+//                     (Point::new(4, 2)..Point::new(6, 0), ""),
+//                 ],
+//                 None,
+//                 cx,
+//             );
+//             assert_eq!(
+//                 buffer.read(cx).text(),
+//                 "
+//                     a
+//                     b()
+//                     c()
+//                 "
+//                 .unindent()
+//             );
+//         });
+//         assert_eq!(
+//             editor.selections.ranges(cx),
+//             &[
+//                 Point::new(1, 2)..Point::new(1, 2),
+//                 Point::new(2, 2)..Point::new(2, 2),
+//             ],
+//         );
+
+//         editor.newline(&Newline, cx);
+//         assert_eq!(
+//             editor.text(cx),
+//             "
+//                 a
+//                 b(
+//                 )
+//                 c(
+//                 )
+//             "
+//             .unindent()
+//         );
+
+//         // The selections are moved after the inserted newlines
+//         assert_eq!(
+//             editor.selections.ranges(cx),
+//             &[
+//                 Point::new(2, 0)..Point::new(2, 0),
+//                 Point::new(4, 0)..Point::new(4, 0),
+//             ],
+//         );
+//     });
+// }
+
+// #[gpui::test]
+// async fn test_newline_above(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |settings| {
+//         settings.defaults.tab_size = NonZeroU32::new(4)
+//     });
+
+//     let language = Arc::new(
+//         Language::new(
+//             LanguageConfig::default(),
+//             Some(tree_sitter_rust::language()),
+//         )
+//         .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
+//         .unwrap(),
+//     );
+
+//     let mut cx = EditorTestContext::new(cx).await;
+//     cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
+//     cx.set_state(indoc! {"
+//         const a: ˇA = (
+//             (ˇ
+//                 «const_functionˇ»(ˇ),
+//                 so«mˇ»et«hˇ»ing_ˇelse,ˇ
+//             )ˇ
+//         ˇ);ˇ
+//     "});
+
+//     cx.update_editor(|e, cx| e.newline_above(&NewlineAbove, cx));
+//     cx.assert_editor_state(indoc! {"
+//         ˇ
+//         const a: A = (
+//             ˇ
+//             (
+//                 ˇ
+//                 ˇ
+//                 const_function(),
+//                 ˇ
+//                 ˇ
+//                 ˇ
+//                 ˇ
+//                 something_else,
+//                 ˇ
+//             )
+//             ˇ
+//             ˇ
+//         );
+//     "});
+// }
+
+// #[gpui::test]
+// async fn test_newline_below(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |settings| {
+//         settings.defaults.tab_size = NonZeroU32::new(4)
+//     });
+
+//     let language = Arc::new(
+//         Language::new(
+//             LanguageConfig::default(),
+//             Some(tree_sitter_rust::language()),
+//         )
+//         .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
+//         .unwrap(),
+//     );
+
+//     let mut cx = EditorTestContext::new(cx).await;
+//     cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
+//     cx.set_state(indoc! {"
+//         const a: ˇA = (
+//             (ˇ
+//                 «const_functionˇ»(ˇ),
+//                 so«mˇ»et«hˇ»ing_ˇelse,ˇ
+//             )ˇ
+//         ˇ);ˇ
+//     "});
+
+//     cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
+//     cx.assert_editor_state(indoc! {"
+//         const a: A = (
+//             ˇ
+//             (
+//                 ˇ
+//                 const_function(),
+//                 ˇ
+//                 ˇ
+//                 something_else,
+//                 ˇ
+//                 ˇ
+//                 ˇ
+//                 ˇ
+//             )
+//             ˇ
+//         );
+//         ˇ
+//         ˇ
+//     "});
+// }
+
+// #[gpui::test]
+// async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |settings| {
+//         settings.defaults.tab_size = NonZeroU32::new(4)
+//     });
+
+//     let language = Arc::new(Language::new(
+//         LanguageConfig {
+//             line_comment: Some("//".into()),
+//             ..LanguageConfig::default()
+//         },
+//         None,
+//     ));
+//     {
+//         let mut cx = EditorTestContext::new(cx).await;
+//         cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
+//         cx.set_state(indoc! {"
+//         // Fooˇ
+//     "});
+
+//         cx.update_editor(|e, cx| e.newline(&Newline, cx));
+//         cx.assert_editor_state(indoc! {"
+//         // Foo
+//         //ˇ
+//     "});
+//         // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
+//         cx.set_state(indoc! {"
+//         ˇ// Foo
+//     "});
+//         cx.update_editor(|e, cx| e.newline(&Newline, cx));
+//         cx.assert_editor_state(indoc! {"
+
+//         ˇ// Foo
+//     "});
+//     }
+//     // Ensure that comment continuations can be disabled.
+//     update_test_language_settings(cx, |settings| {
+//         settings.defaults.extend_comment_on_newline = Some(false);
+//     });
+//     let mut cx = EditorTestContext::new(cx).await;
+//     cx.set_state(indoc! {"
+//         // Fooˇ
+//     "});
+//     cx.update_editor(|e, cx| e.newline(&Newline, cx));
+//     cx.assert_editor_state(indoc! {"
+//         // Foo
+//         ˇ
+//     "});
+// }
+
+// #[gpui::test]
+// fn test_insert_with_old_selections(cx: &mut TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let editor = cx
+//         .add_window(|cx| {
+//             let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
+//             let mut editor = build_editor(buffer.clone(), cx);
+//             editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
+//             editor
+//         })
+//         .root(cx);
+
+//     editor.update(cx, |editor, cx| {
+//         // Edit the buffer directly, deleting ranges surrounding the editor's selections
+//         editor.buffer.update(cx, |buffer, cx| {
+//             buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
+//             assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
+//         });
+//         assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
+
+//         editor.insert("Z", cx);
+//         assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
+
+//         // The selections are moved after the inserted characters
+//         assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
+//     });
+// }
+
+// #[gpui::test]
+// async fn test_tab(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |settings| {
+//         settings.defaults.tab_size = NonZeroU32::new(3)
+//     });
+
+//     let mut cx = EditorTestContext::new(cx).await;
+//     cx.set_state(indoc! {"
+//         ˇabˇc
+//         ˇ🏀ˇ🏀ˇefg
+//         dˇ
+//     "});
+//     cx.update_editor(|e, cx| e.tab(&Tab, cx));
+//     cx.assert_editor_state(indoc! {"
+//            ˇab ˇc
+//            ˇ🏀  ˇ🏀  ˇefg
+//         d  ˇ
+//     "});
+
+//     cx.set_state(indoc! {"
+//         a
+//         «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
+//     "});
+//     cx.update_editor(|e, cx| e.tab(&Tab, cx));
+//     cx.assert_editor_state(indoc! {"
+//         a
+//            «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
+//     "});
+// }
+
+// #[gpui::test]
+// async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let mut cx = EditorTestContext::new(cx).await;
+//     let language = Arc::new(
+//         Language::new(
+//             LanguageConfig::default(),
+//             Some(tree_sitter_rust::language()),
+//         )
+//         .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
+//         .unwrap(),
+//     );
+//     cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
+
+//     // cursors that are already at the suggested indent level insert
+//     // a soft tab. cursors that are to the left of the suggested indent
+//     // auto-indent their line.
+//     cx.set_state(indoc! {"
+//         ˇ
+//         const a: B = (
+//             c(
+//                 d(
+//         ˇ
+//                 )
+//         ˇ
+//         ˇ    )
+//         );
+//     "});
+//     cx.update_editor(|e, cx| e.tab(&Tab, cx));
+//     cx.assert_editor_state(indoc! {"
+//             ˇ
+//         const a: B = (
+//             c(
+//                 d(
+//                     ˇ
+//                 )
+//                 ˇ
+//             ˇ)
+//         );
+//     "});
+
+//     // handle auto-indent when there are multiple cursors on the same line
+//     cx.set_state(indoc! {"
+//         const a: B = (
+//             c(
+//         ˇ    ˇ
+//         ˇ    )
+//         );
+//     "});
+//     cx.update_editor(|e, cx| e.tab(&Tab, cx));
+//     cx.assert_editor_state(indoc! {"
+//         const a: B = (
+//             c(
+//                 ˇ
+//             ˇ)
+//         );
+//     "});
+// }
+
+// #[gpui::test]
+// async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |settings| {
+//         settings.defaults.tab_size = NonZeroU32::new(4)
+//     });
+
+//     let language = Arc::new(
+//         Language::new(
+//             LanguageConfig::default(),
+//             Some(tree_sitter_rust::language()),
+//         )
+//         .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
+//         .unwrap(),
+//     );
+
+//     let mut cx = EditorTestContext::new(cx).await;
+//     cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
+//     cx.set_state(indoc! {"
+//         fn a() {
+//             if b {
+//         \t ˇc
+//             }
+//         }
+//     "});
+
+//     cx.update_editor(|e, cx| e.tab(&Tab, cx));
+//     cx.assert_editor_state(indoc! {"
+//         fn a() {
+//             if b {
+//                 ˇc
+//             }
+//         }
+//     "});
+// }
+
+// #[gpui::test]
+// async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |settings| {
+//         settings.defaults.tab_size = NonZeroU32::new(4);
+//     });
+
+//     let mut cx = EditorTestContext::new(cx).await;
+
+//     cx.set_state(indoc! {"
+//           «oneˇ» «twoˇ»
+//         three
+//          four
+//     "});
+//     cx.update_editor(|e, cx| e.tab(&Tab, cx));
+//     cx.assert_editor_state(indoc! {"
+//             «oneˇ» «twoˇ»
+//         three
+//          four
+//     "});
+
+//     cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
+//     cx.assert_editor_state(indoc! {"
+//         «oneˇ» «twoˇ»
+//         three
+//          four
+//     "});
+
+//     // select across line ending
+//     cx.set_state(indoc! {"
+//         one two
+//         t«hree
+//         ˇ» four
+//     "});
+//     cx.update_editor(|e, cx| e.tab(&Tab, cx));
+//     cx.assert_editor_state(indoc! {"
+//         one two
+//             t«hree
+//         ˇ» four
+//     "});
+
+//     cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
+//     cx.assert_editor_state(indoc! {"
+//         one two
+//         t«hree
+//         ˇ» four
+//     "});
+
+//     // Ensure that indenting/outdenting works when the cursor is at column 0.
+//     cx.set_state(indoc! {"
+//         one two
+//         ˇthree
+//             four
+//     "});
+//     cx.update_editor(|e, cx| e.tab(&Tab, cx));
+//     cx.assert_editor_state(indoc! {"
+//         one two
+//             ˇthree
+//             four
+//     "});
+
+//     cx.set_state(indoc! {"
+//         one two
+//         ˇ    three
+//             four
+//     "});
+//     cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
+//     cx.assert_editor_state(indoc! {"
+//         one two
+//         ˇthree
+//             four
+//     "});
+// }
+
+// #[gpui::test]
+// async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |settings| {
+//         settings.defaults.hard_tabs = Some(true);
+//     });
+
+//     let mut cx = EditorTestContext::new(cx).await;
+
+//     // select two ranges on one line
+//     cx.set_state(indoc! {"
+//         «oneˇ» «twoˇ»
+//         three
+//         four
+//     "});
+//     cx.update_editor(|e, cx| e.tab(&Tab, cx));
+//     cx.assert_editor_state(indoc! {"
+//         \t«oneˇ» «twoˇ»
+//         three
+//         four
+//     "});
+//     cx.update_editor(|e, cx| e.tab(&Tab, cx));
+//     cx.assert_editor_state(indoc! {"
+//         \t\t«oneˇ» «twoˇ»
+//         three
+//         four
+//     "});
+//     cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
+//     cx.assert_editor_state(indoc! {"
+//         \t«oneˇ» «twoˇ»
+//         three
+//         four
+//     "});
+//     cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
+//     cx.assert_editor_state(indoc! {"
+//         «oneˇ» «twoˇ»
+//         three
+//         four
+//     "});
+
+//     // select across a line ending
+//     cx.set_state(indoc! {"
+//         one two
+//         t«hree
+//         ˇ»four
+//     "});
+//     cx.update_editor(|e, cx| e.tab(&Tab, cx));
+//     cx.assert_editor_state(indoc! {"
+//         one two
+//         \tt«hree
+//         ˇ»four
+//     "});
+//     cx.update_editor(|e, cx| e.tab(&Tab, cx));
+//     cx.assert_editor_state(indoc! {"
+//         one two
+//         \t\tt«hree
+//         ˇ»four
+//     "});
+//     cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
+//     cx.assert_editor_state(indoc! {"
+//         one two
+//         \tt«hree
+//         ˇ»four
+//     "});
+//     cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
+//     cx.assert_editor_state(indoc! {"
+//         one two
+//         t«hree
+//         ˇ»four
+//     "});
+
+//     // Ensure that indenting/outdenting works when the cursor is at column 0.
+//     cx.set_state(indoc! {"
+//         one two
+//         ˇthree
+//         four
+//     "});
+//     cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
+//     cx.assert_editor_state(indoc! {"
+//         one two
+//         ˇthree
+//         four
+//     "});
+//     cx.update_editor(|e, cx| e.tab(&Tab, cx));
+//     cx.assert_editor_state(indoc! {"
+//         one two
+//         \tˇthree
+//         four
+//     "});
+//     cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
+//     cx.assert_editor_state(indoc! {"
+//         one two
+//         ˇthree
+//         four
+//     "});
+// }
+
+// #[gpui::test]
+// fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
+//     init_test(cx, |settings| {
+//         settings.languages.extend([
+//             (
+//                 "TOML".into(),
+//                 LanguageSettingsContent {
+//                     tab_size: NonZeroU32::new(2),
+//                     ..Default::default()
+//                 },
+//             ),
+//             (
+//                 "Rust".into(),
+//                 LanguageSettingsContent {
+//                     tab_size: NonZeroU32::new(4),
+//                     ..Default::default()
+//                 },
+//             ),
+//         ]);
+//     });
+
+//     let toml_language = Arc::new(Language::new(
+//         LanguageConfig {
+//             name: "TOML".into(),
+//             ..Default::default()
+//         },
+//         None,
+//     ));
+//     let rust_language = Arc::new(Language::new(
+//         LanguageConfig {
+//             name: "Rust".into(),
+//             ..Default::default()
+//         },
+//         None,
+//     ));
+
+//     let toml_buffer = cx.add_model(|cx| {
+//         Buffer::new(0, cx.model_id() as u64, "a = 1\nb = 2\n").with_language(toml_language, cx)
+//     });
+//     let rust_buffer = cx.add_model(|cx| {
+//         Buffer::new(0, cx.model_id() as u64, "const c: usize = 3;\n")
+//             .with_language(rust_language, cx)
+//     });
+//     let multibuffer = cx.add_model(|cx| {
+//         let mut multibuffer = MultiBuffer::new(0);
+//         multibuffer.push_excerpts(
+//             toml_buffer.clone(),
+//             [ExcerptRange {
+//                 context: Point::new(0, 0)..Point::new(2, 0),
+//                 primary: None,
+//             }],
+//             cx,
+//         );
+//         multibuffer.push_excerpts(
+//             rust_buffer.clone(),
+//             [ExcerptRange {
+//                 context: Point::new(0, 0)..Point::new(1, 0),
+//                 primary: None,
+//             }],
+//             cx,
+//         );
+//         multibuffer
+//     });
+
+//     cx.add_window(|cx| {
+//         let mut editor = build_editor(multibuffer, cx);
+
+//         assert_eq!(
+//             editor.text(cx),
+//             indoc! {"
+//                 a = 1
+//                 b = 2
+
+//                 const c: usize = 3;
+//             "}
+//         );
+
+//         select_ranges(
+//             &mut editor,
+//             indoc! {"
+//                 «aˇ» = 1
+//                 b = 2
+
+//                 «const c:ˇ» usize = 3;
+//             "},
+//             cx,
+//         );
+
+//         editor.tab(&Tab, cx);
+//         assert_text_with_selections(
+//             &mut editor,
+//             indoc! {"
+//                   «aˇ» = 1
+//                 b = 2
+
+//                     «const c:ˇ» usize = 3;
+//             "},
+//             cx,
+//         );
+//         editor.tab_prev(&TabPrev, cx);
+//         assert_text_with_selections(
+//             &mut editor,
+//             indoc! {"
+//                 «aˇ» = 1
+//                 b = 2
+
+//                 «const c:ˇ» usize = 3;
+//             "},
+//             cx,
+//         );
+
+//         editor
+//     });
+// }
+
+// #[gpui::test]
+// async fn test_backspace(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let mut cx = EditorTestContext::new(cx).await;
+
+//     // Basic backspace
+//     cx.set_state(indoc! {"
+//         onˇe two three
+//         fou«rˇ» five six
+//         seven «ˇeight nine
+//         »ten
+//     "});
+//     cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
+//     cx.assert_editor_state(indoc! {"
+//         oˇe two three
+//         fouˇ five six
+//         seven ˇten
+//     "});
+
+//     // Test backspace inside and around indents
+//     cx.set_state(indoc! {"
+//         zero
+//             ˇone
+//                 ˇtwo
+//             ˇ ˇ ˇ  three
+//         ˇ  ˇ  four
+//     "});
+//     cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
+//     cx.assert_editor_state(indoc! {"
+//         zero
+//         ˇone
+//             ˇtwo
+//         ˇ  threeˇ  four
+//     "});
+
+//     // Test backspace with line_mode set to true
+//     cx.update_editor(|e, _| e.selections.line_mode = true);
+//     cx.set_state(indoc! {"
+//         The ˇquick ˇbrown
+//         fox jumps over
+//         the lazy dog
+//         ˇThe qu«ick bˇ»rown"});
+//     cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
+//     cx.assert_editor_state(indoc! {"
+//         ˇfox jumps over
+//         the lazy dogˇ"});
+// }
+
+// #[gpui::test]
+// async fn test_delete(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let mut cx = EditorTestContext::new(cx).await;
+//     cx.set_state(indoc! {"
+//         onˇe two three
+//         fou«rˇ» five six
+//         seven «ˇeight nine
+//         »ten
+//     "});
+//     cx.update_editor(|e, cx| e.delete(&Delete, cx));
+//     cx.assert_editor_state(indoc! {"
+//         onˇ two three
+//         fouˇ five six
+//         seven ˇten
+//     "});
+
+//     // Test backspace with line_mode set to true
+//     cx.update_editor(|e, _| e.selections.line_mode = true);
+//     cx.set_state(indoc! {"
+//         The ˇquick ˇbrown
+//         fox «ˇjum»ps over
+//         the lazy dog
+//         ˇThe qu«ick bˇ»rown"});
+//     cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
+//     cx.assert_editor_state("ˇthe lazy dogˇ");
+// }
+
+// #[gpui::test]
+// fn test_delete_line(cx: &mut TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let view = cx
+//         .add_window(|cx| {
+//             let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
+//             build_editor(buffer, cx)
+//         })
+//         .root(cx);
+//     view.update(cx, |view, cx| {
+//         view.change_selections(None, cx, |s| {
+//             s.select_display_ranges([
+//                 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
+//                 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
+//                 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
+//             ])
+//         });
+//         view.delete_line(&DeleteLine, cx);
+//         assert_eq!(view.display_text(cx), "ghi");
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             vec![
+//                 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
+//                 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)
+//             ]
+//         );
+//     });
+
+//     let view = cx
+//         .add_window(|cx| {
+//             let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
+//             build_editor(buffer, cx)
+//         })
+//         .root(cx);
+//     view.update(cx, |view, cx| {
+//         view.change_selections(None, cx, |s| {
+//             s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)])
+//         });
+//         view.delete_line(&DeleteLine, cx);
+//         assert_eq!(view.display_text(cx), "ghi\n");
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             vec![DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)]
+//         );
+//     });
+// }
+
+// #[gpui::test]
+// fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     cx.add_window(|cx| {
+//         let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
+//         let mut editor = build_editor(buffer.clone(), cx);
+//         let buffer = buffer.read(cx).as_singleton().unwrap();
+
+//         assert_eq!(
+//             editor.selections.ranges::<Point>(cx),
+//             &[Point::new(0, 0)..Point::new(0, 0)]
+//         );
+
+//         // When on single line, replace newline at end by space
+//         editor.join_lines(&JoinLines, cx);
+//         assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
+//         assert_eq!(
+//             editor.selections.ranges::<Point>(cx),
+//             &[Point::new(0, 3)..Point::new(0, 3)]
+//         );
+
+//         // When multiple lines are selected, remove newlines that are spanned by the selection
+//         editor.change_selections(None, cx, |s| {
+//             s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
+//         });
+//         editor.join_lines(&JoinLines, cx);
+//         assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
+//         assert_eq!(
+//             editor.selections.ranges::<Point>(cx),
+//             &[Point::new(0, 11)..Point::new(0, 11)]
+//         );
+
+//         // Undo should be transactional
+//         editor.undo(&Undo, cx);
+//         assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
+//         assert_eq!(
+//             editor.selections.ranges::<Point>(cx),
+//             &[Point::new(0, 5)..Point::new(2, 2)]
+//         );
+
+//         // When joining an empty line don't insert a space
+//         editor.change_selections(None, cx, |s| {
+//             s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
+//         });
+//         editor.join_lines(&JoinLines, cx);
+//         assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
+//         assert_eq!(
+//             editor.selections.ranges::<Point>(cx),
+//             [Point::new(2, 3)..Point::new(2, 3)]
+//         );
+
+//         // We can remove trailing newlines
+//         editor.join_lines(&JoinLines, cx);
+//         assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
+//         assert_eq!(
+//             editor.selections.ranges::<Point>(cx),
+//             [Point::new(2, 3)..Point::new(2, 3)]
+//         );
+
+//         // We don't blow up on the last line
+//         editor.join_lines(&JoinLines, cx);
+//         assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
+//         assert_eq!(
+//             editor.selections.ranges::<Point>(cx),
+//             [Point::new(2, 3)..Point::new(2, 3)]
+//         );
+
+//         // reset to test indentation
+//         editor.buffer.update(cx, |buffer, cx| {
+//             buffer.edit(
+//                 [
+//                     (Point::new(1, 0)..Point::new(1, 2), "  "),
+//                     (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
+//                 ],
+//                 None,
+//                 cx,
+//             )
+//         });
+
+//         // We remove any leading spaces
+//         assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
+//         editor.change_selections(None, cx, |s| {
+//             s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
+//         });
+//         editor.join_lines(&JoinLines, cx);
+//         assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
+
+//         // We don't insert a space for a line containing only spaces
+//         editor.join_lines(&JoinLines, cx);
+//         assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
+
+//         // We ignore any leading tabs
+//         editor.join_lines(&JoinLines, cx);
+//         assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
+
+//         editor
+//     });
+// }
+
+// #[gpui::test]
+// fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     cx.add_window(|cx| {
+//         let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
+//         let mut editor = build_editor(buffer.clone(), cx);
+//         let buffer = buffer.read(cx).as_singleton().unwrap();
+
+//         editor.change_selections(None, cx, |s| {
+//             s.select_ranges([
+//                 Point::new(0, 2)..Point::new(1, 1),
+//                 Point::new(1, 2)..Point::new(1, 2),
+//                 Point::new(3, 1)..Point::new(3, 2),
+//             ])
+//         });
+
+//         editor.join_lines(&JoinLines, cx);
+//         assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
+
+//         assert_eq!(
+//             editor.selections.ranges::<Point>(cx),
+//             [
+//                 Point::new(0, 7)..Point::new(0, 7),
+//                 Point::new(1, 3)..Point::new(1, 3)
+//             ]
+//         );
+//         editor
+//     });
+// }
+
+// #[gpui::test]
+// async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let mut cx = EditorTestContext::new(cx).await;
+
+//     // Test sort_lines_case_insensitive()
+//     cx.set_state(indoc! {"
+//         «z
+//         y
+//         x
+//         Z
+//         Y
+//         Xˇ»
+//     "});
+//     cx.update_editor(|e, cx| e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, cx));
+//     cx.assert_editor_state(indoc! {"
+//         «x
+//         X
+//         y
+//         Y
+//         z
+//         Zˇ»
+//     "});
+
+//     // Test reverse_lines()
+//     cx.set_state(indoc! {"
+//         «5
+//         4
+//         3
+//         2
+//         1ˇ»
+//     "});
+//     cx.update_editor(|e, cx| e.reverse_lines(&ReverseLines, cx));
+//     cx.assert_editor_state(indoc! {"
+//         «1
+//         2
+//         3
+//         4
+//         5ˇ»
+//     "});
+
+//     // Skip testing shuffle_line()
+
+//     // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
+//     // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
+
+//     // Don't manipulate when cursor is on single line, but expand the selection
+//     cx.set_state(indoc! {"
+//         ddˇdd
+//         ccc
+//         bb
+//         a
+//     "});
+//     cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
+//     cx.assert_editor_state(indoc! {"
+//         «ddddˇ»
+//         ccc
+//         bb
+//         a
+//     "});
+
+//     // Basic manipulate case
+//     // Start selection moves to column 0
+//     // End of selection shrinks to fit shorter line
+//     cx.set_state(indoc! {"
+//         dd«d
+//         ccc
+//         bb
+//         aaaaaˇ»
+//     "});
+//     cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
+//     cx.assert_editor_state(indoc! {"
+//         «aaaaa
+//         bb
+//         ccc
+//         dddˇ»
+//     "});
+
+//     // Manipulate case with newlines
+//     cx.set_state(indoc! {"
+//         dd«d
+//         ccc
+
+//         bb
+//         aaaaa
+
+//         ˇ»
+//     "});
+//     cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
+//     cx.assert_editor_state(indoc! {"
+//         «
+
+//         aaaaa
+//         bb
+//         ccc
+//         dddˇ»
+
+//     "});
+// }
+
+// #[gpui::test]
+// async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let mut cx = EditorTestContext::new(cx).await;
+
+//     // Manipulate with multiple selections on a single line
+//     cx.set_state(indoc! {"
+//         dd«dd
+//         cˇ»c«c
+//         bb
+//         aaaˇ»aa
+//     "});
+//     cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
+//     cx.assert_editor_state(indoc! {"
+//         «aaaaa
+//         bb
+//         ccc
+//         ddddˇ»
+//     "});
+
+//     // Manipulate with multiple disjoin selections
+//     cx.set_state(indoc! {"
+//         5«
+//         4
+//         3
+//         2
+//         1ˇ»
+
+//         dd«dd
+//         ccc
+//         bb
+//         aaaˇ»aa
+//     "});
+//     cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx));
+//     cx.assert_editor_state(indoc! {"
+//         «1
+//         2
+//         3
+//         4
+//         5ˇ»
+
+//         «aaaaa
+//         bb
+//         ccc
+//         ddddˇ»
+//     "});
+// }
+
+// #[gpui::test]
+// async fn test_manipulate_text(cx: &mut TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let mut cx = EditorTestContext::new(cx).await;
+
+//     // Test convert_to_upper_case()
+//     cx.set_state(indoc! {"
+//         «hello worldˇ»
+//     "});
+//     cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
+//     cx.assert_editor_state(indoc! {"
+//         «HELLO WORLDˇ»
+//     "});
+
+//     // Test convert_to_lower_case()
+//     cx.set_state(indoc! {"
+//         «HELLO WORLDˇ»
+//     "});
+//     cx.update_editor(|e, cx| e.convert_to_lower_case(&ConvertToLowerCase, cx));
+//     cx.assert_editor_state(indoc! {"
+//         «hello worldˇ»
+//     "});
+
+//     // Test multiple line, single selection case
+//     // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
+//     cx.set_state(indoc! {"
+//         «The quick brown
+//         fox jumps over
+//         the lazy dogˇ»
+//     "});
+//     cx.update_editor(|e, cx| e.convert_to_title_case(&ConvertToTitleCase, cx));
+//     cx.assert_editor_state(indoc! {"
+//         «The Quick Brown
+//         Fox Jumps Over
+//         The Lazy Dogˇ»
+//     "});
+
+//     // Test multiple line, single selection case
+//     // Test code hack that covers the fact that to_case crate doesn't support '\n' as a word boundary
+//     cx.set_state(indoc! {"
+//         «The quick brown
+//         fox jumps over
+//         the lazy dogˇ»
+//     "});
+//     cx.update_editor(|e, cx| e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, cx));
+//     cx.assert_editor_state(indoc! {"
+//         «TheQuickBrown
+//         FoxJumpsOver
+//         TheLazyDogˇ»
+//     "});
+
+//     // From here on out, test more complex cases of manipulate_text()
+
+//     // Test no selection case - should affect words cursors are in
+//     // Cursor at beginning, middle, and end of word
+//     cx.set_state(indoc! {"
+//         ˇhello big beauˇtiful worldˇ
+//     "});
+//     cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
+//     cx.assert_editor_state(indoc! {"
+//         «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
+//     "});
+
+//     // Test multiple selections on a single line and across multiple lines
+//     cx.set_state(indoc! {"
+//         «Theˇ» quick «brown
+//         foxˇ» jumps «overˇ»
+//         the «lazyˇ» dog
+//     "});
+//     cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
+//     cx.assert_editor_state(indoc! {"
+//         «THEˇ» quick «BROWN
+//         FOXˇ» jumps «OVERˇ»
+//         the «LAZYˇ» dog
+//     "});
+
+//     // Test case where text length grows
+//     cx.set_state(indoc! {"
+//         «tschüߡ»
+//     "});
+//     cx.update_editor(|e, cx| e.convert_to_upper_case(&ConvertToUpperCase, cx));
+//     cx.assert_editor_state(indoc! {"
+//         «TSCHÜSSˇ»
+//     "});
+
+//     // Test to make sure we don't crash when text shrinks
+//     cx.set_state(indoc! {"
+//         aaa_bbbˇ
+//     "});
+//     cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
+//     cx.assert_editor_state(indoc! {"
+//         «aaaBbbˇ»
+//     "});
+
+//     // Test to make sure we all aware of the fact that each word can grow and shrink
+//     // Final selections should be aware of this fact
+//     cx.set_state(indoc! {"
+//         aaa_bˇbb bbˇb_ccc ˇccc_ddd
+//     "});
+//     cx.update_editor(|e, cx| e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, cx));
+//     cx.assert_editor_state(indoc! {"
+//         «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
+//     "});
+// }
+
+// #[gpui::test]
+// fn test_duplicate_line(cx: &mut TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let view = cx
+//         .add_window(|cx| {
+//             let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
+//             build_editor(buffer, cx)
+//         })
+//         .root(cx);
+//     view.update(cx, |view, cx| {
+//         view.change_selections(None, cx, |s| {
+//             s.select_display_ranges([
+//                 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
+//                 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
+//                 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
+//                 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
+//             ])
+//         });
+//         view.duplicate_line(&DuplicateLine, cx);
+//         assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             vec![
+//                 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
+//                 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
+//                 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
+//                 DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
+//             ]
+//         );
+//     });
+
+//     let view = cx
+//         .add_window(|cx| {
+//             let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
+//             build_editor(buffer, cx)
+//         })
+//         .root(cx);
+//     view.update(cx, |view, cx| {
+//         view.change_selections(None, cx, |s| {
+//             s.select_display_ranges([
+//                 DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
+//                 DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
+//             ])
+//         });
+//         view.duplicate_line(&DuplicateLine, cx);
+//         assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             vec![
+//                 DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1),
+//                 DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1),
+//             ]
+//         );
+//     });
+// }
+
+// #[gpui::test]
+// fn test_move_line_up_down(cx: &mut TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let view = cx
+//         .add_window(|cx| {
+//             let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
+//             build_editor(buffer, cx)
+//         })
+//         .root(cx);
+//     view.update(cx, |view, cx| {
+//         view.fold_ranges(
+//             vec![
+//                 Point::new(0, 2)..Point::new(1, 2),
+//                 Point::new(2, 3)..Point::new(4, 1),
+//                 Point::new(7, 0)..Point::new(8, 4),
+//             ],
+//             true,
+//             cx,
+//         );
+//         view.change_selections(None, cx, |s| {
+//             s.select_display_ranges([
+//                 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
+//                 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
+//                 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
+//                 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
+//             ])
+//         });
+//         assert_eq!(
+//             view.display_text(cx),
+//             "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
+//         );
+
+//         view.move_line_up(&MoveLineUp, cx);
+//         assert_eq!(
+//             view.display_text(cx),
+//             "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
+//         );
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             vec![
+//                 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
+//                 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
+//                 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
+//                 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
+//             ]
+//         );
+//     });
+
+//     view.update(cx, |view, cx| {
+//         view.move_line_down(&MoveLineDown, cx);
+//         assert_eq!(
+//             view.display_text(cx),
+//             "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
+//         );
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             vec![
+//                 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
+//                 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
+//                 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
+//                 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
+//             ]
+//         );
+//     });
+
+//     view.update(cx, |view, cx| {
+//         view.move_line_down(&MoveLineDown, cx);
+//         assert_eq!(
+//             view.display_text(cx),
+//             "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
+//         );
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             vec![
+//                 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
+//                 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
+//                 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
+//                 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
+//             ]
+//         );
+//     });
+
+//     view.update(cx, |view, cx| {
+//         view.move_line_up(&MoveLineUp, cx);
+//         assert_eq!(
+//             view.display_text(cx),
+//             "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
+//         );
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             vec![
+//                 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
+//                 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
+//                 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
+//                 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
+//             ]
+//         );
+//     });
+// }
+
+// #[gpui::test]
+// fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let editor = cx
+//         .add_window(|cx| {
+//             let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
+//             build_editor(buffer, cx)
+//         })
+//         .root(cx);
+//     editor.update(cx, |editor, cx| {
+//         let snapshot = editor.buffer.read(cx).snapshot(cx);
+//         editor.insert_blocks(
+//             [BlockProperties {
+//                 style: BlockStyle::Fixed,
+//                 position: snapshot.anchor_after(Point::new(2, 0)),
+//                 disposition: BlockDisposition::Below,
+//                 height: 1,
+//                 render: Arc::new(|_| Empty::new().into_any()),
+//             }],
+//             Some(Autoscroll::fit()),
+//             cx,
+//         );
+//         editor.change_selections(None, cx, |s| {
+//             s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
+//         });
+//         editor.move_line_down(&MoveLineDown, cx);
+//     });
+// }
+
+// #[gpui::test]
+// fn test_transpose(cx: &mut TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     _ = cx.add_window(|cx| {
+//         let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
+
+//         editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
+//         editor.transpose(&Default::default(), cx);
+//         assert_eq!(editor.text(cx), "bac");
+//         assert_eq!(editor.selections.ranges(cx), [2..2]);
+
+//         editor.transpose(&Default::default(), cx);
+//         assert_eq!(editor.text(cx), "bca");
+//         assert_eq!(editor.selections.ranges(cx), [3..3]);
+
+//         editor.transpose(&Default::default(), cx);
+//         assert_eq!(editor.text(cx), "bac");
+//         assert_eq!(editor.selections.ranges(cx), [3..3]);
+
+//         editor
+//     });
+
+//     _ = cx.add_window(|cx| {
+//         let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
+
+//         editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
+//         editor.transpose(&Default::default(), cx);
+//         assert_eq!(editor.text(cx), "acb\nde");
+//         assert_eq!(editor.selections.ranges(cx), [3..3]);
+
+//         editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
+//         editor.transpose(&Default::default(), cx);
+//         assert_eq!(editor.text(cx), "acbd\ne");
+//         assert_eq!(editor.selections.ranges(cx), [5..5]);
+
+//         editor.transpose(&Default::default(), cx);
+//         assert_eq!(editor.text(cx), "acbde\n");
+//         assert_eq!(editor.selections.ranges(cx), [6..6]);
+
+//         editor.transpose(&Default::default(), cx);
+//         assert_eq!(editor.text(cx), "acbd\ne");
+//         assert_eq!(editor.selections.ranges(cx), [6..6]);
+
+//         editor
+//     });
+
+//     _ = cx.add_window(|cx| {
+//         let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
+
+//         editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
+//         editor.transpose(&Default::default(), cx);
+//         assert_eq!(editor.text(cx), "bacd\ne");
+//         assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
+
+//         editor.transpose(&Default::default(), cx);
+//         assert_eq!(editor.text(cx), "bcade\n");
+//         assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
+
+//         editor.transpose(&Default::default(), cx);
+//         assert_eq!(editor.text(cx), "bcda\ne");
+//         assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
+
+//         editor.transpose(&Default::default(), cx);
+//         assert_eq!(editor.text(cx), "bcade\n");
+//         assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
+
+//         editor.transpose(&Default::default(), cx);
+//         assert_eq!(editor.text(cx), "bcaed\n");
+//         assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
+
+//         editor
+//     });
+
+//     _ = cx.add_window(|cx| {
+//         let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
+
+//         editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
+//         editor.transpose(&Default::default(), cx);
+//         assert_eq!(editor.text(cx), "🏀🍐✋");
+//         assert_eq!(editor.selections.ranges(cx), [8..8]);
+
+//         editor.transpose(&Default::default(), cx);
+//         assert_eq!(editor.text(cx), "🏀✋🍐");
+//         assert_eq!(editor.selections.ranges(cx), [11..11]);
+
+//         editor.transpose(&Default::default(), cx);
+//         assert_eq!(editor.text(cx), "🏀🍐✋");
+//         assert_eq!(editor.selections.ranges(cx), [11..11]);
+
+//         editor
+//     });
+// }
+
+// #[gpui::test]
+// async fn test_clipboard(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let mut cx = EditorTestContext::new(cx).await;
+
+//     cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
+//     cx.update_editor(|e, cx| e.cut(&Cut, cx));
+//     cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
+
+//     // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
+//     cx.set_state("two ˇfour ˇsix ˇ");
+//     cx.update_editor(|e, cx| e.paste(&Paste, cx));
+//     cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
+
+//     // Paste again but with only two cursors. Since the number of cursors doesn't
+//     // match the number of slices in the clipboard, the entire clipboard text
+//     // is pasted at each cursor.
+//     cx.set_state("ˇtwo one✅ four three six five ˇ");
+//     cx.update_editor(|e, cx| {
+//         e.handle_input("( ", cx);
+//         e.paste(&Paste, cx);
+//         e.handle_input(") ", cx);
+//     });
+//     cx.assert_editor_state(
+//         &([
+//             "( one✅ ",
+//             "three ",
+//             "five ) ˇtwo one✅ four three six five ( one✅ ",
+//             "three ",
+//             "five ) ˇ",
+//         ]
+//         .join("\n")),
+//     );
+
+//     // Cut with three selections, one of which is full-line.
+//     cx.set_state(indoc! {"
+//         1«2ˇ»3
+//         4ˇ567
+//         «8ˇ»9"});
+//     cx.update_editor(|e, cx| e.cut(&Cut, cx));
+//     cx.assert_editor_state(indoc! {"
+//         1ˇ3
+//         ˇ9"});
+
+//     // Paste with three selections, noticing how the copied selection that was full-line
+//     // gets inserted before the second cursor.
+//     cx.set_state(indoc! {"
+//         1ˇ3
+//         9ˇ
+//         «oˇ»ne"});
+//     cx.update_editor(|e, cx| e.paste(&Paste, cx));
+//     cx.assert_editor_state(indoc! {"
+//         12ˇ3
+//         4567
+//         9ˇ
+//         8ˇne"});
+
+//     // Copy with a single cursor only, which writes the whole line into the clipboard.
+//     cx.set_state(indoc! {"
+//         The quick brown
+//         fox juˇmps over
+//         the lazy dog"});
+//     cx.update_editor(|e, cx| e.copy(&Copy, cx));
+//     cx.cx.assert_clipboard_content(Some("fox jumps over\n"));
+
+//     // Paste with three selections, noticing how the copied full-line selection is inserted
+//     // before the empty selections but replaces the selection that is non-empty.
+//     cx.set_state(indoc! {"
+//         Tˇhe quick brown
+//         «foˇ»x jumps over
+//         tˇhe lazy dog"});
+//     cx.update_editor(|e, cx| e.paste(&Paste, cx));
+//     cx.assert_editor_state(indoc! {"
+//         fox jumps over
+//         Tˇhe quick brown
+//         fox jumps over
+//         ˇx jumps over
+//         fox jumps over
+//         tˇhe lazy dog"});
+// }
+
+// #[gpui::test]
+// async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let mut cx = EditorTestContext::new(cx).await;
+//     let language = Arc::new(Language::new(
+//         LanguageConfig::default(),
+//         Some(tree_sitter_rust::language()),
+//     ));
+//     cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
+
+//     // Cut an indented block, without the leading whitespace.
+//     cx.set_state(indoc! {"
+//         const a: B = (
+//             c(),
+//             «d(
+//                 e,
+//                 f
+//             )ˇ»
+//         );
+//     "});
+//     cx.update_editor(|e, cx| e.cut(&Cut, cx));
+//     cx.assert_editor_state(indoc! {"
+//         const a: B = (
+//             c(),
+//             ˇ
+//         );
+//     "});
+
+//     // Paste it at the same position.
+//     cx.update_editor(|e, cx| e.paste(&Paste, cx));
+//     cx.assert_editor_state(indoc! {"
+//         const a: B = (
+//             c(),
+//             d(
+//                 e,
+//                 f
+//             )ˇ
+//         );
+//     "});
+
+//     // Paste it at a line with a lower indent level.
+//     cx.set_state(indoc! {"
+//         ˇ
+//         const a: B = (
+//             c(),
+//         );
+//     "});
+//     cx.update_editor(|e, cx| e.paste(&Paste, cx));
+//     cx.assert_editor_state(indoc! {"
+//         d(
+//             e,
+//             f
+//         )ˇ
+//         const a: B = (
+//             c(),
+//         );
+//     "});
+
+//     // Cut an indented block, with the leading whitespace.
+//     cx.set_state(indoc! {"
+//         const a: B = (
+//             c(),
+//         «    d(
+//                 e,
+//                 f
+//             )
+//         ˇ»);
+//     "});
+//     cx.update_editor(|e, cx| e.cut(&Cut, cx));
+//     cx.assert_editor_state(indoc! {"
+//         const a: B = (
+//             c(),
+//         ˇ);
+//     "});
+
+//     // Paste it at the same position.
+//     cx.update_editor(|e, cx| e.paste(&Paste, cx));
+//     cx.assert_editor_state(indoc! {"
+//         const a: B = (
+//             c(),
+//             d(
+//                 e,
+//                 f
+//             )
+//         ˇ);
+//     "});
+
+//     // Paste it at a line with a higher indent level.
+//     cx.set_state(indoc! {"
+//         const a: B = (
+//             c(),
+//             d(
+//                 e,
+//                 fˇ
+//             )
+//         );
+//     "});
+//     cx.update_editor(|e, cx| e.paste(&Paste, cx));
+//     cx.assert_editor_state(indoc! {"
+//         const a: B = (
+//             c(),
+//             d(
+//                 e,
+//                 f    d(
+//                     e,
+//                     f
+//                 )
+//         ˇ
+//             )
+//         );
+//     "});
+// }
+
+// #[gpui::test]
+// fn test_select_all(cx: &mut TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let view = cx
+//         .add_window(|cx| {
+//             let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
+//             build_editor(buffer, cx)
+//         })
+//         .root(cx);
+//     view.update(cx, |view, cx| {
+//         view.select_all(&SelectAll, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
+//         );
+//     });
+// }
+
+// #[gpui::test]
+// fn test_select_line(cx: &mut TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let view = cx
+//         .add_window(|cx| {
+//             let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
+//             build_editor(buffer, cx)
+//         })
+//         .root(cx);
+//     view.update(cx, |view, cx| {
+//         view.change_selections(None, cx, |s| {
+//             s.select_display_ranges([
+//                 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
+//                 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
+//                 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
+//                 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
+//             ])
+//         });
+//         view.select_line(&SelectLine, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             vec![
+//                 DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
+//                 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
+//             ]
+//         );
+//     });
+
+//     view.update(cx, |view, cx| {
+//         view.select_line(&SelectLine, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             vec![
+//                 DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
+//                 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
+//             ]
+//         );
+//     });
+
+//     view.update(cx, |view, cx| {
+//         view.select_line(&SelectLine, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
+//         );
+//     });
+// }
+
+// #[gpui::test]
+// fn test_split_selection_into_lines(cx: &mut TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let view = cx
+//         .add_window(|cx| {
+//             let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
+//             build_editor(buffer, cx)
+//         })
+//         .root(cx);
+//     view.update(cx, |view, cx| {
+//         view.fold_ranges(
+//             vec![
+//                 Point::new(0, 2)..Point::new(1, 2),
+//                 Point::new(2, 3)..Point::new(4, 1),
+//                 Point::new(7, 0)..Point::new(8, 4),
+//             ],
+//             true,
+//             cx,
+//         );
+//         view.change_selections(None, cx, |s| {
+//             s.select_display_ranges([
+//                 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
+//                 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
+//                 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
+//                 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
+//             ])
+//         });
+//         assert_eq!(view.display_text(cx), "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i");
+//     });
+
+//     view.update(cx, |view, cx| {
+//         view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
+//         assert_eq!(
+//             view.display_text(cx),
+//             "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
+//         );
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             [
+//                 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
+//                 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
+//                 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
+//                 DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4)
+//             ]
+//         );
+//     });
+
+//     view.update(cx, |view, cx| {
+//         view.change_selections(None, cx, |s| {
+//             s.select_display_ranges([DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)])
+//         });
+//         view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
+//         assert_eq!(
+//             view.display_text(cx),
+//             "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
+//         );
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             [
+//                 DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5),
+//                 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
+//                 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
+//                 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
+//                 DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
+//                 DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
+//                 DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
+//                 DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
+//             ]
+//         );
+//     });
+// }
+
+// #[gpui::test]
+// fn test_add_selection_above_below(cx: &mut TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let view = cx
+//         .add_window(|cx| {
+//             let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
+//             build_editor(buffer, cx)
+//         })
+//         .root(cx);
+
+//     view.update(cx, |view, cx| {
+//         view.change_selections(None, cx, |s| {
+//             s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)])
+//         });
+//     });
+//     view.update(cx, |view, cx| {
+//         view.add_selection_above(&AddSelectionAbove, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             vec![
+//                 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
+//                 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
+//             ]
+//         );
+//     });
+
+//     view.update(cx, |view, cx| {
+//         view.add_selection_above(&AddSelectionAbove, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             vec![
+//                 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
+//                 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
+//             ]
+//         );
+//     });
+
+//     view.update(cx, |view, cx| {
+//         view.add_selection_below(&AddSelectionBelow, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
+//         );
+
+//         view.undo_selection(&UndoSelection, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             vec![
+//                 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
+//                 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
+//             ]
+//         );
+
+//         view.redo_selection(&RedoSelection, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
+//         );
+//     });
+
+//     view.update(cx, |view, cx| {
+//         view.add_selection_below(&AddSelectionBelow, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             vec![
+//                 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
+//                 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
+//             ]
+//         );
+//     });
+
+//     view.update(cx, |view, cx| {
+//         view.add_selection_below(&AddSelectionBelow, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             vec![
+//                 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
+//                 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
+//             ]
+//         );
+//     });
+
+//     view.update(cx, |view, cx| {
+//         view.change_selections(None, cx, |s| {
+//             s.select_display_ranges([DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)])
+//         });
+//     });
+//     view.update(cx, |view, cx| {
+//         view.add_selection_below(&AddSelectionBelow, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             vec![
+//                 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
+//                 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
+//             ]
+//         );
+//     });
+
+//     view.update(cx, |view, cx| {
+//         view.add_selection_below(&AddSelectionBelow, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             vec![
+//                 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
+//                 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
+//             ]
+//         );
+//     });
+
+//     view.update(cx, |view, cx| {
+//         view.add_selection_above(&AddSelectionAbove, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
+//         );
+//     });
+
+//     view.update(cx, |view, cx| {
+//         view.add_selection_above(&AddSelectionAbove, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
+//         );
+//     });
+
+//     view.update(cx, |view, cx| {
+//         view.change_selections(None, cx, |s| {
+//             s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)])
+//         });
+//         view.add_selection_below(&AddSelectionBelow, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             vec![
+//                 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
+//                 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
+//                 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
+//             ]
+//         );
+//     });
+
+//     view.update(cx, |view, cx| {
+//         view.add_selection_below(&AddSelectionBelow, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             vec![
+//                 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
+//                 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
+//                 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
+//                 DisplayPoint::new(4, 1)..DisplayPoint::new(4, 4),
+//             ]
+//         );
+//     });
+
+//     view.update(cx, |view, cx| {
+//         view.add_selection_above(&AddSelectionAbove, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             vec![
+//                 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
+//                 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
+//                 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
+//             ]
+//         );
+//     });
+
+//     view.update(cx, |view, cx| {
+//         view.change_selections(None, cx, |s| {
+//             s.select_display_ranges([DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)])
+//         });
+//     });
+//     view.update(cx, |view, cx| {
+//         view.add_selection_above(&AddSelectionAbove, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             vec![
+//                 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1),
+//                 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
+//                 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
+//                 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
+//             ]
+//         );
+//     });
+
+//     view.update(cx, |view, cx| {
+//         view.add_selection_below(&AddSelectionBelow, cx);
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             vec![
+//                 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
+//                 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
+//                 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
+//             ]
+//         );
+//     });
+// }
+
+// #[gpui::test]
+// async fn test_select_next(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let mut cx = EditorTestContext::new(cx).await;
+//     cx.set_state("abc\nˇabc abc\ndefabc\nabc");
+
+//     cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
+//         .unwrap();
+//     cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
+
+//     cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
+//         .unwrap();
+//     cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
+
+//     cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
+//     cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
+
+//     cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
+//     cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
+
+//     cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
+//         .unwrap();
+//     cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
+
+//     cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx))
+//         .unwrap();
+//     cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
+// }
+
+// #[gpui::test]
+// async fn test_select_previous(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |_| {});
+//     {
+//         // `Select previous` without a selection (selects wordwise)
+//         let mut cx = EditorTestContext::new(cx).await;
+//         cx.set_state("abc\nˇabc abc\ndefabc\nabc");
+
+//         cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
+//             .unwrap();
+//         cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
+
+//         cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
+//             .unwrap();
+//         cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
+
+//         cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
+//         cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
+
+//         cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
+//         cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
+
+//         cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
+//             .unwrap();
+//         cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
+
+//         cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
+//             .unwrap();
+//         cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
+//     }
+//     {
+//         // `Select previous` with a selection
+//         let mut cx = EditorTestContext::new(cx).await;
+//         cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
+
+//         cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
+//             .unwrap();
+//         cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
+
+//         cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
+//             .unwrap();
+//         cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
+
+//         cx.update_editor(|view, cx| view.undo_selection(&UndoSelection, cx));
+//         cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
+
+//         cx.update_editor(|view, cx| view.redo_selection(&RedoSelection, cx));
+//         cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
+
+//         cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
+//             .unwrap();
+//         cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
+
+//         cx.update_editor(|e, cx| e.select_previous(&SelectPrevious::default(), cx))
+//             .unwrap();
+//         cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
+//     }
+// }
+
+// #[gpui::test]
+// async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let language = Arc::new(Language::new(
+//         LanguageConfig::default(),
+//         Some(tree_sitter_rust::language()),
+//     ));
+
+//     let text = r#"
+//         use mod1::mod2::{mod3, mod4};
+
+//         fn fn_1(param1: bool, param2: &str) {
+//             let var1 = "text";
+//         }
+//     "#
+//     .unindent();
+
+//     let buffer =
+//         cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, text).with_language(language, cx));
+//     let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
+//     let view = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
+//     view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
+//         .await;
+
+//     view.update(cx, |view, cx| {
+//         view.change_selections(None, cx, |s| {
+//             s.select_display_ranges([
+//                 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
+//                 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
+//                 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
+//             ]);
+//         });
+//         view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
+//     });
+//     assert_eq!(
+//         view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
+//         &[
+//             DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
+//             DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
+//             DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
+//         ]
+//     );
+
+//     view.update(cx, |view, cx| {
+//         view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
+//     });
+//     assert_eq!(
+//         view.update(cx, |view, cx| view.selections.display_ranges(cx)),
+//         &[
+//             DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
+//             DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
+//         ]
+//     );
+
+//     view.update(cx, |view, cx| {
+//         view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
+//     });
+//     assert_eq!(
+//         view.update(cx, |view, cx| view.selections.display_ranges(cx)),
+//         &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
+//     );
+
+//     // Trying to expand the selected syntax node one more time has no effect.
+//     view.update(cx, |view, cx| {
+//         view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
+//     });
+//     assert_eq!(
+//         view.update(cx, |view, cx| view.selections.display_ranges(cx)),
+//         &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
+//     );
+
+//     view.update(cx, |view, cx| {
+//         view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
+//     });
+//     assert_eq!(
+//         view.update(cx, |view, cx| view.selections.display_ranges(cx)),
+//         &[
+//             DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
+//             DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
+//         ]
+//     );
+
+//     view.update(cx, |view, cx| {
+//         view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
+//     });
+//     assert_eq!(
+//         view.update(cx, |view, cx| view.selections.display_ranges(cx)),
+//         &[
+//             DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
+//             DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
+//             DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
+//         ]
+//     );
+
+//     view.update(cx, |view, cx| {
+//         view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
+//     });
+//     assert_eq!(
+//         view.update(cx, |view, cx| view.selections.display_ranges(cx)),
+//         &[
+//             DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
+//             DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
+//             DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
+//         ]
+//     );
+
+//     // Trying to shrink the selected syntax node one more time has no effect.
+//     view.update(cx, |view, cx| {
+//         view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
+//     });
+//     assert_eq!(
+//         view.update(cx, |view, cx| view.selections.display_ranges(cx)),
+//         &[
+//             DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
+//             DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
+//             DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
+//         ]
+//     );
+
+//     // Ensure that we keep expanding the selection if the larger selection starts or ends within
+//     // a fold.
+//     view.update(cx, |view, cx| {
+//         view.fold_ranges(
+//             vec![
+//                 Point::new(0, 21)..Point::new(0, 24),
+//                 Point::new(3, 20)..Point::new(3, 22),
+//             ],
+//             true,
+//             cx,
+//         );
+//         view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
+//     });
+//     assert_eq!(
+//         view.update(cx, |view, cx| view.selections.display_ranges(cx)),
+//         &[
+//             DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
+//             DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
+//             DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
+//         ]
+//     );
+// }
+
+// #[gpui::test]
+// async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let language = Arc::new(
+//         Language::new(
+//             LanguageConfig {
+//                 brackets: BracketPairConfig {
+//                     pairs: vec![
+//                         BracketPair {
+//                             start: "{".to_string(),
+//                             end: "}".to_string(),
+//                             close: false,
+//                             newline: true,
+//                         },
+//                         BracketPair {
+//                             start: "(".to_string(),
+//                             end: ")".to_string(),
+//                             close: false,
+//                             newline: true,
+//                         },
+//                     ],
+//                     ..Default::default()
+//                 },
+//                 ..Default::default()
+//             },
+//             Some(tree_sitter_rust::language()),
+//         )
+//         .with_indents_query(
+//             r#"
+//                 (_ "(" ")" @end) @indent
+//                 (_ "{" "}" @end) @indent
+//             "#,
+//         )
+//         .unwrap(),
+//     );
+
+//     let text = "fn a() {}";
+
+//     let buffer =
+//         cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, text).with_language(language, cx));
+//     let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
+//     let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
+//     editor
+//         .condition(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
+//         .await;
+
+//     editor.update(cx, |editor, cx| {
+//         editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
+//         editor.newline(&Newline, cx);
+//         assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
+//         assert_eq!(
+//             editor.selections.ranges(cx),
+//             &[
+//                 Point::new(1, 4)..Point::new(1, 4),
+//                 Point::new(3, 4)..Point::new(3, 4),
+//                 Point::new(5, 0)..Point::new(5, 0)
+//             ]
+//         );
+//     });
+// }
+
+// #[gpui::test]
+// async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let mut cx = EditorTestContext::new(cx).await;
+
+//     let language = Arc::new(Language::new(
+//         LanguageConfig {
+//             brackets: BracketPairConfig {
+//                 pairs: vec![
+//                     BracketPair {
+//                         start: "{".to_string(),
+//                         end: "}".to_string(),
+//                         close: true,
+//                         newline: true,
+//                     },
+//                     BracketPair {
+//                         start: "(".to_string(),
+//                         end: ")".to_string(),
+//                         close: true,
+//                         newline: true,
+//                     },
+//                     BracketPair {
+//                         start: "/*".to_string(),
+//                         end: " */".to_string(),
+//                         close: true,
+//                         newline: true,
+//                     },
+//                     BracketPair {
+//                         start: "[".to_string(),
+//                         end: "]".to_string(),
+//                         close: false,
+//                         newline: true,
+//                     },
+//                     BracketPair {
+//                         start: "\"".to_string(),
+//                         end: "\"".to_string(),
+//                         close: true,
+//                         newline: false,
+//                     },
+//                 ],
+//                 ..Default::default()
+//             },
+//             autoclose_before: "})]".to_string(),
+//             ..Default::default()
+//         },
+//         Some(tree_sitter_rust::language()),
+//     ));
+
+//     let registry = Arc::new(LanguageRegistry::test());
+//     registry.add(language.clone());
+//     cx.update_buffer(|buffer, cx| {
+//         buffer.set_language_registry(registry);
+//         buffer.set_language(Some(language), cx);
+//     });
+
+//     cx.set_state(
+//         &r#"
+//             🏀ˇ
+//             εˇ
+//             ❤️ˇ
+//         "#
+//         .unindent(),
+//     );
+
+//     // autoclose multiple nested brackets at multiple cursors
+//     cx.update_editor(|view, cx| {
+//         view.handle_input("{", cx);
+//         view.handle_input("{", cx);
+//         view.handle_input("{", cx);
+//     });
+//     cx.assert_editor_state(
+//         &"
+//             🏀{{{ˇ}}}
+//             ε{{{ˇ}}}
+//             ❤️{{{ˇ}}}
+//         "
+//         .unindent(),
+//     );
+
+//     // insert a different closing bracket
+//     cx.update_editor(|view, cx| {
+//         view.handle_input(")", cx);
+//     });
+//     cx.assert_editor_state(
+//         &"
+//             🏀{{{)ˇ}}}
+//             ε{{{)ˇ}}}
+//             ❤️{{{)ˇ}}}
+//         "
+//         .unindent(),
+//     );
+
+//     // skip over the auto-closed brackets when typing a closing bracket
+//     cx.update_editor(|view, cx| {
+//         view.move_right(&MoveRight, cx);
+//         view.handle_input("}", cx);
+//         view.handle_input("}", cx);
+//         view.handle_input("}", cx);
+//     });
+//     cx.assert_editor_state(
+//         &"
+//             🏀{{{)}}}}ˇ
+//             ε{{{)}}}}ˇ
+//             ❤️{{{)}}}}ˇ
+//         "
+//         .unindent(),
+//     );
+
+//     // autoclose multi-character pairs
+//     cx.set_state(
+//         &"
+//             ˇ
+//             ˇ
+//         "
+//         .unindent(),
+//     );
+//     cx.update_editor(|view, cx| {
+//         view.handle_input("/", cx);
+//         view.handle_input("*", cx);
+//     });
+//     cx.assert_editor_state(
+//         &"
+//             /*ˇ */
+//             /*ˇ */
+//         "
+//         .unindent(),
+//     );
+
+//     // one cursor autocloses a multi-character pair, one cursor
+//     // does not autoclose.
+//     cx.set_state(
+//         &"
+//             /ˇ
+//             ˇ
+//         "
+//         .unindent(),
+//     );
+//     cx.update_editor(|view, cx| view.handle_input("*", cx));
+//     cx.assert_editor_state(
+//         &"
+//             /*ˇ */
+//             *ˇ
+//         "
+//         .unindent(),
+//     );
+
+//     // Don't autoclose if the next character isn't whitespace and isn't
+//     // listed in the language's "autoclose_before" section.
+//     cx.set_state("ˇa b");
+//     cx.update_editor(|view, cx| view.handle_input("{", cx));
+//     cx.assert_editor_state("{ˇa b");
+
+//     // Don't autoclose if `close` is false for the bracket pair
+//     cx.set_state("ˇ");
+//     cx.update_editor(|view, cx| view.handle_input("[", cx));
+//     cx.assert_editor_state("[ˇ");
+
+//     // Surround with brackets if text is selected
+//     cx.set_state("«aˇ» b");
+//     cx.update_editor(|view, cx| view.handle_input("{", cx));
+//     cx.assert_editor_state("{«aˇ»} b");
+
+//     // Autclose pair where the start and end characters are the same
+//     cx.set_state("aˇ");
+//     cx.update_editor(|view, cx| view.handle_input("\"", cx));
+//     cx.assert_editor_state("a\"ˇ\"");
+//     cx.update_editor(|view, cx| view.handle_input("\"", cx));
+//     cx.assert_editor_state("a\"\"ˇ");
+// }
+
+// #[gpui::test]
+// async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let mut cx = EditorTestContext::new(cx).await;
+
+//     let html_language = Arc::new(
+//         Language::new(
+//             LanguageConfig {
+//                 name: "HTML".into(),
+//                 brackets: BracketPairConfig {
+//                     pairs: vec![
+//                         BracketPair {
+//                             start: "<".into(),
+//                             end: ">".into(),
+//                             close: true,
+//                             ..Default::default()
+//                         },
+//                         BracketPair {
+//                             start: "{".into(),
+//                             end: "}".into(),
+//                             close: true,
+//                             ..Default::default()
+//                         },
+//                         BracketPair {
+//                             start: "(".into(),
+//                             end: ")".into(),
+//                             close: true,
+//                             ..Default::default()
+//                         },
+//                     ],
+//                     ..Default::default()
+//                 },
+//                 autoclose_before: "})]>".into(),
+//                 ..Default::default()
+//             },
+//             Some(tree_sitter_html::language()),
+//         )
+//         .with_injection_query(
+//             r#"
+//             (script_element
+//                 (raw_text) @content
+//                 (#set! "language" "javascript"))
+//             "#,
+//         )
+//         .unwrap(),
+//     );
+
+//     let javascript_language = Arc::new(Language::new(
+//         LanguageConfig {
+//             name: "JavaScript".into(),
+//             brackets: BracketPairConfig {
+//                 pairs: vec![
+//                     BracketPair {
+//                         start: "/*".into(),
+//                         end: " */".into(),
+//                         close: true,
+//                         ..Default::default()
+//                     },
+//                     BracketPair {
+//                         start: "{".into(),
+//                         end: "}".into(),
+//                         close: true,
+//                         ..Default::default()
+//                     },
+//                     BracketPair {
+//                         start: "(".into(),
+//                         end: ")".into(),
+//                         close: true,
+//                         ..Default::default()
+//                     },
+//                 ],
+//                 ..Default::default()
+//             },
+//             autoclose_before: "})]>".into(),
+//             ..Default::default()
+//         },
+//         Some(tree_sitter_typescript::language_tsx()),
+//     ));
+
+//     let registry = Arc::new(LanguageRegistry::test());
+//     registry.add(html_language.clone());
+//     registry.add(javascript_language.clone());
+
+//     cx.update_buffer(|buffer, cx| {
+//         buffer.set_language_registry(registry);
+//         buffer.set_language(Some(html_language), cx);
+//     });
+
+//     cx.set_state(
+//         &r#"
+//             <body>ˇ
+//                 <script>
+//                     var x = 1;ˇ
+//                 </script>
+//             </body>ˇ
+//         "#
+//         .unindent(),
+//     );
+
+//     // Precondition: different languages are active at different locations.
+//     cx.update_editor(|editor, cx| {
+//         let snapshot = editor.snapshot(cx);
+//         let cursors = editor.selections.ranges::<usize>(cx);
+//         let languages = cursors
+//             .iter()
+//             .map(|c| snapshot.language_at(c.start).unwrap().name())
+//             .collect::<Vec<_>>();
+//         assert_eq!(
+//             languages,
+//             &["HTML".into(), "JavaScript".into(), "HTML".into()]
+//         );
+//     });
+
+//     // Angle brackets autoclose in HTML, but not JavaScript.
+//     cx.update_editor(|editor, cx| {
+//         editor.handle_input("<", cx);
+//         editor.handle_input("a", cx);
+//     });
+//     cx.assert_editor_state(
+//         &r#"
+//             <body><aˇ>
+//                 <script>
+//                     var x = 1;<aˇ
+//                 </script>
+//             </body><aˇ>
+//         "#
+//         .unindent(),
+//     );
+
+//     // Curly braces and parens autoclose in both HTML and JavaScript.
+//     cx.update_editor(|editor, cx| {
+//         editor.handle_input(" b=", cx);
+//         editor.handle_input("{", cx);
+//         editor.handle_input("c", cx);
+//         editor.handle_input("(", cx);
+//     });
+//     cx.assert_editor_state(
+//         &r#"
+//             <body><a b={c(ˇ)}>
+//                 <script>
+//                     var x = 1;<a b={c(ˇ)}
+//                 </script>
+//             </body><a b={c(ˇ)}>
+//         "#
+//         .unindent(),
+//     );
+
+//     // Brackets that were already autoclosed are skipped.
+//     cx.update_editor(|editor, cx| {
+//         editor.handle_input(")", cx);
+//         editor.handle_input("d", cx);
+//         editor.handle_input("}", cx);
+//     });
+//     cx.assert_editor_state(
+//         &r#"
+//             <body><a b={c()d}ˇ>
+//                 <script>
+//                     var x = 1;<a b={c()d}ˇ
+//                 </script>
+//             </body><a b={c()d}ˇ>
+//         "#
+//         .unindent(),
+//     );
+//     cx.update_editor(|editor, cx| {
+//         editor.handle_input(">", cx);
+//     });
+//     cx.assert_editor_state(
+//         &r#"
+//             <body><a b={c()d}>ˇ
+//                 <script>
+//                     var x = 1;<a b={c()d}>ˇ
+//                 </script>
+//             </body><a b={c()d}>ˇ
+//         "#
+//         .unindent(),
+//     );
+
+//     // Reset
+//     cx.set_state(
+//         &r#"
+//             <body>ˇ
+//                 <script>
+//                     var x = 1;ˇ
+//                 </script>
+//             </body>ˇ
+//         "#
+//         .unindent(),
+//     );
+
+//     cx.update_editor(|editor, cx| {
+//         editor.handle_input("<", cx);
+//     });
+//     cx.assert_editor_state(
+//         &r#"
+//             <body><ˇ>
+//                 <script>
+//                     var x = 1;<ˇ
+//                 </script>
+//             </body><ˇ>
+//         "#
+//         .unindent(),
+//     );
+
+//     // When backspacing, the closing angle brackets are removed.
+//     cx.update_editor(|editor, cx| {
+//         editor.backspace(&Backspace, cx);
+//     });
+//     cx.assert_editor_state(
+//         &r#"
+//             <body>ˇ
+//                 <script>
+//                     var x = 1;ˇ
+//                 </script>
+//             </body>ˇ
+//         "#
+//         .unindent(),
+//     );
+
+//     // Block comments autoclose in JavaScript, but not HTML.
+//     cx.update_editor(|editor, cx| {
+//         editor.handle_input("/", cx);
+//         editor.handle_input("*", cx);
+//     });
+//     cx.assert_editor_state(
+//         &r#"
+//             <body>/*ˇ
+//                 <script>
+//                     var x = 1;/*ˇ */
+//                 </script>
+//             </body>/*ˇ
+//         "#
+//         .unindent(),
+//     );
+// }
+
+// #[gpui::test]
+// async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let mut cx = EditorTestContext::new(cx).await;
+
+//     let rust_language = Arc::new(
+//         Language::new(
+//             LanguageConfig {
+//                 name: "Rust".into(),
+//                 brackets: serde_json::from_value(json!([
+//                     { "start": "{", "end": "}", "close": true, "newline": true },
+//                     { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
+//                 ]))
+//                 .unwrap(),
+//                 autoclose_before: "})]>".into(),
+//                 ..Default::default()
+//             },
+//             Some(tree_sitter_rust::language()),
+//         )
+//         .with_override_query("(string_literal) @string")
+//         .unwrap(),
+//     );
+
+//     let registry = Arc::new(LanguageRegistry::test());
+//     registry.add(rust_language.clone());
+
+//     cx.update_buffer(|buffer, cx| {
+//         buffer.set_language_registry(registry);
+//         buffer.set_language(Some(rust_language), cx);
+//     });
+
+//     cx.set_state(
+//         &r#"
+//             let x = ˇ
+//         "#
+//         .unindent(),
+//     );
+
+//     // Inserting a quotation mark. A closing quotation mark is automatically inserted.
+//     cx.update_editor(|editor, cx| {
+//         editor.handle_input("\"", cx);
+//     });
+//     cx.assert_editor_state(
+//         &r#"
+//             let x = "ˇ"
+//         "#
+//         .unindent(),
+//     );
+
+//     // Inserting another quotation mark. The cursor moves across the existing
+//     // automatically-inserted quotation mark.
+//     cx.update_editor(|editor, cx| {
+//         editor.handle_input("\"", cx);
+//     });
+//     cx.assert_editor_state(
+//         &r#"
+//             let x = ""ˇ
+//         "#
+//         .unindent(),
+//     );
+
+//     // Reset
+//     cx.set_state(
+//         &r#"
+//             let x = ˇ
+//         "#
+//         .unindent(),
+//     );
+
+//     // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
+//     cx.update_editor(|editor, cx| {
+//         editor.handle_input("\"", cx);
+//         editor.handle_input(" ", cx);
+//         editor.move_left(&Default::default(), cx);
+//         editor.handle_input("\\", cx);
+//         editor.handle_input("\"", cx);
+//     });
+//     cx.assert_editor_state(
+//         &r#"
+//             let x = "\"ˇ "
+//         "#
+//         .unindent(),
+//     );
+
+//     // Inserting a closing quotation mark at the position of an automatically-inserted quotation
+//     // mark. Nothing is inserted.
+//     cx.update_editor(|editor, cx| {
+//         editor.move_right(&Default::default(), cx);
+//         editor.handle_input("\"", cx);
+//     });
+//     cx.assert_editor_state(
+//         &r#"
+//             let x = "\" "ˇ
+//         "#
+//         .unindent(),
+//     );
+// }
+
+// #[gpui::test]
+// async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let language = Arc::new(Language::new(
+//         LanguageConfig {
+//             brackets: BracketPairConfig {
+//                 pairs: vec![
+//                     BracketPair {
+//                         start: "{".to_string(),
+//                         end: "}".to_string(),
+//                         close: true,
+//                         newline: true,
+//                     },
+//                     BracketPair {
+//                         start: "/* ".to_string(),
+//                         end: "*/".to_string(),
+//                         close: true,
+//                         ..Default::default()
+//                     },
+//                 ],
+//                 ..Default::default()
+//             },
+//             ..Default::default()
+//         },
+//         Some(tree_sitter_rust::language()),
+//     ));
+
+//     let text = r#"
+//         a
+//         b
+//         c
+//     "#
+//     .unindent();
+
+//     let buffer =
+//         cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, text).with_language(language, cx));
+//     let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
+//     let view = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
+//     view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
+//         .await;
+
+//     view.update(cx, |view, cx| {
+//         view.change_selections(None, cx, |s| {
+//             s.select_display_ranges([
+//                 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
+//                 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
+//                 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
+//             ])
+//         });
+
+//         view.handle_input("{", cx);
+//         view.handle_input("{", cx);
+//         view.handle_input("{", cx);
+//         assert_eq!(
+//             view.text(cx),
+//             "
+//                 {{{a}}}
+//                 {{{b}}}
+//                 {{{c}}}
+//             "
+//             .unindent()
+//         );
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             [
+//                 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 4),
+//                 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 4),
+//                 DisplayPoint::new(2, 3)..DisplayPoint::new(2, 4)
+//             ]
+//         );
+
+//         view.undo(&Undo, cx);
+//         view.undo(&Undo, cx);
+//         view.undo(&Undo, cx);
+//         assert_eq!(
+//             view.text(cx),
+//             "
+//                 a
+//                 b
+//                 c
+//             "
+//             .unindent()
+//         );
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             [
+//                 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
+//                 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
+//                 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
+//             ]
+//         );
+
+//         // Ensure inserting the first character of a multi-byte bracket pair
+//         // doesn't surround the selections with the bracket.
+//         view.handle_input("/", cx);
+//         assert_eq!(
+//             view.text(cx),
+//             "
+//                 /
+//                 /
+//                 /
+//             "
+//             .unindent()
+//         );
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             [
+//                 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
+//                 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
+//                 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1)
+//             ]
+//         );
+
+//         view.undo(&Undo, cx);
+//         assert_eq!(
+//             view.text(cx),
+//             "
+//                 a
+//                 b
+//                 c
+//             "
+//             .unindent()
+//         );
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             [
+//                 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
+//                 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
+//                 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
+//             ]
+//         );
+
+//         // Ensure inserting the last character of a multi-byte bracket pair
+//         // doesn't surround the selections with the bracket.
+//         view.handle_input("*", cx);
+//         assert_eq!(
+//             view.text(cx),
+//             "
+//                 *
+//                 *
+//                 *
+//             "
+//             .unindent()
+//         );
+//         assert_eq!(
+//             view.selections.display_ranges(cx),
+//             [
+//                 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
+//                 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
+//                 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1)
+//             ]
+//         );
+//     });
+// }
+
+// #[gpui::test]
+// async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let language = Arc::new(Language::new(
+//         LanguageConfig {
+//             brackets: BracketPairConfig {
+//                 pairs: vec![BracketPair {
+//                     start: "{".to_string(),
+//                     end: "}".to_string(),
+//                     close: true,
+//                     newline: true,
+//                 }],
+//                 ..Default::default()
+//             },
+//             autoclose_before: "}".to_string(),
+//             ..Default::default()
+//         },
+//         Some(tree_sitter_rust::language()),
+//     ));
+
+//     let text = r#"
+//         a
+//         b
+//         c
+//     "#
+//     .unindent();
+
+//     let buffer =
+//         cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, text).with_language(language, cx));
+//     let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
+//     let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
+//     editor
+//         .condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
+//         .await;
+
+//     editor.update(cx, |editor, cx| {
+//         editor.change_selections(None, cx, |s| {
+//             s.select_ranges([
+//                 Point::new(0, 1)..Point::new(0, 1),
+//                 Point::new(1, 1)..Point::new(1, 1),
+//                 Point::new(2, 1)..Point::new(2, 1),
+//             ])
+//         });
+
+//         editor.handle_input("{", cx);
+//         editor.handle_input("{", cx);
+//         editor.handle_input("_", cx);
+//         assert_eq!(
+//             editor.text(cx),
+//             "
+//                 a{{_}}
+//                 b{{_}}
+//                 c{{_}}
+//             "
+//             .unindent()
+//         );
+//         assert_eq!(
+//             editor.selections.ranges::<Point>(cx),
+//             [
+//                 Point::new(0, 4)..Point::new(0, 4),
+//                 Point::new(1, 4)..Point::new(1, 4),
+//                 Point::new(2, 4)..Point::new(2, 4)
+//             ]
+//         );
+
+//         editor.backspace(&Default::default(), cx);
+//         editor.backspace(&Default::default(), cx);
+//         assert_eq!(
+//             editor.text(cx),
+//             "
+//                 a{}
+//                 b{}
+//                 c{}
+//             "
+//             .unindent()
+//         );
+//         assert_eq!(
+//             editor.selections.ranges::<Point>(cx),
+//             [
+//                 Point::new(0, 2)..Point::new(0, 2),
+//                 Point::new(1, 2)..Point::new(1, 2),
+//                 Point::new(2, 2)..Point::new(2, 2)
+//             ]
+//         );
+
+//         editor.delete_to_previous_word_start(&Default::default(), cx);
+//         assert_eq!(
+//             editor.text(cx),
+//             "
+//                 a
+//                 b
+//                 c
+//             "
+//             .unindent()
+//         );
+//         assert_eq!(
+//             editor.selections.ranges::<Point>(cx),
+//             [
+//                 Point::new(0, 1)..Point::new(0, 1),
+//                 Point::new(1, 1)..Point::new(1, 1),
+//                 Point::new(2, 1)..Point::new(2, 1)
+//             ]
+//         );
+//     });
+// }
+
+// #[gpui::test]
+// async fn test_snippets(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let (text, insertion_ranges) = marked_text_ranges(
+//         indoc! {"
+//             a.ˇ b
+//             a.ˇ b
+//             a.ˇ b
+//         "},
+//         false,
+//     );
+
+//     let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
+//     let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
+
+//     editor.update(cx, |editor, cx| {
+//         let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
+
+//         editor
+//             .insert_snippet(&insertion_ranges, snippet, cx)
+//             .unwrap();
+
+//         fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
+//             let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
+//             assert_eq!(editor.text(cx), expected_text);
+//             assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
+//         }
+
+//         assert(
+//             editor,
+//             cx,
+//             indoc! {"
+//                 a.f(«one», two, «three») b
+//                 a.f(«one», two, «three») b
+//                 a.f(«one», two, «three») b
+//             "},
+//         );
+
+//         // Can't move earlier than the first tab stop
+//         assert!(!editor.move_to_prev_snippet_tabstop(cx));
+//         assert(
+//             editor,
+//             cx,
+//             indoc! {"
+//                 a.f(«one», two, «three») b
+//                 a.f(«one», two, «three») b
+//                 a.f(«one», two, «three») b
+//             "},
+//         );
+
+//         assert!(editor.move_to_next_snippet_tabstop(cx));
+//         assert(
+//             editor,
+//             cx,
+//             indoc! {"
+//                 a.f(one, «two», three) b
+//                 a.f(one, «two», three) b
+//                 a.f(one, «two», three) b
+//             "},
+//         );
+
+//         editor.move_to_prev_snippet_tabstop(cx);
+//         assert(
+//             editor,
+//             cx,
+//             indoc! {"
+//                 a.f(«one», two, «three») b
+//                 a.f(«one», two, «three») b
+//                 a.f(«one», two, «three») b
+//             "},
+//         );
+
+//         assert!(editor.move_to_next_snippet_tabstop(cx));
+//         assert(
+//             editor,
+//             cx,
+//             indoc! {"
+//                 a.f(one, «two», three) b
+//                 a.f(one, «two», three) b
+//                 a.f(one, «two», three) b
+//             "},
+//         );
+//         assert!(editor.move_to_next_snippet_tabstop(cx));
+//         assert(
+//             editor,
+//             cx,
+//             indoc! {"
+//                 a.f(one, two, three)ˇ b
+//                 a.f(one, two, three)ˇ b
+//                 a.f(one, two, three)ˇ b
+//             "},
+//         );
+
+//         // As soon as the last tab stop is reached, snippet state is gone
+//         editor.move_to_prev_snippet_tabstop(cx);
+//         assert(
+//             editor,
+//             cx,
+//             indoc! {"
+//                 a.f(one, two, three)ˇ b
+//                 a.f(one, two, three)ˇ b
+//                 a.f(one, two, three)ˇ b
+//             "},
+//         );
+//     });
+// }
+
+// #[gpui::test]
+// async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let mut language = Language::new(
+//         LanguageConfig {
+//             name: "Rust".into(),
+//             path_suffixes: vec!["rs".to_string()],
+//             ..Default::default()
+//         },
+//         Some(tree_sitter_rust::language()),
+//     );
+//     let mut fake_servers = language
+//         .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
+//             capabilities: lsp::ServerCapabilities {
+//                 document_formatting_provider: Some(lsp::OneOf::Left(true)),
+//                 ..Default::default()
+//             },
+//             ..Default::default()
+//         }))
+//         .await;
+
+//     let fs = FakeFs::new(cx.background());
+//     fs.insert_file("/file.rs", Default::default()).await;
+
+//     let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
+//     project.update(cx, |project, _| project.languages().add(Arc::new(language)));
+//     let buffer = project
+//         .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
+//         .await
+//         .unwrap();
+
+//     cx.foreground().start_waiting();
+//     let fake_server = fake_servers.next().await.unwrap();
+
+//     let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
+//     let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
+//     editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
+//     assert!(cx.read(|cx| editor.is_dirty(cx)));
+
+//     let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
+//     fake_server
+//         .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
+//             assert_eq!(
+//                 params.text_document.uri,
+//                 lsp::Url::from_file_path("/file.rs").unwrap()
+//             );
+//             assert_eq!(params.options.tab_size, 4);
+//             Ok(Some(vec![lsp::TextEdit::new(
+//                 lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
+//                 ", ".to_string(),
+//             )]))
+//         })
+//         .next()
+//         .await;
+//     cx.foreground().start_waiting();
+//     save.await.unwrap();
+//     assert_eq!(
+//         editor.read_with(cx, |editor, cx| editor.text(cx)),
+//         "one, two\nthree\n"
+//     );
+//     assert!(!cx.read(|cx| editor.is_dirty(cx)));
+
+//     editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
+//     assert!(cx.read(|cx| editor.is_dirty(cx)));
+
+//     // Ensure we can still save even if formatting hangs.
+//     fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
+//         assert_eq!(
+//             params.text_document.uri,
+//             lsp::Url::from_file_path("/file.rs").unwrap()
+//         );
+//         futures::future::pending::<()>().await;
+//         unreachable!()
+//     });
+//     let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
+//     cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
+//     cx.foreground().start_waiting();
+//     save.await.unwrap();
+//     assert_eq!(
+//         editor.read_with(cx, |editor, cx| editor.text(cx)),
+//         "one\ntwo\nthree\n"
+//     );
+//     assert!(!cx.read(|cx| editor.is_dirty(cx)));
+
+//     // Set rust language override and assert overridden tabsize is sent to language server
+//     update_test_language_settings(cx, |settings| {
+//         settings.languages.insert(
+//             "Rust".into(),
+//             LanguageSettingsContent {
+//                 tab_size: NonZeroU32::new(8),
+//                 ..Default::default()
+//             },
+//         );
+//     });
+
+//     let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
+//     fake_server
+//         .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
+//             assert_eq!(
+//                 params.text_document.uri,
+//                 lsp::Url::from_file_path("/file.rs").unwrap()
+//             );
+//             assert_eq!(params.options.tab_size, 8);
+//             Ok(Some(vec![]))
+//         })
+//         .next()
+//         .await;
+//     cx.foreground().start_waiting();
+//     save.await.unwrap();
+// }
+
+// #[gpui::test]
+// async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let mut language = Language::new(
+//         LanguageConfig {
+//             name: "Rust".into(),
+//             path_suffixes: vec!["rs".to_string()],
+//             ..Default::default()
+//         },
+//         Some(tree_sitter_rust::language()),
+//     );
+//     let mut fake_servers = language
+//         .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
+//             capabilities: lsp::ServerCapabilities {
+//                 document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
+//                 ..Default::default()
+//             },
+//             ..Default::default()
+//         }))
+//         .await;
+
+//     let fs = FakeFs::new(cx.background());
+//     fs.insert_file("/file.rs", Default::default()).await;
+
+//     let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
+//     project.update(cx, |project, _| project.languages().add(Arc::new(language)));
+//     let buffer = project
+//         .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
+//         .await
+//         .unwrap();
+
+//     cx.foreground().start_waiting();
+//     let fake_server = fake_servers.next().await.unwrap();
+
+//     let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
+//     let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
+//     editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
+//     assert!(cx.read(|cx| editor.is_dirty(cx)));
+
+//     let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
+//     fake_server
+//         .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
+//             assert_eq!(
+//                 params.text_document.uri,
+//                 lsp::Url::from_file_path("/file.rs").unwrap()
+//             );
+//             assert_eq!(params.options.tab_size, 4);
+//             Ok(Some(vec![lsp::TextEdit::new(
+//                 lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
+//                 ", ".to_string(),
+//             )]))
+//         })
+//         .next()
+//         .await;
+//     cx.foreground().start_waiting();
+//     save.await.unwrap();
+//     assert_eq!(
+//         editor.read_with(cx, |editor, cx| editor.text(cx)),
+//         "one, two\nthree\n"
+//     );
+//     assert!(!cx.read(|cx| editor.is_dirty(cx)));
+
+//     editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
+//     assert!(cx.read(|cx| editor.is_dirty(cx)));
+
+//     // Ensure we can still save even if formatting hangs.
+//     fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
+//         move |params, _| async move {
+//             assert_eq!(
+//                 params.text_document.uri,
+//                 lsp::Url::from_file_path("/file.rs").unwrap()
+//             );
+//             futures::future::pending::<()>().await;
+//             unreachable!()
+//         },
+//     );
+//     let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
+//     cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
+//     cx.foreground().start_waiting();
+//     save.await.unwrap();
+//     assert_eq!(
+//         editor.read_with(cx, |editor, cx| editor.text(cx)),
+//         "one\ntwo\nthree\n"
+//     );
+//     assert!(!cx.read(|cx| editor.is_dirty(cx)));
+
+//     // Set rust language override and assert overridden tabsize is sent to language server
+//     update_test_language_settings(cx, |settings| {
+//         settings.languages.insert(
+//             "Rust".into(),
+//             LanguageSettingsContent {
+//                 tab_size: NonZeroU32::new(8),
+//                 ..Default::default()
+//             },
+//         );
+//     });
+
+//     let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
+//     fake_server
+//         .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
+//             assert_eq!(
+//                 params.text_document.uri,
+//                 lsp::Url::from_file_path("/file.rs").unwrap()
+//             );
+//             assert_eq!(params.options.tab_size, 8);
+//             Ok(Some(vec![]))
+//         })
+//         .next()
+//         .await;
+//     cx.foreground().start_waiting();
+//     save.await.unwrap();
+// }
+
+// #[gpui::test]
+// async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |settings| {
+//         settings.defaults.formatter = Some(language_settings::Formatter::LanguageServer)
+//     });
+
+//     let mut language = Language::new(
+//         LanguageConfig {
+//             name: "Rust".into(),
+//             path_suffixes: vec!["rs".to_string()],
+//             // Enable Prettier formatting for the same buffer, and ensure
+//             // LSP is called instead of Prettier.
+//             prettier_parser_name: Some("test_parser".to_string()),
+//             ..Default::default()
+//         },
+//         Some(tree_sitter_rust::language()),
+//     );
+//     let mut fake_servers = language
+//         .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
+//             capabilities: lsp::ServerCapabilities {
+//                 document_formatting_provider: Some(lsp::OneOf::Left(true)),
+//                 ..Default::default()
+//             },
+//             ..Default::default()
+//         }))
+//         .await;
+
+//     let fs = FakeFs::new(cx.background());
+//     fs.insert_file("/file.rs", Default::default()).await;
+
+//     let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
+//     project.update(cx, |project, _| {
+//         project.languages().add(Arc::new(language));
+//     });
+//     let buffer = project
+//         .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
+//         .await
+//         .unwrap();
+
+//     cx.foreground().start_waiting();
+//     let fake_server = fake_servers.next().await.unwrap();
+
+//     let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
+//     let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
+//     editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
+
+//     let format = editor.update(cx, |editor, cx| {
+//         editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
+//     });
+//     fake_server
+//         .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
+//             assert_eq!(
+//                 params.text_document.uri,
+//                 lsp::Url::from_file_path("/file.rs").unwrap()
+//             );
+//             assert_eq!(params.options.tab_size, 4);
+//             Ok(Some(vec![lsp::TextEdit::new(
+//                 lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
+//                 ", ".to_string(),
+//             )]))
+//         })
+//         .next()
+//         .await;
+//     cx.foreground().start_waiting();
+//     format.await.unwrap();
+//     assert_eq!(
+//         editor.read_with(cx, |editor, cx| editor.text(cx)),
+//         "one, two\nthree\n"
+//     );
+
+//     editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
+//     // Ensure we don't lock if formatting hangs.
+//     fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
+//         assert_eq!(
+//             params.text_document.uri,
+//             lsp::Url::from_file_path("/file.rs").unwrap()
+//         );
+//         futures::future::pending::<()>().await;
+//         unreachable!()
+//     });
+//     let format = editor.update(cx, |editor, cx| {
+//         editor.perform_format(project, FormatTrigger::Manual, cx)
+//     });
+//     cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
+//     cx.foreground().start_waiting();
+//     format.await.unwrap();
+//     assert_eq!(
+//         editor.read_with(cx, |editor, cx| editor.text(cx)),
+//         "one\ntwo\nthree\n"
+//     );
+// }
+
+// #[gpui::test]
+// async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let mut cx = EditorLspTestContext::new_rust(
+//         lsp::ServerCapabilities {
+//             document_formatting_provider: Some(lsp::OneOf::Left(true)),
+//             ..Default::default()
+//         },
+//         cx,
+//     )
+//     .await;
+
+//     cx.set_state(indoc! {"
+//         one.twoˇ
+//     "});
+
+//     // The format request takes a long time. When it completes, it inserts
+//     // a newline and an indent before the `.`
+//     cx.lsp
+//         .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
+//             let executor = cx.background();
+//             async move {
+//                 executor.timer(Duration::from_millis(100)).await;
+//                 Ok(Some(vec![lsp::TextEdit {
+//                     range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
+//                     new_text: "\n    ".into(),
+//                 }]))
+//             }
+//         });
+
+//     // Submit a format request.
+//     let format_1 = cx
+//         .update_editor(|editor, cx| editor.format(&Format, cx))
+//         .unwrap();
+//     cx.foreground().run_until_parked();
+
+//     // Submit a second format request.
+//     let format_2 = cx
+//         .update_editor(|editor, cx| editor.format(&Format, cx))
+//         .unwrap();
+//     cx.foreground().run_until_parked();
+
+//     // Wait for both format requests to complete
+//     cx.foreground().advance_clock(Duration::from_millis(200));
+//     cx.foreground().start_waiting();
+//     format_1.await.unwrap();
+//     cx.foreground().start_waiting();
+//     format_2.await.unwrap();
+
+//     // The formatting edits only happens once.
+//     cx.assert_editor_state(indoc! {"
+//         one
+//             .twoˇ
+//     "});
+// }
+
+// #[gpui::test]
+// async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |settings| {
+//         settings.defaults.formatter = Some(language_settings::Formatter::Auto)
+//     });
+
+//     let mut cx = EditorLspTestContext::new_rust(
+//         lsp::ServerCapabilities {
+//             document_formatting_provider: Some(lsp::OneOf::Left(true)),
+//             ..Default::default()
+//         },
+//         cx,
+//     )
+//     .await;
+
+//     // Set up a buffer white some trailing whitespace and no trailing newline.
+//     cx.set_state(
+//         &[
+//             "one ",   //
+//             "twoˇ",   //
+//             "three ", //
+//             "four",   //
+//         ]
+//         .join("\n"),
+//     );
+
+//     // Submit a format request.
+//     let format = cx
+//         .update_editor(|editor, cx| editor.format(&Format, cx))
+//         .unwrap();
+
+//     // Record which buffer changes have been sent to the language server
+//     let buffer_changes = Arc::new(Mutex::new(Vec::new()));
+//     cx.lsp
+//         .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
+//             let buffer_changes = buffer_changes.clone();
+//             move |params, _| {
+//                 buffer_changes.lock().extend(
+//                     params
+//                         .content_changes
+//                         .into_iter()
+//                         .map(|e| (e.range.unwrap(), e.text)),
+//                 );
+//             }
+//         });
+
+//     // Handle formatting requests to the language server.
+//     cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
+//         let buffer_changes = buffer_changes.clone();
+//         move |_, _| {
+//             // When formatting is requested, trailing whitespace has already been stripped,
+//             // and the trailing newline has already been added.
+//             assert_eq!(
+//                 &buffer_changes.lock()[1..],
+//                 &[
+//                     (
+//                         lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
+//                         "".into()
+//                     ),
+//                     (
+//                         lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
+//                         "".into()
+//                     ),
+//                     (
+//                         lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
+//                         "\n".into()
+//                     ),
+//                 ]
+//             );
+
+//             // Insert blank lines between each line of the buffer.
+//             async move {
+//                 Ok(Some(vec![
+//                     lsp::TextEdit {
+//                         range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
+//                         new_text: "\n".into(),
+//                     },
+//                     lsp::TextEdit {
+//                         range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
+//                         new_text: "\n".into(),
+//                     },
+//                 ]))
+//             }
+//         }
+//     });
+
+//     // After formatting the buffer, the trailing whitespace is stripped,
+//     // a newline is appended, and the edits provided by the language server
+//     // have been applied.
+//     format.await.unwrap();
+//     cx.assert_editor_state(
+//         &[
+//             "one",   //
+//             "",      //
+//             "twoˇ",  //
+//             "",      //
+//             "three", //
+//             "four",  //
+//             "",      //
+//         ]
+//         .join("\n"),
+//     );
+
+//     // Undoing the formatting undoes the trailing whitespace removal, the
+//     // trailing newline, and the LSP edits.
+//     cx.update_buffer(|buffer, cx| buffer.undo(cx));
+//     cx.assert_editor_state(
+//         &[
+//             "one ",   //
+//             "twoˇ",   //
+//             "three ", //
+//             "four",   //
+//         ]
+//         .join("\n"),
+//     );
+// }
+
+// #[gpui::test]
+// async fn test_completion(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let mut cx = EditorLspTestContext::new_rust(
+//         lsp::ServerCapabilities {
+//             completion_provider: Some(lsp::CompletionOptions {
+//                 trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
+//                 resolve_provider: Some(true),
+//                 ..Default::default()
+//             }),
+//             ..Default::default()
+//         },
+//         cx,
+//     )
+//     .await;
+
+//     cx.set_state(indoc! {"
+//         oneˇ
+//         two
+//         three
+//     "});
+//     cx.simulate_keystroke(".");
+//     handle_completion_request(
+//         &mut cx,
+//         indoc! {"
+//             one.|<>
+//             two
+//             three
+//         "},
+//         vec!["first_completion", "second_completion"],
+//     )
+//     .await;
+//     cx.condition(|editor, _| editor.context_menu_visible())
+//         .await;
+//     let apply_additional_edits = cx.update_editor(|editor, cx| {
+//         editor.context_menu_next(&Default::default(), cx);
+//         editor
+//             .confirm_completion(&ConfirmCompletion::default(), cx)
+//             .unwrap()
+//     });
+//     cx.assert_editor_state(indoc! {"
+//         one.second_completionˇ
+//         two
+//         three
+//     "});
+
+//     handle_resolve_completion_request(
+//         &mut cx,
+//         Some(vec![
+//             (
+//                 //This overlaps with the primary completion edit which is
+//                 //misbehavior from the LSP spec, test that we filter it out
+//                 indoc! {"
+//                     one.second_ˇcompletion
+//                     two
+//                     threeˇ
+//                 "},
+//                 "overlapping additional edit",
+//             ),
+//             (
+//                 indoc! {"
+//                     one.second_completion
+//                     two
+//                     threeˇ
+//                 "},
+//                 "\nadditional edit",
+//             ),
+//         ]),
+//     )
+//     .await;
+//     apply_additional_edits.await.unwrap();
+//     cx.assert_editor_state(indoc! {"
+//         one.second_completionˇ
+//         two
+//         three
+//         additional edit
+//     "});
+
+//     cx.set_state(indoc! {"
+//         one.second_completion
+//         twoˇ
+//         threeˇ
+//         additional edit
+//     "});
+//     cx.simulate_keystroke(" ");
+//     assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
+//     cx.simulate_keystroke("s");
+//     assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
+
+//     cx.assert_editor_state(indoc! {"
+//         one.second_completion
+//         two sˇ
+//         three sˇ
+//         additional edit
+//     "});
+//     handle_completion_request(
+//         &mut cx,
+//         indoc! {"
+//             one.second_completion
+//             two s
+//             three <s|>
+//             additional edit
+//         "},
+//         vec!["fourth_completion", "fifth_completion", "sixth_completion"],
+//     )
+//     .await;
+//     cx.condition(|editor, _| editor.context_menu_visible())
+//         .await;
+
+//     cx.simulate_keystroke("i");
+
+//     handle_completion_request(
+//         &mut cx,
+//         indoc! {"
+//             one.second_completion
+//             two si
+//             three <si|>
+//             additional edit
+//         "},
+//         vec!["fourth_completion", "fifth_completion", "sixth_completion"],
+//     )
+//     .await;
+//     cx.condition(|editor, _| editor.context_menu_visible())
+//         .await;
+
+//     let apply_additional_edits = cx.update_editor(|editor, cx| {
+//         editor
+//             .confirm_completion(&ConfirmCompletion::default(), cx)
+//             .unwrap()
+//     });
+//     cx.assert_editor_state(indoc! {"
+//         one.second_completion
+//         two sixth_completionˇ
+//         three sixth_completionˇ
+//         additional edit
+//     "});
+
+//     handle_resolve_completion_request(&mut cx, None).await;
+//     apply_additional_edits.await.unwrap();
+
+//     cx.update(|cx| {
+//         cx.update_global::<SettingsStore, _, _>(|settings, cx| {
+//             settings.update_user_settings::<EditorSettings>(cx, |settings| {
+//                 settings.show_completions_on_input = Some(false);
+//             });
+//         })
+//     });
+//     cx.set_state("editorˇ");
+//     cx.simulate_keystroke(".");
+//     assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
+//     cx.simulate_keystroke("c");
+//     cx.simulate_keystroke("l");
+//     cx.simulate_keystroke("o");
+//     cx.assert_editor_state("editor.cloˇ");
+//     assert!(cx.editor(|e, _| e.context_menu.read().is_none()));
+//     cx.update_editor(|editor, cx| {
+//         editor.show_completions(&ShowCompletions, cx);
+//     });
+//     handle_completion_request(&mut cx, "editor.<clo|>", vec!["close", "clobber"]).await;
+//     cx.condition(|editor, _| editor.context_menu_visible())
+//         .await;
+//     let apply_additional_edits = cx.update_editor(|editor, cx| {
+//         editor
+//             .confirm_completion(&ConfirmCompletion::default(), cx)
+//             .unwrap()
+//     });
+//     cx.assert_editor_state("editor.closeˇ");
+//     handle_resolve_completion_request(&mut cx, None).await;
+//     apply_additional_edits.await.unwrap();
+// }
+
+// #[gpui::test]
+// async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |_| {});
+//     let mut cx = EditorTestContext::new(cx).await;
+//     let language = Arc::new(Language::new(
+//         LanguageConfig {
+//             line_comment: Some("// ".into()),
+//             ..Default::default()
+//         },
+//         Some(tree_sitter_rust::language()),
+//     ));
+//     cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
+
+//     // If multiple selections intersect a line, the line is only toggled once.
+//     cx.set_state(indoc! {"
+//         fn a() {
+//             «//b();
+//             ˇ»// «c();
+//             //ˇ»  d();
+//         }
+//     "});
+
+//     cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
+
+//     cx.assert_editor_state(indoc! {"
+//         fn a() {
+//             «b();
+//             c();
+//             ˇ» d();
+//         }
+//     "});
+
+//     // The comment prefix is inserted at the same column for every line in a
+//     // selection.
+//     cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
+
+//     cx.assert_editor_state(indoc! {"
+//         fn a() {
+//             // «b();
+//             // c();
+//             ˇ»//  d();
+//         }
+//     "});
+
+//     // If a selection ends at the beginning of a line, that line is not toggled.
+//     cx.set_selections_state(indoc! {"
+//         fn a() {
+//             // b();
+//             «// c();
+//         ˇ»    //  d();
+//         }
+//     "});
+
+//     cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
+
+//     cx.assert_editor_state(indoc! {"
+//         fn a() {
+//             // b();
+//             «c();
+//         ˇ»    //  d();
+//         }
+//     "});
+
+//     // If a selection span a single line and is empty, the line is toggled.
+//     cx.set_state(indoc! {"
+//         fn a() {
+//             a();
+//             b();
+//         ˇ
+//         }
+//     "});
+
+//     cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
+
+//     cx.assert_editor_state(indoc! {"
+//         fn a() {
+//             a();
+//             b();
+//         //•ˇ
+//         }
+//     "});
+
+//     // If a selection span multiple lines, empty lines are not toggled.
+//     cx.set_state(indoc! {"
+//         fn a() {
+//             «a();
+
+//             c();ˇ»
+//         }
+//     "});
+
+//     cx.update_editor(|e, cx| e.toggle_comments(&ToggleComments::default(), cx));
+
+//     cx.assert_editor_state(indoc! {"
+//         fn a() {
+//             // «a();
+
+//             // c();ˇ»
+//         }
+//     "});
+// }
+
+// #[gpui::test]
+// async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let language = Arc::new(Language::new(
+//         LanguageConfig {
+//             line_comment: Some("// ".into()),
+//             ..Default::default()
+//         },
+//         Some(tree_sitter_rust::language()),
+//     ));
+
+//     let registry = Arc::new(LanguageRegistry::test());
+//     registry.add(language.clone());
+
+//     let mut cx = EditorTestContext::new(cx).await;
+//     cx.update_buffer(|buffer, cx| {
+//         buffer.set_language_registry(registry);
+//         buffer.set_language(Some(language), cx);
+//     });
+
+//     let toggle_comments = &ToggleComments {
+//         advance_downwards: true,
+//     };
+
+//     // Single cursor on one line -> advance
+//     // Cursor moves horizontally 3 characters as well on non-blank line
+//     cx.set_state(indoc!(
+//         "fn a() {
+//              ˇdog();
+//              cat();
+//         }"
+//     ));
+//     cx.update_editor(|editor, cx| {
+//         editor.toggle_comments(toggle_comments, cx);
+//     });
+//     cx.assert_editor_state(indoc!(
+//         "fn a() {
+//              // dog();
+//              catˇ();
+//         }"
+//     ));
+
+//     // Single selection on one line -> don't advance
+//     cx.set_state(indoc!(
+//         "fn a() {
+//              «dog()ˇ»;
+//              cat();
+//         }"
+//     ));
+//     cx.update_editor(|editor, cx| {
+//         editor.toggle_comments(toggle_comments, cx);
+//     });
+//     cx.assert_editor_state(indoc!(
+//         "fn a() {
+//              // «dog()ˇ»;
+//              cat();
+//         }"
+//     ));
+
+//     // Multiple cursors on one line -> advance
+//     cx.set_state(indoc!(
+//         "fn a() {
+//              ˇdˇog();
+//              cat();
+//         }"
+//     ));
+//     cx.update_editor(|editor, cx| {
+//         editor.toggle_comments(toggle_comments, cx);
+//     });
+//     cx.assert_editor_state(indoc!(
+//         "fn a() {
+//              // dog();
+//              catˇ(ˇ);
+//         }"
+//     ));
+
+//     // Multiple cursors on one line, with selection -> don't advance
+//     cx.set_state(indoc!(
+//         "fn a() {
+//              ˇdˇog«()ˇ»;
+//              cat();
+//         }"
+//     ));
+//     cx.update_editor(|editor, cx| {
+//         editor.toggle_comments(toggle_comments, cx);
+//     });
+//     cx.assert_editor_state(indoc!(
+//         "fn a() {
+//              // ˇdˇog«()ˇ»;
+//              cat();
+//         }"
+//     ));
+
+//     // Single cursor on one line -> advance
+//     // Cursor moves to column 0 on blank line
+//     cx.set_state(indoc!(
+//         "fn a() {
+//              ˇdog();
+
+//              cat();
+//         }"
+//     ));
+//     cx.update_editor(|editor, cx| {
+//         editor.toggle_comments(toggle_comments, cx);
+//     });
+//     cx.assert_editor_state(indoc!(
+//         "fn a() {
+//              // dog();
+//         ˇ
+//              cat();
+//         }"
+//     ));
+
+//     // Single cursor on one line -> advance
+//     // Cursor starts and ends at column 0
+//     cx.set_state(indoc!(
+//         "fn a() {
+//          ˇ    dog();
+//              cat();
+//         }"
+//     ));
+//     cx.update_editor(|editor, cx| {
+//         editor.toggle_comments(toggle_comments, cx);
+//     });
+//     cx.assert_editor_state(indoc!(
+//         "fn a() {
+//              // dog();
+//          ˇ    cat();
+//         }"
+//     ));
+// }
+
+// #[gpui::test]
+// async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let mut cx = EditorTestContext::new(cx).await;
+
+//     let html_language = Arc::new(
+//         Language::new(
+//             LanguageConfig {
+//                 name: "HTML".into(),
+//                 block_comment: Some(("<!-- ".into(), " -->".into())),
+//                 ..Default::default()
+//             },
+//             Some(tree_sitter_html::language()),
+//         )
+//         .with_injection_query(
+//             r#"
+//             (script_element
+//                 (raw_text) @content
+//                 (#set! "language" "javascript"))
+//             "#,
+//         )
+//         .unwrap(),
+//     );
+
+//     let javascript_language = Arc::new(Language::new(
+//         LanguageConfig {
+//             name: "JavaScript".into(),
+//             line_comment: Some("// ".into()),
+//             ..Default::default()
+//         },
+//         Some(tree_sitter_typescript::language_tsx()),
+//     ));
+
+//     let registry = Arc::new(LanguageRegistry::test());
+//     registry.add(html_language.clone());
+//     registry.add(javascript_language.clone());
+
+//     cx.update_buffer(|buffer, cx| {
+//         buffer.set_language_registry(registry);
+//         buffer.set_language(Some(html_language), cx);
+//     });
+
+//     // Toggle comments for empty selections
+//     cx.set_state(
+//         &r#"
+//             <p>A</p>ˇ
+//             <p>B</p>ˇ
+//             <p>C</p>ˇ
+//         "#
+//         .unindent(),
+//     );
+//     cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
+//     cx.assert_editor_state(
+//         &r#"
+//             <!-- <p>A</p>ˇ -->
+//             <!-- <p>B</p>ˇ -->
+//             <!-- <p>C</p>ˇ -->
+//         "#
+//         .unindent(),
+//     );
+//     cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
+//     cx.assert_editor_state(
+//         &r#"
+//             <p>A</p>ˇ
+//             <p>B</p>ˇ
+//             <p>C</p>ˇ
+//         "#
+//         .unindent(),
+//     );
+
+//     // Toggle comments for mixture of empty and non-empty selections, where
+//     // multiple selections occupy a given line.
+//     cx.set_state(
+//         &r#"
+//             <p>A«</p>
+//             <p>ˇ»B</p>ˇ
+//             <p>C«</p>
+//             <p>ˇ»D</p>ˇ
+//         "#
+//         .unindent(),
+//     );
+
+//     cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
+//     cx.assert_editor_state(
+//         &r#"
+//             <!-- <p>A«</p>
+//             <p>ˇ»B</p>ˇ -->
+//             <!-- <p>C«</p>
+//             <p>ˇ»D</p>ˇ -->
+//         "#
+//         .unindent(),
+//     );
+//     cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
+//     cx.assert_editor_state(
+//         &r#"
+//             <p>A«</p>
+//             <p>ˇ»B</p>ˇ
+//             <p>C«</p>
+//             <p>ˇ»D</p>ˇ
+//         "#
+//         .unindent(),
+//     );
+
+//     // Toggle comments when different languages are active for different
+//     // selections.
+//     cx.set_state(
+//         &r#"
+//             ˇ<script>
+//                 ˇvar x = new Y();
+//             ˇ</script>
+//         "#
+//         .unindent(),
+//     );
+//     cx.foreground().run_until_parked();
+//     cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments::default(), cx));
+//     cx.assert_editor_state(
+//         &r#"
+//             <!-- ˇ<script> -->
+//                 // ˇvar x = new Y();
+//             <!-- ˇ</script> -->
+//         "#
+//         .unindent(),
+//     );
+// }
+
+// #[gpui::test]
+// fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let buffer = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, sample_text(3, 4, 'a')));
+//     let multibuffer = cx.add_model(|cx| {
+//         let mut multibuffer = MultiBuffer::new(0);
+//         multibuffer.push_excerpts(
+//             buffer.clone(),
+//             [
+//                 ExcerptRange {
+//                     context: Point::new(0, 0)..Point::new(0, 4),
+//                     primary: None,
+//                 },
+//                 ExcerptRange {
+//                     context: Point::new(1, 0)..Point::new(1, 4),
+//                     primary: None,
+//                 },
+//             ],
+//             cx,
+//         );
+//         assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
+//         multibuffer
+//     });
+
+//     let view = cx.add_window(|cx| build_editor(multibuffer, cx)).root(cx);
+//     view.update(cx, |view, cx| {
+//         assert_eq!(view.text(cx), "aaaa\nbbbb");
+//         view.change_selections(None, cx, |s| {
+//             s.select_ranges([
+//                 Point::new(0, 0)..Point::new(0, 0),
+//                 Point::new(1, 0)..Point::new(1, 0),
+//             ])
+//         });
+
+//         view.handle_input("X", cx);
+//         assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
+//         assert_eq!(
+//             view.selections.ranges(cx),
+//             [
+//                 Point::new(0, 1)..Point::new(0, 1),
+//                 Point::new(1, 1)..Point::new(1, 1),
+//             ]
+//         );
+
+//         // Ensure the cursor's head is respected when deleting across an excerpt boundary.
+//         view.change_selections(None, cx, |s| {
+//             s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
+//         });
+//         view.backspace(&Default::default(), cx);
+//         assert_eq!(view.text(cx), "Xa\nbbb");
+//         assert_eq!(
+//             view.selections.ranges(cx),
+//             [Point::new(1, 0)..Point::new(1, 0)]
+//         );
+
+//         view.change_selections(None, cx, |s| {
+//             s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
+//         });
+//         view.backspace(&Default::default(), cx);
+//         assert_eq!(view.text(cx), "X\nbb");
+//         assert_eq!(
+//             view.selections.ranges(cx),
+//             [Point::new(0, 1)..Point::new(0, 1)]
+//         );
+//     });
+// }
+
+// #[gpui::test]
+// fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let markers = vec![('[', ']').into(), ('(', ')').into()];
+//     let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
+//         indoc! {"
+//             [aaaa
+//             (bbbb]
+//             cccc)",
+//         },
+//         markers.clone(),
+//     );
+//     let excerpt_ranges = markers.into_iter().map(|marker| {
+//         let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
+//         ExcerptRange {
+//             context,
+//             primary: None,
+//         }
+//     });
+//     let buffer = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, initial_text));
+//     let multibuffer = cx.add_model(|cx| {
+//         let mut multibuffer = MultiBuffer::new(0);
+//         multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
+//         multibuffer
+//     });
+
+//     let view = cx.add_window(|cx| build_editor(multibuffer, cx)).root(cx);
+//     view.update(cx, |view, cx| {
+//         let (expected_text, selection_ranges) = marked_text_ranges(
+//             indoc! {"
+//                 aaaa
+//                 bˇbbb
+//                 bˇbbˇb
+//                 cccc"
+//             },
+//             true,
+//         );
+//         assert_eq!(view.text(cx), expected_text);
+//         view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
+
+//         view.handle_input("X", cx);
+
+//         let (expected_text, expected_selections) = marked_text_ranges(
+//             indoc! {"
+//                 aaaa
+//                 bXˇbbXb
+//                 bXˇbbXˇb
+//                 cccc"
+//             },
+//             false,
+//         );
+//         assert_eq!(view.text(cx), expected_text);
+//         assert_eq!(view.selections.ranges(cx), expected_selections);
+
+//         view.newline(&Newline, cx);
+//         let (expected_text, expected_selections) = marked_text_ranges(
+//             indoc! {"
+//                 aaaa
+//                 bX
+//                 ˇbbX
+//                 b
+//                 bX
+//                 ˇbbX
+//                 ˇb
+//                 cccc"
+//             },
+//             false,
+//         );
+//         assert_eq!(view.text(cx), expected_text);
+//         assert_eq!(view.selections.ranges(cx), expected_selections);
+//     });
+// }
+
+// #[gpui::test]
+// fn test_refresh_selections(cx: &mut TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let buffer = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, sample_text(3, 4, 'a')));
+//     let mut excerpt1_id = None;
+//     let multibuffer = cx.add_model(|cx| {
+//         let mut multibuffer = MultiBuffer::new(0);
+//         excerpt1_id = multibuffer
+//             .push_excerpts(
+//                 buffer.clone(),
+//                 [
+//                     ExcerptRange {
+//                         context: Point::new(0, 0)..Point::new(1, 4),
+//                         primary: None,
+//                     },
+//                     ExcerptRange {
+//                         context: Point::new(1, 0)..Point::new(2, 4),
+//                         primary: None,
+//                     },
+//                 ],
+//                 cx,
+//             )
+//             .into_iter()
+//             .next();
+//         assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
+//         multibuffer
+//     });
+
+//     let editor = cx
+//         .add_window(|cx| {
+//             let mut editor = build_editor(multibuffer.clone(), cx);
+//             let snapshot = editor.snapshot(cx);
+//             editor.change_selections(None, cx, |s| {
+//                 s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
+//             });
+//             editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
+//             assert_eq!(
+//                 editor.selections.ranges(cx),
+//                 [
+//                     Point::new(1, 3)..Point::new(1, 3),
+//                     Point::new(2, 1)..Point::new(2, 1),
+//                 ]
+//             );
+//             editor
+//         })
+//         .root(cx);
+
+//     // Refreshing selections is a no-op when excerpts haven't changed.
+//     editor.update(cx, |editor, cx| {
+//         editor.change_selections(None, cx, |s| s.refresh());
+//         assert_eq!(
+//             editor.selections.ranges(cx),
+//             [
+//                 Point::new(1, 3)..Point::new(1, 3),
+//                 Point::new(2, 1)..Point::new(2, 1),
+//             ]
+//         );
+//     });
+
+//     multibuffer.update(cx, |multibuffer, cx| {
+//         multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
+//     });
+//     editor.update(cx, |editor, cx| {
+//         // Removing an excerpt causes the first selection to become degenerate.
+//         assert_eq!(
+//             editor.selections.ranges(cx),
+//             [
+//                 Point::new(0, 0)..Point::new(0, 0),
+//                 Point::new(0, 1)..Point::new(0, 1)
+//             ]
+//         );
+
+//         // Refreshing selections will relocate the first selection to the original buffer
+//         // location.
+//         editor.change_selections(None, cx, |s| s.refresh());
+//         assert_eq!(
+//             editor.selections.ranges(cx),
+//             [
+//                 Point::new(0, 1)..Point::new(0, 1),
+//                 Point::new(0, 3)..Point::new(0, 3)
+//             ]
+//         );
+//         assert!(editor.selections.pending_anchor().is_some());
+//     });
+// }
+
+// #[gpui::test]
+// fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let buffer = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, sample_text(3, 4, 'a')));
+//     let mut excerpt1_id = None;
+//     let multibuffer = cx.add_model(|cx| {
+//         let mut multibuffer = MultiBuffer::new(0);
+//         excerpt1_id = multibuffer
+//             .push_excerpts(
+//                 buffer.clone(),
+//                 [
+//                     ExcerptRange {
+//                         context: Point::new(0, 0)..Point::new(1, 4),
+//                         primary: None,
+//                     },
+//                     ExcerptRange {
+//                         context: Point::new(1, 0)..Point::new(2, 4),
+//                         primary: None,
+//                     },
+//                 ],
+//                 cx,
+//             )
+//             .into_iter()
+//             .next();
+//         assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
+//         multibuffer
+//     });
+
+//     let editor = cx
+//         .add_window(|cx| {
+//             let mut editor = build_editor(multibuffer.clone(), cx);
+//             let snapshot = editor.snapshot(cx);
+//             editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
+//             assert_eq!(
+//                 editor.selections.ranges(cx),
+//                 [Point::new(1, 3)..Point::new(1, 3)]
+//             );
+//             editor
+//         })
+//         .root(cx);
+
+//     multibuffer.update(cx, |multibuffer, cx| {
+//         multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
+//     });
+//     editor.update(cx, |editor, cx| {
+//         assert_eq!(
+//             editor.selections.ranges(cx),
+//             [Point::new(0, 0)..Point::new(0, 0)]
+//         );
+
+//         // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
+//         editor.change_selections(None, cx, |s| s.refresh());
+//         assert_eq!(
+//             editor.selections.ranges(cx),
+//             [Point::new(0, 3)..Point::new(0, 3)]
+//         );
+//         assert!(editor.selections.pending_anchor().is_some());
+//     });
+// }
+
+// #[gpui::test]
+// async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let language = Arc::new(
+//         Language::new(
+//             LanguageConfig {
+//                 brackets: BracketPairConfig {
+//                     pairs: vec![
+//                         BracketPair {
+//                             start: "{".to_string(),
+//                             end: "}".to_string(),
+//                             close: true,
+//                             newline: true,
+//                         },
+//                         BracketPair {
+//                             start: "/* ".to_string(),
+//                             end: " */".to_string(),
+//                             close: true,
+//                             newline: true,
+//                         },
+//                     ],
+//                     ..Default::default()
+//                 },
+//                 ..Default::default()
+//             },
+//             Some(tree_sitter_rust::language()),
+//         )
+//         .with_indents_query("")
+//         .unwrap(),
+//     );
+
+//     let text = concat!(
+//         "{   }\n",     //
+//         "  x\n",       //
+//         "  /*   */\n", //
+//         "x\n",         //
+//         "{{} }\n",     //
+//     );
+
+//     let buffer =
+//         cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, text).with_language(language, cx));
+//     let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
+//     let view = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
+//     view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
+//         .await;
+
+//     view.update(cx, |view, cx| {
+//         view.change_selections(None, cx, |s| {
+//             s.select_display_ranges([
+//                 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
+//                 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
+//                 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
+//             ])
+//         });
+//         view.newline(&Newline, cx);
+
+//         assert_eq!(
+//             view.buffer().read(cx).read(cx).text(),
+//             concat!(
+//                 "{ \n",    // Suppress rustfmt
+//                 "\n",      //
+//                 "}\n",     //
+//                 "  x\n",   //
+//                 "  /* \n", //
+//                 "  \n",    //
+//                 "  */\n",  //
+//                 "x\n",     //
+//                 "{{} \n",  //
+//                 "}\n",     //
+//             )
+//         );
+//     });
+// }
+
+// #[gpui::test]
+// fn test_highlighted_ranges(cx: &mut TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let editor = cx
+//         .add_window(|cx| {
+//             let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
+//             build_editor(buffer.clone(), cx)
+//         })
+//         .root(cx);
+
+//     editor.update(cx, |editor, cx| {
+//         struct Type1;
+//         struct Type2;
+
+//         let buffer = editor.buffer.read(cx).snapshot(cx);
+
+//         let anchor_range =
+//             |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
+
+//         editor.highlight_background::<Type1>(
+//             vec![
+//                 anchor_range(Point::new(2, 1)..Point::new(2, 3)),
+//                 anchor_range(Point::new(4, 2)..Point::new(4, 4)),
+//                 anchor_range(Point::new(6, 3)..Point::new(6, 5)),
+//                 anchor_range(Point::new(8, 4)..Point::new(8, 6)),
+//             ],
+//             |_| Hsla::red(),
+//             cx,
+//         );
+//         editor.highlight_background::<Type2>(
+//             vec![
+//                 anchor_range(Point::new(3, 2)..Point::new(3, 5)),
+//                 anchor_range(Point::new(5, 3)..Point::new(5, 6)),
+//                 anchor_range(Point::new(7, 4)..Point::new(7, 7)),
+//                 anchor_range(Point::new(9, 5)..Point::new(9, 8)),
+//             ],
+//             |_| Hsla::green(),
+//             cx,
+//         );
+
+//         let snapshot = editor.snapshot(cx);
+//         let mut highlighted_ranges = editor.background_highlights_in_range(
+//             anchor_range(Point::new(3, 4)..Point::new(7, 4)),
+//             &snapshot,
+//             theme::current(cx).as_ref(),
+//         );
+//         // Enforce a consistent ordering based on color without relying on the ordering of the
+//         // highlight's `TypeId` which is non-deterministic.
+//         highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
+//         assert_eq!(
+//             highlighted_ranges,
+//             &[
+//                 (
+//                     DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
+//                     Hsla::green(),
+//                 ),
+//                 (
+//                     DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
+//                     Hsla::green(),
+//                 ),
+//                 (
+//                     DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
+//                     Hsla::red(),
+//                 ),
+//                 (
+//                     DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
+//                     Hsla::red(),
+//                 ),
+//             ]
+//         );
+//         assert_eq!(
+//             editor.background_highlights_in_range(
+//                 anchor_range(Point::new(5, 6)..Point::new(6, 4)),
+//                 &snapshot,
+//                 theme::current(cx).as_ref(),
+//             ),
+//             &[(
+//                 DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
+//                 Hsla::red(),
+//             )]
+//         );
+//     });
+// }
+
+// #[gpui::test]
+// async fn test_following(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let fs = FakeFs::new(cx.background());
+//     let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
+
+//     let buffer = project.update(cx, |project, cx| {
+//         let buffer = project
+//             .create_buffer(&sample_text(16, 8, 'a'), None, cx)
+//             .unwrap();
+//         cx.add_model(|cx| MultiBuffer::singleton(buffer, cx))
+//     });
+//     let leader = cx
+//         .add_window(|cx| build_editor(buffer.clone(), cx))
+//         .root(cx);
+//     let follower = cx
+//         .update(|cx| {
+//             cx.add_window(
+//                 WindowOptions {
+//                     bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))),
+//                     ..Default::default()
+//                 },
+//                 |cx| build_editor(buffer.clone(), cx),
+//             )
+//         })
+//         .root(cx);
+
+//     let is_still_following = Rc::new(RefCell::new(true));
+//     let follower_edit_event_count = Rc::new(RefCell::new(0));
+//     let pending_update = Rc::new(RefCell::new(None));
+//     follower.update(cx, {
+//         let update = pending_update.clone();
+//         let is_still_following = is_still_following.clone();
+//         let follower_edit_event_count = follower_edit_event_count.clone();
+//         |_, cx| {
+//             cx.subscribe(&leader, move |_, leader, event, cx| {
+//                 leader
+//                     .read(cx)
+//                     .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
+//             })
+//             .detach();
+
+//             cx.subscribe(&follower, move |_, _, event, cx| {
+//                 if Editor::should_unfollow_on_event(event, cx) {
+//                     *is_still_following.borrow_mut() = false;
+//                 }
+//                 if let Event::BufferEdited = event {
+//                     *follower_edit_event_count.borrow_mut() += 1;
+//                 }
+//             })
+//             .detach();
+//         }
+//     });
+
+//     // Update the selections only
+//     leader.update(cx, |leader, cx| {
+//         leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
+//     });
+//     follower
+//         .update(cx, |follower, cx| {
+//             follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
+//         })
+//         .await
+//         .unwrap();
+//     follower.read_with(cx, |follower, cx| {
+//         assert_eq!(follower.selections.ranges(cx), vec![1..1]);
+//     });
+//     assert_eq!(*is_still_following.borrow(), true);
+//     assert_eq!(*follower_edit_event_count.borrow(), 0);
+
+//     // Update the scroll position only
+//     leader.update(cx, |leader, cx| {
+//         leader.set_scroll_position(vec2f(1.5, 3.5), cx);
+//     });
+//     follower
+//         .update(cx, |follower, cx| {
+//             follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
+//         })
+//         .await
+//         .unwrap();
+//     assert_eq!(
+//         follower.update(cx, |follower, cx| follower.scroll_position(cx)),
+//         vec2f(1.5, 3.5)
+//     );
+//     assert_eq!(*is_still_following.borrow(), true);
+//     assert_eq!(*follower_edit_event_count.borrow(), 0);
+
+//     // Update the selections and scroll position. The follower's scroll position is updated
+//     // via autoscroll, not via the leader's exact scroll position.
+//     leader.update(cx, |leader, cx| {
+//         leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
+//         leader.request_autoscroll(Autoscroll::newest(), cx);
+//         leader.set_scroll_position(vec2f(1.5, 3.5), cx);
+//     });
+//     follower
+//         .update(cx, |follower, cx| {
+//             follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
+//         })
+//         .await
+//         .unwrap();
+//     follower.update(cx, |follower, cx| {
+//         assert_eq!(follower.scroll_position(cx), vec2f(1.5, 0.0));
+//         assert_eq!(follower.selections.ranges(cx), vec![0..0]);
+//     });
+//     assert_eq!(*is_still_following.borrow(), true);
+
+//     // Creating a pending selection that precedes another selection
+//     leader.update(cx, |leader, cx| {
+//         leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
+//         leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
+//     });
+//     follower
+//         .update(cx, |follower, cx| {
+//             follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
+//         })
+//         .await
+//         .unwrap();
+//     follower.read_with(cx, |follower, cx| {
+//         assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
+//     });
+//     assert_eq!(*is_still_following.borrow(), true);
+
+//     // Extend the pending selection so that it surrounds another selection
+//     leader.update(cx, |leader, cx| {
+//         leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
+//     });
+//     follower
+//         .update(cx, |follower, cx| {
+//             follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
+//         })
+//         .await
+//         .unwrap();
+//     follower.read_with(cx, |follower, cx| {
+//         assert_eq!(follower.selections.ranges(cx), vec![0..2]);
+//     });
+
+//     // Scrolling locally breaks the follow
+//     follower.update(cx, |follower, cx| {
+//         let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
+//         follower.set_scroll_anchor(
+//             ScrollAnchor {
+//                 anchor: top_anchor,
+//                 offset: vec2f(0.0, 0.5),
+//             },
+//             cx,
+//         );
+//     });
+//     assert_eq!(*is_still_following.borrow(), false);
+// }
+
+// #[gpui::test]
+// async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let fs = FakeFs::new(cx.background());
+//     let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
+//     let workspace = cx
+//         .add_window(|cx| Workspace::test_new(project.clone(), cx))
+//         .root(cx);
+//     let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
+
+//     let leader = pane.update(cx, |_, cx| {
+//         let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
+//         cx.add_view(|cx| build_editor(multibuffer.clone(), cx))
+//     });
+
+//     // Start following the editor when it has no excerpts.
+//     let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
+//     let follower_1 = cx
+//         .update(|cx| {
+//             Editor::from_state_proto(
+//                 pane.clone(),
+//                 workspace.clone(),
+//                 ViewId {
+//                     creator: Default::default(),
+//                     id: 0,
+//                 },
+//                 &mut state_message,
+//                 cx,
+//             )
+//         })
+//         .unwrap()
+//         .await
+//         .unwrap();
+
+//     let update_message = Rc::new(RefCell::new(None));
+//     follower_1.update(cx, {
+//         let update = update_message.clone();
+//         |_, cx| {
+//             cx.subscribe(&leader, move |_, leader, event, cx| {
+//                 leader
+//                     .read(cx)
+//                     .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
+//             })
+//             .detach();
+//         }
+//     });
+
+//     let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
+//         (
+//             project
+//                 .create_buffer("abc\ndef\nghi\njkl\n", None, cx)
+//                 .unwrap(),
+//             project
+//                 .create_buffer("mno\npqr\nstu\nvwx\n", None, cx)
+//                 .unwrap(),
+//         )
+//     });
+
+//     // Insert some excerpts.
+//     leader.update(cx, |leader, cx| {
+//         leader.buffer.update(cx, |multibuffer, cx| {
+//             let excerpt_ids = multibuffer.push_excerpts(
+//                 buffer_1.clone(),
+//                 [
+//                     ExcerptRange {
+//                         context: 1..6,
+//                         primary: None,
+//                     },
+//                     ExcerptRange {
+//                         context: 12..15,
+//                         primary: None,
+//                     },
+//                     ExcerptRange {
+//                         context: 0..3,
+//                         primary: None,
+//                     },
+//                 ],
+//                 cx,
+//             );
+//             multibuffer.insert_excerpts_after(
+//                 excerpt_ids[0],
+//                 buffer_2.clone(),
+//                 [
+//                     ExcerptRange {
+//                         context: 8..12,
+//                         primary: None,
+//                     },
+//                     ExcerptRange {
+//                         context: 0..6,
+//                         primary: None,
+//                     },
+//                 ],
+//                 cx,
+//             );
+//         });
+//     });
+
+//     // Apply the update of adding the excerpts.
+//     follower_1
+//         .update(cx, |follower, cx| {
+//             follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
+//         })
+//         .await
+//         .unwrap();
+//     assert_eq!(
+//         follower_1.read_with(cx, |editor, cx| editor.text(cx)),
+//         leader.read_with(cx, |editor, cx| editor.text(cx))
+//     );
+//     update_message.borrow_mut().take();
+
+//     // Start following separately after it already has excerpts.
+//     let mut state_message = leader.update(cx, |leader, cx| leader.to_state_proto(cx));
+//     let follower_2 = cx
+//         .update(|cx| {
+//             Editor::from_state_proto(
+//                 pane.clone(),
+//                 workspace.clone(),
+//                 ViewId {
+//                     creator: Default::default(),
+//                     id: 0,
+//                 },
+//                 &mut state_message,
+//                 cx,
+//             )
+//         })
+//         .unwrap()
+//         .await
+//         .unwrap();
+//     assert_eq!(
+//         follower_2.read_with(cx, |editor, cx| editor.text(cx)),
+//         leader.read_with(cx, |editor, cx| editor.text(cx))
+//     );
+
+//     // Remove some excerpts.
+//     leader.update(cx, |leader, cx| {
+//         leader.buffer.update(cx, |multibuffer, cx| {
+//             let excerpt_ids = multibuffer.excerpt_ids();
+//             multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
+//             multibuffer.remove_excerpts([excerpt_ids[0]], cx);
+//         });
+//     });
+
+//     // Apply the update of removing the excerpts.
+//     follower_1
+//         .update(cx, |follower, cx| {
+//             follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
+//         })
+//         .await
+//         .unwrap();
+//     follower_2
+//         .update(cx, |follower, cx| {
+//             follower.apply_update_proto(&project, update_message.borrow().clone().unwrap(), cx)
+//         })
+//         .await
+//         .unwrap();
+//     update_message.borrow_mut().take();
+//     assert_eq!(
+//         follower_1.read_with(cx, |editor, cx| editor.text(cx)),
+//         leader.read_with(cx, |editor, cx| editor.text(cx))
+//     );
+// }
+
+// #[test]
+// fn test_combine_syntax_and_fuzzy_match_highlights() {
+//     let string = "abcdefghijklmnop";
+//     let syntax_ranges = [
+//         (
+//             0..3,
+//             HighlightStyle {
+//                 color: Some(Hsla::red()),
+//                 ..Default::default()
+//             },
+//         ),
+//         (
+//             4..8,
+//             HighlightStyle {
+//                 color: Some(Hsla::green()),
+//                 ..Default::default()
+//             },
+//         ),
+//     ];
+//     let match_indices = [4, 6, 7, 8];
+//     assert_eq!(
+//         combine_syntax_and_fuzzy_match_highlights(
+//             string,
+//             Default::default(),
+//             syntax_ranges.into_iter(),
+//             &match_indices,
+//         ),
+//         &[
+//             (
+//                 0..3,
+//                 HighlightStyle {
+//                     color: Some(Hsla::red()),
+//                     ..Default::default()
+//                 },
+//             ),
+//             (
+//                 4..5,
+//                 HighlightStyle {
+//                     color: Some(Hsla::green()),
+//                     weight: Some(fonts::Weight::BOLD),
+//                     ..Default::default()
+//                 },
+//             ),
+//             (
+//                 5..6,
+//                 HighlightStyle {
+//                     color: Some(Hsla::green()),
+//                     ..Default::default()
+//                 },
+//             ),
+//             (
+//                 6..8,
+//                 HighlightStyle {
+//                     color: Some(Hsla::green()),
+//                     weight: Some(fonts::Weight::BOLD),
+//                     ..Default::default()
+//                 },
+//             ),
+//             (
+//                 8..9,
+//                 HighlightStyle {
+//                     weight: Some(fonts::Weight::BOLD),
+//                     ..Default::default()
+//                 },
+//             ),
+//         ]
+//     );
+// }
+
+// #[gpui::test]
+// async fn go_to_prev_overlapping_diagnostic(
+//     deterministic: Arc<Deterministic>,
+//     cx: &mut gpui::TestAppContext,
+// ) {
+//     init_test(cx, |_| {});
+
+//     let mut cx = EditorTestContext::new(cx).await;
+//     let project = cx.update_editor(|editor, _| editor.project.clone().unwrap());
+
+//     cx.set_state(indoc! {"
+//         ˇfn func(abc def: i32) -> u32 {
+//         }
+//     "});
+
+//     cx.update(|cx| {
+//         project.update(cx, |project, cx| {
+//             project
+//                 .update_diagnostics(
+//                     LanguageServerId(0),
+//                     lsp::PublishDiagnosticsParams {
+//                         uri: lsp::Url::from_file_path("/root/file").unwrap(),
+//                         version: None,
+//                         diagnostics: vec![
+//                             lsp::Diagnostic {
+//                                 range: lsp::Range::new(
+//                                     lsp::Position::new(0, 11),
+//                                     lsp::Position::new(0, 12),
+//                                 ),
+//                                 severity: Some(lsp::DiagnosticSeverity::ERROR),
+//                                 ..Default::default()
+//                             },
+//                             lsp::Diagnostic {
+//                                 range: lsp::Range::new(
+//                                     lsp::Position::new(0, 12),
+//                                     lsp::Position::new(0, 15),
+//                                 ),
+//                                 severity: Some(lsp::DiagnosticSeverity::ERROR),
+//                                 ..Default::default()
+//                             },
+//                             lsp::Diagnostic {
+//                                 range: lsp::Range::new(
+//                                     lsp::Position::new(0, 25),
+//                                     lsp::Position::new(0, 28),
+//                                 ),
+//                                 severity: Some(lsp::DiagnosticSeverity::ERROR),
+//                                 ..Default::default()
+//                             },
+//                         ],
+//                     },
+//                     &[],
+//                     cx,
+//                 )
+//                 .unwrap()
+//         });
+//     });
+
+//     deterministic.run_until_parked();
+
+//     cx.update_editor(|editor, cx| {
+//         editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
+//     });
+
+//     cx.assert_editor_state(indoc! {"
+//         fn func(abc def: i32) -> ˇu32 {
+//         }
+//     "});
+
+//     cx.update_editor(|editor, cx| {
+//         editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
+//     });
+
+//     cx.assert_editor_state(indoc! {"
+//         fn func(abc ˇdef: i32) -> u32 {
+//         }
+//     "});
+
+//     cx.update_editor(|editor, cx| {
+//         editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
+//     });
+
+//     cx.assert_editor_state(indoc! {"
+//         fn func(abcˇ def: i32) -> u32 {
+//         }
+//     "});
+
+//     cx.update_editor(|editor, cx| {
+//         editor.go_to_prev_diagnostic(&GoToPrevDiagnostic, cx);
+//     });
+
+//     cx.assert_editor_state(indoc! {"
+//         fn func(abc def: i32) -> ˇu32 {
+//         }
+//     "});
+// }
+
+// #[gpui::test]
+// async fn go_to_hunk(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let mut cx = EditorTestContext::new(cx).await;
+
+//     let diff_base = r#"
+//         use some::mod;
+
+//         const A: u32 = 42;
+
+//         fn main() {
+//             println!("hello");
+
+//             println!("world");
+//         }
+//         "#
+//     .unindent();
+
+//     // Edits are modified, removed, modified, added
+//     cx.set_state(
+//         &r#"
+//         use some::modified;
+
+//         ˇ
+//         fn main() {
+//             println!("hello there");
+
+//             println!("around the");
+//             println!("world");
+//         }
+//         "#
+//         .unindent(),
+//     );
+
+//     cx.set_diff_base(Some(&diff_base));
+//     deterministic.run_until_parked();
+
+//     cx.update_editor(|editor, cx| {
+//         //Wrap around the bottom of the buffer
+//         for _ in 0..3 {
+//             editor.go_to_hunk(&GoToHunk, cx);
+//         }
+//     });
+
+//     cx.assert_editor_state(
+//         &r#"
+//         ˇuse some::modified;
+
+//         fn main() {
+//             println!("hello there");
+
+//             println!("around the");
+//             println!("world");
+//         }
+//         "#
+//         .unindent(),
+//     );
+
+//     cx.update_editor(|editor, cx| {
+//         //Wrap around the top of the buffer
+//         for _ in 0..2 {
+//             editor.go_to_prev_hunk(&GoToPrevHunk, cx);
+//         }
+//     });
+
+//     cx.assert_editor_state(
+//         &r#"
+//         use some::modified;
+
+//         fn main() {
+//         ˇ    println!("hello there");
+
+//             println!("around the");
+//             println!("world");
+//         }
+//         "#
+//         .unindent(),
+//     );
+
+//     cx.update_editor(|editor, cx| {
+//         editor.go_to_prev_hunk(&GoToPrevHunk, cx);
+//     });
+
+//     cx.assert_editor_state(
+//         &r#"
+//         use some::modified;
+
+//         ˇ
+//         fn main() {
+//             println!("hello there");
+
+//             println!("around the");
+//             println!("world");
+//         }
+//         "#
+//         .unindent(),
+//     );
+
+//     cx.update_editor(|editor, cx| {
+//         for _ in 0..3 {
+//             editor.go_to_prev_hunk(&GoToPrevHunk, cx);
+//         }
+//     });
+
+//     cx.assert_editor_state(
+//         &r#"
+//         use some::modified;
+
+//         fn main() {
+//         ˇ    println!("hello there");
+
+//             println!("around the");
+//             println!("world");
+//         }
+//         "#
+//         .unindent(),
+//     );
+
+//     cx.update_editor(|editor, cx| {
+//         editor.fold(&Fold, cx);
+
+//         //Make sure that the fold only gets one hunk
+//         for _ in 0..4 {
+//             editor.go_to_hunk(&GoToHunk, cx);
+//         }
+//     });
+
+//     cx.assert_editor_state(
+//         &r#"
+//         ˇuse some::modified;
+
+//         fn main() {
+//             println!("hello there");
+
+//             println!("around the");
+//             println!("world");
+//         }
+//         "#
+//         .unindent(),
+//     );
+// }
+
+// #[test]
+// fn test_split_words() {
+//     fn split<'a>(text: &'a str) -> Vec<&'a str> {
+//         split_words(text).collect()
+//     }
+
+//     assert_eq!(split("HelloWorld"), &["Hello", "World"]);
+//     assert_eq!(split("hello_world"), &["hello_", "world"]);
+//     assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
+//     assert_eq!(split("Hello_World"), &["Hello_", "World"]);
+//     assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
+//     assert_eq!(split("helloworld"), &["helloworld"]);
+// }
+
+// #[gpui::test]
+// async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
+//     let mut assert = |before, after| {
+//         let _state_context = cx.set_state(before);
+//         cx.update_editor(|editor, cx| {
+//             editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, cx)
+//         });
+//         cx.assert_editor_state(after);
+//     };
+
+//     // Outside bracket jumps to outside of matching bracket
+//     assert("console.logˇ(var);", "console.log(var)ˇ;");
+//     assert("console.log(var)ˇ;", "console.logˇ(var);");
+
+//     // Inside bracket jumps to inside of matching bracket
+//     assert("console.log(ˇvar);", "console.log(varˇ);");
+//     assert("console.log(varˇ);", "console.log(ˇvar);");
+
+//     // When outside a bracket and inside, favor jumping to the inside bracket
+//     assert(
+//         "console.log('foo', [1, 2, 3]ˇ);",
+//         "console.log(ˇ'foo', [1, 2, 3]);",
+//     );
+//     assert(
+//         "console.log(ˇ'foo', [1, 2, 3]);",
+//         "console.log('foo', [1, 2, 3]ˇ);",
+//     );
+
+//     // Bias forward if two options are equally likely
+//     assert(
+//         "let result = curried_fun()ˇ();",
+//         "let result = curried_fun()()ˇ;",
+//     );
+
+//     // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
+//     assert(
+//         indoc! {"
+//             function test() {
+//                 console.log('test')ˇ
+//             }"},
+//         indoc! {"
+//             function test() {
+//                 console.logˇ('test')
+//             }"},
+//     );
+// }
+
+// #[gpui::test(iterations = 10)]
+// async fn test_copilot(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let (copilot, copilot_lsp) = Copilot::fake(cx);
+//     cx.update(|cx| cx.set_global(copilot));
+//     let mut cx = EditorLspTestContext::new_rust(
+//         lsp::ServerCapabilities {
+//             completion_provider: Some(lsp::CompletionOptions {
+//                 trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
+//                 ..Default::default()
+//             }),
+//             ..Default::default()
+//         },
+//         cx,
+//     )
+//     .await;
+
+//     // When inserting, ensure autocompletion is favored over Copilot suggestions.
+//     cx.set_state(indoc! {"
+//         oneˇ
+//         two
+//         three
+//     "});
+//     cx.simulate_keystroke(".");
+//     let _ = handle_completion_request(
+//         &mut cx,
+//         indoc! {"
+//             one.|<>
+//             two
+//             three
+//         "},
+//         vec!["completion_a", "completion_b"],
+//     );
+//     handle_copilot_completion_request(
+//         &copilot_lsp,
+//         vec![copilot::request::Completion {
+//             text: "one.copilot1".into(),
+//             range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
+//             ..Default::default()
+//         }],
+//         vec![],
+//     );
+//     deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
+//     cx.update_editor(|editor, cx| {
+//         assert!(editor.context_menu_visible());
+//         assert!(!editor.has_active_copilot_suggestion(cx));
+
+//         // Confirming a completion inserts it and hides the context menu, without showing
+//         // the copilot suggestion afterwards.
+//         editor
+//             .confirm_completion(&Default::default(), cx)
+//             .unwrap()
+//             .detach();
+//         assert!(!editor.context_menu_visible());
+//         assert!(!editor.has_active_copilot_suggestion(cx));
+//         assert_eq!(editor.text(cx), "one.completion_a\ntwo\nthree\n");
+//         assert_eq!(editor.display_text(cx), "one.completion_a\ntwo\nthree\n");
+//     });
+
+//     // Ensure Copilot suggestions are shown right away if no autocompletion is available.
+//     cx.set_state(indoc! {"
+//         oneˇ
+//         two
+//         three
+//     "});
+//     cx.simulate_keystroke(".");
+//     let _ = handle_completion_request(
+//         &mut cx,
+//         indoc! {"
+//             one.|<>
+//             two
+//             three
+//         "},
+//         vec![],
+//     );
+//     handle_copilot_completion_request(
+//         &copilot_lsp,
+//         vec![copilot::request::Completion {
+//             text: "one.copilot1".into(),
+//             range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
+//             ..Default::default()
+//         }],
+//         vec![],
+//     );
+//     deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
+//     cx.update_editor(|editor, cx| {
+//         assert!(!editor.context_menu_visible());
+//         assert!(editor.has_active_copilot_suggestion(cx));
+//         assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
+//         assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
+//     });
+
+//     // Reset editor, and ensure autocompletion is still favored over Copilot suggestions.
+//     cx.set_state(indoc! {"
+//         oneˇ
+//         two
+//         three
+//     "});
+//     cx.simulate_keystroke(".");
+//     let _ = handle_completion_request(
+//         &mut cx,
+//         indoc! {"
+//             one.|<>
+//             two
+//             three
+//         "},
+//         vec!["completion_a", "completion_b"],
+//     );
+//     handle_copilot_completion_request(
+//         &copilot_lsp,
+//         vec![copilot::request::Completion {
+//             text: "one.copilot1".into(),
+//             range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
+//             ..Default::default()
+//         }],
+//         vec![],
+//     );
+//     deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
+//     cx.update_editor(|editor, cx| {
+//         assert!(editor.context_menu_visible());
+//         assert!(!editor.has_active_copilot_suggestion(cx));
+
+//         // When hiding the context menu, the Copilot suggestion becomes visible.
+//         editor.hide_context_menu(cx);
+//         assert!(!editor.context_menu_visible());
+//         assert!(editor.has_active_copilot_suggestion(cx));
+//         assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
+//         assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
+//     });
+
+//     // Ensure existing completion is interpolated when inserting again.
+//     cx.simulate_keystroke("c");
+//     deterministic.run_until_parked();
+//     cx.update_editor(|editor, cx| {
+//         assert!(!editor.context_menu_visible());
+//         assert!(editor.has_active_copilot_suggestion(cx));
+//         assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
+//         assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
+//     });
+
+//     // After debouncing, new Copilot completions should be requested.
+//     handle_copilot_completion_request(
+//         &copilot_lsp,
+//         vec![copilot::request::Completion {
+//             text: "one.copilot2".into(),
+//             range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 5)),
+//             ..Default::default()
+//         }],
+//         vec![],
+//     );
+//     deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
+//     cx.update_editor(|editor, cx| {
+//         assert!(!editor.context_menu_visible());
+//         assert!(editor.has_active_copilot_suggestion(cx));
+//         assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
+//         assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
+
+//         // Canceling should remove the active Copilot suggestion.
+//         editor.cancel(&Default::default(), cx);
+//         assert!(!editor.has_active_copilot_suggestion(cx));
+//         assert_eq!(editor.display_text(cx), "one.c\ntwo\nthree\n");
+//         assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
+
+//         // After canceling, tabbing shouldn't insert the previously shown suggestion.
+//         editor.tab(&Default::default(), cx);
+//         assert!(!editor.has_active_copilot_suggestion(cx));
+//         assert_eq!(editor.display_text(cx), "one.c   \ntwo\nthree\n");
+//         assert_eq!(editor.text(cx), "one.c   \ntwo\nthree\n");
+
+//         // When undoing the previously active suggestion is shown again.
+//         editor.undo(&Default::default(), cx);
+//         assert!(editor.has_active_copilot_suggestion(cx));
+//         assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
+//         assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
+//     });
+
+//     // If an edit occurs outside of this editor, the suggestion is still correctly interpolated.
+//     cx.update_buffer(|buffer, cx| buffer.edit([(5..5, "o")], None, cx));
+//     cx.update_editor(|editor, cx| {
+//         assert!(editor.has_active_copilot_suggestion(cx));
+//         assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
+//         assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
+
+//         // Tabbing when there is an active suggestion inserts it.
+//         editor.tab(&Default::default(), cx);
+//         assert!(!editor.has_active_copilot_suggestion(cx));
+//         assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
+//         assert_eq!(editor.text(cx), "one.copilot2\ntwo\nthree\n");
+
+//         // When undoing the previously active suggestion is shown again.
+//         editor.undo(&Default::default(), cx);
+//         assert!(editor.has_active_copilot_suggestion(cx));
+//         assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
+//         assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
+
+//         // Hide suggestion.
+//         editor.cancel(&Default::default(), cx);
+//         assert!(!editor.has_active_copilot_suggestion(cx));
+//         assert_eq!(editor.display_text(cx), "one.co\ntwo\nthree\n");
+//         assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
+//     });
+
+//     // If an edit occurs outside of this editor but no suggestion is being shown,
+//     // we won't make it visible.
+//     cx.update_buffer(|buffer, cx| buffer.edit([(6..6, "p")], None, cx));
+//     cx.update_editor(|editor, cx| {
+//         assert!(!editor.has_active_copilot_suggestion(cx));
+//         assert_eq!(editor.display_text(cx), "one.cop\ntwo\nthree\n");
+//         assert_eq!(editor.text(cx), "one.cop\ntwo\nthree\n");
+//     });
+
+//     // Reset the editor to verify how suggestions behave when tabbing on leading indentation.
+//     cx.update_editor(|editor, cx| {
+//         editor.set_text("fn foo() {\n  \n}", cx);
+//         editor.change_selections(None, cx, |s| {
+//             s.select_ranges([Point::new(1, 2)..Point::new(1, 2)])
+//         });
+//     });
+//     handle_copilot_completion_request(
+//         &copilot_lsp,
+//         vec![copilot::request::Completion {
+//             text: "    let x = 4;".into(),
+//             range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
+//             ..Default::default()
+//         }],
+//         vec![],
+//     );
+
+//     cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
+//     deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
+//     cx.update_editor(|editor, cx| {
+//         assert!(editor.has_active_copilot_suggestion(cx));
+//         assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
+//         assert_eq!(editor.text(cx), "fn foo() {\n  \n}");
+
+//         // Tabbing inside of leading whitespace inserts indentation without accepting the suggestion.
+//         editor.tab(&Default::default(), cx);
+//         assert!(editor.has_active_copilot_suggestion(cx));
+//         assert_eq!(editor.text(cx), "fn foo() {\n    \n}");
+//         assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
+
+//         // Tabbing again accepts the suggestion.
+//         editor.tab(&Default::default(), cx);
+//         assert!(!editor.has_active_copilot_suggestion(cx));
+//         assert_eq!(editor.text(cx), "fn foo() {\n    let x = 4;\n}");
+//         assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
+//     });
+// }
+
+// #[gpui::test]
+// async fn test_copilot_completion_invalidation(
+//     deterministic: Arc<Deterministic>,
+//     cx: &mut gpui::TestAppContext,
+// ) {
+//     init_test(cx, |_| {});
+
+//     let (copilot, copilot_lsp) = Copilot::fake(cx);
+//     cx.update(|cx| cx.set_global(copilot));
+//     let mut cx = EditorLspTestContext::new_rust(
+//         lsp::ServerCapabilities {
+//             completion_provider: Some(lsp::CompletionOptions {
+//                 trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
+//                 ..Default::default()
+//             }),
+//             ..Default::default()
+//         },
+//         cx,
+//     )
+//     .await;
+
+//     cx.set_state(indoc! {"
+//         one
+//         twˇ
+//         three
+//     "});
+
+//     handle_copilot_completion_request(
+//         &copilot_lsp,
+//         vec![copilot::request::Completion {
+//             text: "two.foo()".into(),
+//             range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
+//             ..Default::default()
+//         }],
+//         vec![],
+//     );
+//     cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
+//     deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
+//     cx.update_editor(|editor, cx| {
+//         assert!(editor.has_active_copilot_suggestion(cx));
+//         assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
+//         assert_eq!(editor.text(cx), "one\ntw\nthree\n");
+
+//         editor.backspace(&Default::default(), cx);
+//         assert!(editor.has_active_copilot_suggestion(cx));
+//         assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
+//         assert_eq!(editor.text(cx), "one\nt\nthree\n");
+
+//         editor.backspace(&Default::default(), cx);
+//         assert!(editor.has_active_copilot_suggestion(cx));
+//         assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
+//         assert_eq!(editor.text(cx), "one\n\nthree\n");
+
+//         // Deleting across the original suggestion range invalidates it.
+//         editor.backspace(&Default::default(), cx);
+//         assert!(!editor.has_active_copilot_suggestion(cx));
+//         assert_eq!(editor.display_text(cx), "one\nthree\n");
+//         assert_eq!(editor.text(cx), "one\nthree\n");
+
+//         // Undoing the deletion restores the suggestion.
+//         editor.undo(&Default::default(), cx);
+//         assert!(editor.has_active_copilot_suggestion(cx));
+//         assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
+//         assert_eq!(editor.text(cx), "one\n\nthree\n");
+//     });
+// }
+
+// #[gpui::test]
+// async fn test_copilot_multibuffer(
+//     deterministic: Arc<Deterministic>,
+//     cx: &mut gpui::TestAppContext,
+// ) {
+//     init_test(cx, |_| {});
+
+//     let (copilot, copilot_lsp) = Copilot::fake(cx);
+//     cx.update(|cx| cx.set_global(copilot));
+
+//     let buffer_1 = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, "a = 1\nb = 2\n"));
+//     let buffer_2 = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, "c = 3\nd = 4\n"));
+//     let multibuffer = cx.add_model(|cx| {
+//         let mut multibuffer = MultiBuffer::new(0);
+//         multibuffer.push_excerpts(
+//             buffer_1.clone(),
+//             [ExcerptRange {
+//                 context: Point::new(0, 0)..Point::new(2, 0),
+//                 primary: None,
+//             }],
+//             cx,
+//         );
+//         multibuffer.push_excerpts(
+//             buffer_2.clone(),
+//             [ExcerptRange {
+//                 context: Point::new(0, 0)..Point::new(2, 0),
+//                 primary: None,
+//             }],
+//             cx,
+//         );
+//         multibuffer
+//     });
+//     let editor = cx.add_window(|cx| build_editor(multibuffer, cx)).root(cx);
+
+//     handle_copilot_completion_request(
+//         &copilot_lsp,
+//         vec![copilot::request::Completion {
+//             text: "b = 2 + a".into(),
+//             range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 5)),
+//             ..Default::default()
+//         }],
+//         vec![],
+//     );
+//     editor.update(cx, |editor, cx| {
+//         // Ensure copilot suggestions are shown for the first excerpt.
+//         editor.change_selections(None, cx, |s| {
+//             s.select_ranges([Point::new(1, 5)..Point::new(1, 5)])
+//         });
+//         editor.next_copilot_suggestion(&Default::default(), cx);
+//     });
+//     deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
+//     editor.update(cx, |editor, cx| {
+//         assert!(editor.has_active_copilot_suggestion(cx));
+//         assert_eq!(
+//             editor.display_text(cx),
+//             "\n\na = 1\nb = 2 + a\n\n\n\nc = 3\nd = 4\n"
+//         );
+//         assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
+//     });
+
+//     handle_copilot_completion_request(
+//         &copilot_lsp,
+//         vec![copilot::request::Completion {
+//             text: "d = 4 + c".into(),
+//             range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 6)),
+//             ..Default::default()
+//         }],
+//         vec![],
+//     );
+//     editor.update(cx, |editor, cx| {
+//         // Move to another excerpt, ensuring the suggestion gets cleared.
+//         editor.change_selections(None, cx, |s| {
+//             s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
+//         });
+//         assert!(!editor.has_active_copilot_suggestion(cx));
+//         assert_eq!(
+//             editor.display_text(cx),
+//             "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4\n"
+//         );
+//         assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
+
+//         // Type a character, ensuring we don't even try to interpolate the previous suggestion.
+//         editor.handle_input(" ", cx);
+//         assert!(!editor.has_active_copilot_suggestion(cx));
+//         assert_eq!(
+//             editor.display_text(cx),
+//             "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 \n"
+//         );
+//         assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
+//     });
+
+//     // Ensure the new suggestion is displayed when the debounce timeout expires.
+//     deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
+//     editor.update(cx, |editor, cx| {
+//         assert!(editor.has_active_copilot_suggestion(cx));
+//         assert_eq!(
+//             editor.display_text(cx),
+//             "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 + c\n"
+//         );
+//         assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
+//     });
+// }
+
+// #[gpui::test]
+// async fn test_copilot_disabled_globs(
+//     deterministic: Arc<Deterministic>,
+//     cx: &mut gpui::TestAppContext,
+// ) {
+//     init_test(cx, |settings| {
+//         settings
+//             .copilot
+//             .get_or_insert(Default::default())
+//             .disabled_globs = Some(vec![".env*".to_string()]);
+//     });
+
+//     let (copilot, copilot_lsp) = Copilot::fake(cx);
+//     cx.update(|cx| cx.set_global(copilot));
+
+//     let fs = FakeFs::new(cx.background());
+//     fs.insert_tree(
+//         "/test",
+//         json!({
+//             ".env": "SECRET=something\n",
+//             "README.md": "hello\n"
+//         }),
+//     )
+//     .await;
+//     let project = Project::test(fs, ["/test".as_ref()], cx).await;
+
+//     let private_buffer = project
+//         .update(cx, |project, cx| {
+//             project.open_local_buffer("/test/.env", cx)
+//         })
+//         .await
+//         .unwrap();
+//     let public_buffer = project
+//         .update(cx, |project, cx| {
+//             project.open_local_buffer("/test/README.md", cx)
+//         })
+//         .await
+//         .unwrap();
+
+//     let multibuffer = cx.add_model(|cx| {
+//         let mut multibuffer = MultiBuffer::new(0);
+//         multibuffer.push_excerpts(
+//             private_buffer.clone(),
+//             [ExcerptRange {
+//                 context: Point::new(0, 0)..Point::new(1, 0),
+//                 primary: None,
+//             }],
+//             cx,
+//         );
+//         multibuffer.push_excerpts(
+//             public_buffer.clone(),
+//             [ExcerptRange {
+//                 context: Point::new(0, 0)..Point::new(1, 0),
+//                 primary: None,
+//             }],
+//             cx,
+//         );
+//         multibuffer
+//     });
+//     let editor = cx.add_window(|cx| build_editor(multibuffer, cx)).root(cx);
+
+//     let mut copilot_requests = copilot_lsp
+//         .handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| async move {
+//             Ok(copilot::request::GetCompletionsResult {
+//                 completions: vec![copilot::request::Completion {
+//                     text: "next line".into(),
+//                     range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
+//                     ..Default::default()
+//                 }],
+//             })
+//         });
+
+//     editor.update(cx, |editor, cx| {
+//         editor.change_selections(None, cx, |selections| {
+//             selections.select_ranges([Point::new(0, 0)..Point::new(0, 0)])
+//         });
+//         editor.next_copilot_suggestion(&Default::default(), cx);
+//     });
+
+//     deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
+//     assert!(copilot_requests.try_next().is_err());
+
+//     editor.update(cx, |editor, cx| {
+//         editor.change_selections(None, cx, |s| {
+//             s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
+//         });
+//         editor.next_copilot_suggestion(&Default::default(), cx);
+//     });
+
+//     deterministic.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
+//     assert!(copilot_requests.try_next().is_ok());
+// }
+
+// #[gpui::test]
+// async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let mut language = Language::new(
+//         LanguageConfig {
+//             name: "Rust".into(),
+//             path_suffixes: vec!["rs".to_string()],
+//             brackets: BracketPairConfig {
+//                 pairs: vec![BracketPair {
+//                     start: "{".to_string(),
+//                     end: "}".to_string(),
+//                     close: true,
+//                     newline: true,
+//                 }],
+//                 disabled_scopes_by_bracket_ix: Vec::new(),
+//             },
+//             ..Default::default()
+//         },
+//         Some(tree_sitter_rust::language()),
+//     );
+//     let mut fake_servers = language
+//         .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
+//             capabilities: lsp::ServerCapabilities {
+//                 document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
+//                     first_trigger_character: "{".to_string(),
+//                     more_trigger_character: None,
+//                 }),
+//                 ..Default::default()
+//             },
+//             ..Default::default()
+//         }))
+//         .await;
+
+//     let fs = FakeFs::new(cx.background());
+//     fs.insert_tree(
+//         "/a",
+//         json!({
+//             "main.rs": "fn main() { let a = 5; }",
+//             "other.rs": "// Test file",
+//         }),
+//     )
+//     .await;
+//     let project = Project::test(fs, ["/a".as_ref()], cx).await;
+//     project.update(cx, |project, _| project.languages().add(Arc::new(language)));
+//     let workspace = cx
+//         .add_window(|cx| Workspace::test_new(project.clone(), cx))
+//         .root(cx);
+//     let worktree_id = workspace.update(cx, |workspace, cx| {
+//         workspace.project().read_with(cx, |project, cx| {
+//             project.worktrees(cx).next().unwrap().read(cx).id()
+//         })
+//     });
+
+//     let buffer = project
+//         .update(cx, |project, cx| {
+//             project.open_local_buffer("/a/main.rs", cx)
+//         })
+//         .await
+//         .unwrap();
+//     cx.foreground().run_until_parked();
+//     cx.foreground().start_waiting();
+//     let fake_server = fake_servers.next().await.unwrap();
+//     let editor_handle = workspace
+//         .update(cx, |workspace, cx| {
+//             workspace.open_path((worktree_id, "main.rs"), None, true, cx)
+//         })
+//         .await
+//         .unwrap()
+//         .downcast::<Editor>()
+//         .unwrap();
+
+//     fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
+//         assert_eq!(
+//             params.text_document_position.text_document.uri,
+//             lsp::Url::from_file_path("/a/main.rs").unwrap(),
+//         );
+//         assert_eq!(
+//             params.text_document_position.position,
+//             lsp::Position::new(0, 21),
+//         );
+
+//         Ok(Some(vec![lsp::TextEdit {
+//             new_text: "]".to_string(),
+//             range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
+//         }]))
+//     });
+
+//     editor_handle.update(cx, |editor, cx| {
+//         cx.focus(&editor_handle);
+//         editor.change_selections(None, cx, |s| {
+//             s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
+//         });
+//         editor.handle_input("{", cx);
+//     });
+
+//     cx.foreground().run_until_parked();
+
+//     buffer.read_with(cx, |buffer, _| {
+//         assert_eq!(
+//             buffer.text(),
+//             "fn main() { let a = {5}; }",
+//             "No extra braces from on type formatting should appear in the buffer"
+//         )
+//     });
+// }
+
+// #[gpui::test]
+// async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let language_name: Arc<str> = "Rust".into();
+//     let mut language = Language::new(
+//         LanguageConfig {
+//             name: Arc::clone(&language_name),
+//             path_suffixes: vec!["rs".to_string()],
+//             ..Default::default()
+//         },
+//         Some(tree_sitter_rust::language()),
+//     );
+
+//     let server_restarts = Arc::new(AtomicUsize::new(0));
+//     let closure_restarts = Arc::clone(&server_restarts);
+//     let language_server_name = "test language server";
+//     let mut fake_servers = language
+//         .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
+//             name: language_server_name,
+//             initialization_options: Some(json!({
+//                 "testOptionValue": true
+//             })),
+//             initializer: Some(Box::new(move |fake_server| {
+//                 let task_restarts = Arc::clone(&closure_restarts);
+//                 fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
+//                     task_restarts.fetch_add(1, atomic::Ordering::Release);
+//                     futures::future::ready(Ok(()))
+//                 });
+//             })),
+//             ..Default::default()
+//         }))
+//         .await;
+
+//     let fs = FakeFs::new(cx.background());
+//     fs.insert_tree(
+//         "/a",
+//         json!({
+//             "main.rs": "fn main() { let a = 5; }",
+//             "other.rs": "// Test file",
+//         }),
+//     )
+//     .await;
+//     let project = Project::test(fs, ["/a".as_ref()], cx).await;
+//     project.update(cx, |project, _| project.languages().add(Arc::new(language)));
+//     let _window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
+//     let _buffer = project
+//         .update(cx, |project, cx| {
+//             project.open_local_buffer("/a/main.rs", cx)
+//         })
+//         .await
+//         .unwrap();
+//     let _fake_server = fake_servers.next().await.unwrap();
+//     update_test_language_settings(cx, |language_settings| {
+//         language_settings.languages.insert(
+//             Arc::clone(&language_name),
+//             LanguageSettingsContent {
+//                 tab_size: NonZeroU32::new(8),
+//                 ..Default::default()
+//             },
+//         );
+//     });
+//     cx.foreground().run_until_parked();
+//     assert_eq!(
+//         server_restarts.load(atomic::Ordering::Acquire),
+//         0,
+//         "Should not restart LSP server on an unrelated change"
+//     );
+
+//     update_test_project_settings(cx, |project_settings| {
+//         project_settings.lsp.insert(
+//             "Some other server name".into(),
+//             LspSettings {
+//                 initialization_options: Some(json!({
+//                     "some other init value": false
+//                 })),
+//             },
+//         );
+//     });
+//     cx.foreground().run_until_parked();
+//     assert_eq!(
+//         server_restarts.load(atomic::Ordering::Acquire),
+//         0,
+//         "Should not restart LSP server on an unrelated LSP settings change"
+//     );
+
+//     update_test_project_settings(cx, |project_settings| {
+//         project_settings.lsp.insert(
+//             language_server_name.into(),
+//             LspSettings {
+//                 initialization_options: Some(json!({
+//                     "anotherInitValue": false
+//                 })),
+//             },
+//         );
+//     });
+//     cx.foreground().run_until_parked();
+//     assert_eq!(
+//         server_restarts.load(atomic::Ordering::Acquire),
+//         1,
+//         "Should restart LSP server on a related LSP settings change"
+//     );
+
+//     update_test_project_settings(cx, |project_settings| {
+//         project_settings.lsp.insert(
+//             language_server_name.into(),
+//             LspSettings {
+//                 initialization_options: Some(json!({
+//                     "anotherInitValue": false
+//                 })),
+//             },
+//         );
+//     });
+//     cx.foreground().run_until_parked();
+//     assert_eq!(
+//         server_restarts.load(atomic::Ordering::Acquire),
+//         1,
+//         "Should not restart LSP server on a related LSP settings change that is the same"
+//     );
+
+//     update_test_project_settings(cx, |project_settings| {
+//         project_settings.lsp.insert(
+//             language_server_name.into(),
+//             LspSettings {
+//                 initialization_options: None,
+//             },
+//         );
+//     });
+//     cx.foreground().run_until_parked();
+//     assert_eq!(
+//         server_restarts.load(atomic::Ordering::Acquire),
+//         2,
+//         "Should restart LSP server on another related LSP settings change"
+//     );
+// }
+
+// #[gpui::test]
+// async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let mut cx = EditorLspTestContext::new_rust(
+//         lsp::ServerCapabilities {
+//             completion_provider: Some(lsp::CompletionOptions {
+//                 trigger_characters: Some(vec![".".to_string()]),
+//                 resolve_provider: Some(true),
+//                 ..Default::default()
+//             }),
+//             ..Default::default()
+//         },
+//         cx,
+//     )
+//     .await;
+
+//     cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
+//     cx.simulate_keystroke(".");
+//     let completion_item = lsp::CompletionItem {
+//         label: "some".into(),
+//         kind: Some(lsp::CompletionItemKind::SNIPPET),
+//         detail: Some("Wrap the expression in an `Option::Some`".to_string()),
+//         documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
+//             kind: lsp::MarkupKind::Markdown,
+//             value: "```rust\nSome(2)\n```".to_string(),
+//         })),
+//         deprecated: Some(false),
+//         sort_text: Some("fffffff2".to_string()),
+//         filter_text: Some("some".to_string()),
+//         insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
+//         text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
+//             range: lsp::Range {
+//                 start: lsp::Position {
+//                     line: 0,
+//                     character: 22,
+//                 },
+//                 end: lsp::Position {
+//                     line: 0,
+//                     character: 22,
+//                 },
+//             },
+//             new_text: "Some(2)".to_string(),
+//         })),
+//         additional_text_edits: Some(vec![lsp::TextEdit {
+//             range: lsp::Range {
+//                 start: lsp::Position {
+//                     line: 0,
+//                     character: 20,
+//                 },
+//                 end: lsp::Position {
+//                     line: 0,
+//                     character: 22,
+//                 },
+//             },
+//             new_text: "".to_string(),
+//         }]),
+//         ..Default::default()
+//     };
+
+//     let closure_completion_item = completion_item.clone();
+//     let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
+//         let task_completion_item = closure_completion_item.clone();
+//         async move {
+//             Ok(Some(lsp::CompletionResponse::Array(vec![
+//                 task_completion_item,
+//             ])))
+//         }
+//     });
+
+//     request.next().await;
+
+//     cx.condition(|editor, _| editor.context_menu_visible())
+//         .await;
+//     let apply_additional_edits = cx.update_editor(|editor, cx| {
+//         editor
+//             .confirm_completion(&ConfirmCompletion::default(), cx)
+//             .unwrap()
+//     });
+//     cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
+
+//     cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
+//         let task_completion_item = completion_item.clone();
+//         async move { Ok(task_completion_item) }
+//     })
+//     .next()
+//     .await
+//     .unwrap();
+//     apply_additional_edits.await.unwrap();
+//     cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
+// }
+
+// #[gpui::test]
+// async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |_| {});
+
+//     let mut cx = EditorLspTestContext::new(
+//         Language::new(
+//             LanguageConfig {
+//                 path_suffixes: vec!["jsx".into()],
+//                 overrides: [(
+//                     "element".into(),
+//                     LanguageConfigOverride {
+//                         word_characters: Override::Set(['-'].into_iter().collect()),
+//                         ..Default::default()
+//                     },
+//                 )]
+//                 .into_iter()
+//                 .collect(),
+//                 ..Default::default()
+//             },
+//             Some(tree_sitter_typescript::language_tsx()),
+//         )
+//         .with_override_query("(jsx_self_closing_element) @element")
+//         .unwrap(),
+//         lsp::ServerCapabilities {
+//             completion_provider: Some(lsp::CompletionOptions {
+//                 trigger_characters: Some(vec![":".to_string()]),
+//                 ..Default::default()
+//             }),
+//             ..Default::default()
+//         },
+//         cx,
+//     )
+//     .await;
+
+//     cx.lsp
+//         .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
+//             Ok(Some(lsp::CompletionResponse::Array(vec![
+//                 lsp::CompletionItem {
+//                     label: "bg-blue".into(),
+//                     ..Default::default()
+//                 },
+//                 lsp::CompletionItem {
+//                     label: "bg-red".into(),
+//                     ..Default::default()
+//                 },
+//                 lsp::CompletionItem {
+//                     label: "bg-yellow".into(),
+//                     ..Default::default()
+//                 },
+//             ])))
+//         });
+
+//     cx.set_state(r#"<p class="bgˇ" />"#);
+
+//     // Trigger completion when typing a dash, because the dash is an extra
+//     // word character in the 'element' scope, which contains the cursor.
+//     cx.simulate_keystroke("-");
+//     cx.foreground().run_until_parked();
+//     cx.update_editor(|editor, _| {
+//         if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
+//             assert_eq!(
+//                 menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
+//                 &["bg-red", "bg-blue", "bg-yellow"]
+//             );
+//         } else {
+//             panic!("expected completion menu to be open");
+//         }
+//     });
+
+//     cx.simulate_keystroke("l");
+//     cx.foreground().run_until_parked();
+//     cx.update_editor(|editor, _| {
+//         if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
+//             assert_eq!(
+//                 menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
+//                 &["bg-blue", "bg-yellow"]
+//             );
+//         } else {
+//             panic!("expected completion menu to be open");
+//         }
+//     });
+
+//     // When filtering completions, consider the character after the '-' to
+//     // be the start of a subword.
+//     cx.set_state(r#"<p class="yelˇ" />"#);
+//     cx.simulate_keystroke("l");
+//     cx.foreground().run_until_parked();
+//     cx.update_editor(|editor, _| {
+//         if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() {
+//             assert_eq!(
+//                 menu.matches.iter().map(|m| &m.string).collect::<Vec<_>>(),
+//                 &["bg-yellow"]
+//             );
+//         } else {
+//             panic!("expected completion menu to be open");
+//         }
+//     });
+// }
+
+// #[gpui::test]
+// async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
+//     init_test(cx, |settings| {
+//         settings.defaults.formatter = Some(language_settings::Formatter::Prettier)
+//     });
+
+//     let mut language = Language::new(
+//         LanguageConfig {
+//             name: "Rust".into(),
+//             path_suffixes: vec!["rs".to_string()],
+//             prettier_parser_name: Some("test_parser".to_string()),
+//             ..Default::default()
+//         },
+//         Some(tree_sitter_rust::language()),
+//     );
+
+//     let test_plugin = "test_plugin";
+//     let _ = language
+//         .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
+//             prettier_plugins: vec![test_plugin],
+//             ..Default::default()
+//         }))
+//         .await;
+
+//     let fs = FakeFs::new(cx.background());
+//     fs.insert_file("/file.rs", Default::default()).await;
+
+//     let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
+//     let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
+//     project.update(cx, |project, _| {
+//         project.languages().add(Arc::new(language));
+//     });
+//     let buffer = project
+//         .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
+//         .await
+//         .unwrap();
+
+//     let buffer_text = "one\ntwo\nthree\n";
+//     let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
+//     let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
+//     editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
+
+//     let format = editor.update(cx, |editor, cx| {
+//         editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
+//     });
+//     format.await.unwrap();
+//     assert_eq!(
+//         editor.read_with(cx, |editor, cx| editor.text(cx)),
+//         buffer_text.to_string() + prettier_format_suffix,
+//         "Test prettier formatting was not applied to the original buffer text",
+//     );
+
+//     update_test_language_settings(cx, |settings| {
+//         settings.defaults.formatter = Some(language_settings::Formatter::Auto)
+//     });
+//     let format = editor.update(cx, |editor, cx| {
+//         editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
+//     });
+//     format.await.unwrap();
+//     assert_eq!(
+//         editor.read_with(cx, |editor, cx| editor.text(cx)),
+//         buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
+//         "Autoformatting (via test prettier) was not applied to the original buffer text",
+//     );
+// }
+
+// fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
+//     let point = DisplayPoint::new(row as u32, column as u32);
+//     point..point
+// }
+
+// fn assert_selection_ranges(marked_text: &str, view: &mut Editor, cx: &mut ViewContext<Editor>) {
+//     let (text, ranges) = marked_text_ranges(marked_text, true);
+//     assert_eq!(view.text(cx), text);
+//     assert_eq!(
+//         view.selections.ranges(cx),
+//         ranges,
+//         "Assert selections are {}",
+//         marked_text
+//     );
+// }
+
+// /// Handle completion request passing a marked string specifying where the completion
+// /// should be triggered from using '|' character, what range should be replaced, and what completions
+// /// should be returned using '<' and '>' to delimit the range
+// pub fn handle_completion_request<'a>(
+//     cx: &mut EditorLspTestContext<'a>,
+//     marked_string: &str,
+//     completions: Vec<&'static str>,
+// ) -> impl Future<Output = ()> {
+//     let complete_from_marker: TextRangeMarker = '|'.into();
+//     let replace_range_marker: TextRangeMarker = ('<', '>').into();
+//     let (_, mut marked_ranges) = marked_text_ranges_by(
+//         marked_string,
+//         vec![complete_from_marker.clone(), replace_range_marker.clone()],
+//     );
+
+//     let complete_from_position =
+//         cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
+//     let replace_range =
+//         cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
+
+//     let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
+//         let completions = completions.clone();
+//         async move {
+//             assert_eq!(params.text_document_position.text_document.uri, url.clone());
+//             assert_eq!(
+//                 params.text_document_position.position,
+//                 complete_from_position
+//             );
+//             Ok(Some(lsp::CompletionResponse::Array(
+//                 completions
+//                     .iter()
+//                     .map(|completion_text| lsp::CompletionItem {
+//                         label: completion_text.to_string(),
+//                         text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
+//                             range: replace_range,
+//                             new_text: completion_text.to_string(),
+//                         })),
+//                         ..Default::default()
+//                     })
+//                     .collect(),
+//             )))
+//         }
+//     });
+
+//     async move {
+//         request.next().await;
+//     }
+// }
+
+// fn handle_resolve_completion_request<'a>(
+//     cx: &mut EditorLspTestContext<'a>,
+//     edits: Option<Vec<(&'static str, &'static str)>>,
+// ) -> impl Future<Output = ()> {
+//     let edits = edits.map(|edits| {
+//         edits
+//             .iter()
+//             .map(|(marked_string, new_text)| {
+//                 let (_, marked_ranges) = marked_text_ranges(marked_string, false);
+//                 let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
+//                 lsp::TextEdit::new(replace_range, new_text.to_string())
+//             })
+//             .collect::<Vec<_>>()
+//     });
+
+//     let mut request =
+//         cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
+//             let edits = edits.clone();
+//             async move {
+//                 Ok(lsp::CompletionItem {
+//                     additional_text_edits: edits,
+//                     ..Default::default()
+//                 })
+//             }
+//         });
+
+//     async move {
+//         request.next().await;
+//     }
+// }
+
+// fn handle_copilot_completion_request(
+//     lsp: &lsp::FakeLanguageServer,
+//     completions: Vec<copilot::request::Completion>,
+//     completions_cycling: Vec<copilot::request::Completion>,
+// ) {
+//     lsp.handle_request::<copilot::request::GetCompletions, _, _>(move |_params, _cx| {
+//         let completions = completions.clone();
+//         async move {
+//             Ok(copilot::request::GetCompletionsResult {
+//                 completions: completions.clone(),
+//             })
+//         }
+//     });
+//     lsp.handle_request::<copilot::request::GetCompletionsCycling, _, _>(move |_params, _cx| {
+//         let completions_cycling = completions_cycling.clone();
+//         async move {
+//             Ok(copilot::request::GetCompletionsResult {
+//                 completions: completions_cycling.clone(),
+//             })
+//         }
+//     });
+// }
+
+// pub(crate) fn update_test_language_settings(
+//     cx: &mut TestAppContext,
+//     f: impl Fn(&mut AllLanguageSettingsContent),
+// ) {
+//     cx.update(|cx| {
+//         cx.update_global::<SettingsStore, _, _>(|store, cx| {
+//             store.update_user_settings::<AllLanguageSettings>(cx, f);
+//         });
+//     });
+// }
+
+// pub(crate) fn update_test_project_settings(
+//     cx: &mut TestAppContext,
+//     f: impl Fn(&mut ProjectSettings),
+// ) {
+//     cx.update(|cx| {
+//         cx.update_global::<SettingsStore, _, _>(|store, cx| {
+//             store.update_user_settings::<ProjectSettings>(cx, f);
+//         });
+//     });
+// }
+
+// pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
+//     cx.foreground().forbid_parking();
+
+//     cx.update(|cx| {
+//         cx.set_global(SettingsStore::test(cx));
+//         theme::init((), cx);
+//         client::init_settings(cx);
+//         language::init(cx);
+//         Project::init_settings(cx);
+//         workspace::init_settings(cx);
+//         crate::init(cx);
+//     });
+
+//     update_test_language_settings(cx, f);
+// }

crates/editor2/src/git.rs 🔗

@@ -88,195 +88,195 @@ pub fn diff_hunk_to_display(hunk: DiffHunk<u32>, snapshot: &DisplaySnapshot) ->
     }
 }
 
-#[cfg(any(test, feature = "test_support"))]
-mod tests {
-    use crate::editor_tests::init_test;
-    use crate::Point;
-    use gpui::TestAppContext;
-    use multi_buffer::{ExcerptRange, MultiBuffer};
-    use project::{FakeFs, Project};
-    use unindent::Unindent;
-    #[gpui::test]
-    async fn test_diff_hunks_in_range(cx: &mut TestAppContext) {
-        use git::diff::DiffHunkStatus;
-        init_test(cx, |_| {});
+// #[cfg(any(test, feature = "test_support"))]
+// mod tests {
+//     // use crate::editor_tests::init_test;
+//     use crate::Point;
+//     use gpui::TestAppContext;
+//     use multi_buffer::{ExcerptRange, MultiBuffer};
+//     use project::{FakeFs, Project};
+//     use unindent::Unindent;
+//     #[gpui::test]
+//     async fn test_diff_hunks_in_range(cx: &mut TestAppContext) {
+//         use git::diff::DiffHunkStatus;
+//         init_test(cx, |_| {});
 
-        let fs = FakeFs::new(cx.background());
-        let project = Project::test(fs, [], cx).await;
+//         let fs = FakeFs::new(cx.background());
+//         let project = Project::test(fs, [], cx).await;
 
-        // buffer has two modified hunks with two rows each
-        let buffer_1 = project
-            .update(cx, |project, cx| {
-                project.create_buffer(
-                    "
-                        1.zero
-                        1.ONE
-                        1.TWO
-                        1.three
-                        1.FOUR
-                        1.FIVE
-                        1.six
-                    "
-                    .unindent()
-                    .as_str(),
-                    None,
-                    cx,
-                )
-            })
-            .unwrap();
-        buffer_1.update(cx, |buffer, cx| {
-            buffer.set_diff_base(
-                Some(
-                    "
-                        1.zero
-                        1.one
-                        1.two
-                        1.three
-                        1.four
-                        1.five
-                        1.six
-                    "
-                    .unindent(),
-                ),
-                cx,
-            );
-        });
+//         // buffer has two modified hunks with two rows each
+//         let buffer_1 = project
+//             .update(cx, |project, cx| {
+//                 project.create_buffer(
+//                     "
+//                         1.zero
+//                         1.ONE
+//                         1.TWO
+//                         1.three
+//                         1.FOUR
+//                         1.FIVE
+//                         1.six
+//                     "
+//                     .unindent()
+//                     .as_str(),
+//                     None,
+//                     cx,
+//                 )
+//             })
+//             .unwrap();
+//         buffer_1.update(cx, |buffer, cx| {
+//             buffer.set_diff_base(
+//                 Some(
+//                     "
+//                         1.zero
+//                         1.one
+//                         1.two
+//                         1.three
+//                         1.four
+//                         1.five
+//                         1.six
+//                     "
+//                     .unindent(),
+//                 ),
+//                 cx,
+//             );
+//         });
 
-        // buffer has a deletion hunk and an insertion hunk
-        let buffer_2 = project
-            .update(cx, |project, cx| {
-                project.create_buffer(
-                    "
-                        2.zero
-                        2.one
-                        2.two
-                        2.three
-                        2.four
-                        2.five
-                        2.six
-                    "
-                    .unindent()
-                    .as_str(),
-                    None,
-                    cx,
-                )
-            })
-            .unwrap();
-        buffer_2.update(cx, |buffer, cx| {
-            buffer.set_diff_base(
-                Some(
-                    "
-                        2.zero
-                        2.one
-                        2.one-and-a-half
-                        2.two
-                        2.three
-                        2.four
-                        2.six
-                    "
-                    .unindent(),
-                ),
-                cx,
-            );
-        });
+//         // buffer has a deletion hunk and an insertion hunk
+//         let buffer_2 = project
+//             .update(cx, |project, cx| {
+//                 project.create_buffer(
+//                     "
+//                         2.zero
+//                         2.one
+//                         2.two
+//                         2.three
+//                         2.four
+//                         2.five
+//                         2.six
+//                     "
+//                     .unindent()
+//                     .as_str(),
+//                     None,
+//                     cx,
+//                 )
+//             })
+//             .unwrap();
+//         buffer_2.update(cx, |buffer, cx| {
+//             buffer.set_diff_base(
+//                 Some(
+//                     "
+//                         2.zero
+//                         2.one
+//                         2.one-and-a-half
+//                         2.two
+//                         2.three
+//                         2.four
+//                         2.six
+//                     "
+//                     .unindent(),
+//                 ),
+//                 cx,
+//             );
+//         });
 
-        cx.foreground().run_until_parked();
+//         cx.foreground().run_until_parked();
 
-        let multibuffer = cx.add_model(|cx| {
-            let mut multibuffer = MultiBuffer::new(0);
-            multibuffer.push_excerpts(
-                buffer_1.clone(),
-                [
-                    // excerpt ends in the middle of a modified hunk
-                    ExcerptRange {
-                        context: Point::new(0, 0)..Point::new(1, 5),
-                        primary: Default::default(),
-                    },
-                    // excerpt begins in the middle of a modified hunk
-                    ExcerptRange {
-                        context: Point::new(5, 0)..Point::new(6, 5),
-                        primary: Default::default(),
-                    },
-                ],
-                cx,
-            );
-            multibuffer.push_excerpts(
-                buffer_2.clone(),
-                [
-                    // excerpt ends at a deletion
-                    ExcerptRange {
-                        context: Point::new(0, 0)..Point::new(1, 5),
-                        primary: Default::default(),
-                    },
-                    // excerpt starts at a deletion
-                    ExcerptRange {
-                        context: Point::new(2, 0)..Point::new(2, 5),
-                        primary: Default::default(),
-                    },
-                    // excerpt fully contains a deletion hunk
-                    ExcerptRange {
-                        context: Point::new(1, 0)..Point::new(2, 5),
-                        primary: Default::default(),
-                    },
-                    // excerpt fully contains an insertion hunk
-                    ExcerptRange {
-                        context: Point::new(4, 0)..Point::new(6, 5),
-                        primary: Default::default(),
-                    },
-                ],
-                cx,
-            );
-            multibuffer
-        });
+//         let multibuffer = cx.add_model(|cx| {
+//             let mut multibuffer = MultiBuffer::new(0);
+//             multibuffer.push_excerpts(
+//                 buffer_1.clone(),
+//                 [
+//                     // excerpt ends in the middle of a modified hunk
+//                     ExcerptRange {
+//                         context: Point::new(0, 0)..Point::new(1, 5),
+//                         primary: Default::default(),
+//                     },
+//                     // excerpt begins in the middle of a modified hunk
+//                     ExcerptRange {
+//                         context: Point::new(5, 0)..Point::new(6, 5),
+//                         primary: Default::default(),
+//                     },
+//                 ],
+//                 cx,
+//             );
+//             multibuffer.push_excerpts(
+//                 buffer_2.clone(),
+//                 [
+//                     // excerpt ends at a deletion
+//                     ExcerptRange {
+//                         context: Point::new(0, 0)..Point::new(1, 5),
+//                         primary: Default::default(),
+//                     },
+//                     // excerpt starts at a deletion
+//                     ExcerptRange {
+//                         context: Point::new(2, 0)..Point::new(2, 5),
+//                         primary: Default::default(),
+//                     },
+//                     // excerpt fully contains a deletion hunk
+//                     ExcerptRange {
+//                         context: Point::new(1, 0)..Point::new(2, 5),
+//                         primary: Default::default(),
+//                     },
+//                     // excerpt fully contains an insertion hunk
+//                     ExcerptRange {
+//                         context: Point::new(4, 0)..Point::new(6, 5),
+//                         primary: Default::default(),
+//                     },
+//                 ],
+//                 cx,
+//             );
+//             multibuffer
+//         });
 
-        let snapshot = multibuffer.read_with(cx, |b, cx| b.snapshot(cx));
+//         let snapshot = multibuffer.read_with(cx, |b, cx| b.snapshot(cx));
 
-        assert_eq!(
-            snapshot.text(),
-            "
-                1.zero
-                1.ONE
-                1.FIVE
-                1.six
-                2.zero
-                2.one
-                2.two
-                2.one
-                2.two
-                2.four
-                2.five
-                2.six"
-                .unindent()
-        );
+//         assert_eq!(
+//             snapshot.text(),
+//             "
+//                 1.zero
+//                 1.ONE
+//                 1.FIVE
+//                 1.six
+//                 2.zero
+//                 2.one
+//                 2.two
+//                 2.one
+//                 2.two
+//                 2.four
+//                 2.five
+//                 2.six"
+//                 .unindent()
+//         );
 
-        let expected = [
-            (DiffHunkStatus::Modified, 1..2),
-            (DiffHunkStatus::Modified, 2..3),
-            //TODO: Define better when and where removed hunks show up at range extremities
-            (DiffHunkStatus::Removed, 6..6),
-            (DiffHunkStatus::Removed, 8..8),
-            (DiffHunkStatus::Added, 10..11),
-        ];
+//         let expected = [
+//             (DiffHunkStatus::Modified, 1..2),
+//             (DiffHunkStatus::Modified, 2..3),
+//             //TODO: Define better when and where removed hunks show up at range extremities
+//             (DiffHunkStatus::Removed, 6..6),
+//             (DiffHunkStatus::Removed, 8..8),
+//             (DiffHunkStatus::Added, 10..11),
+//         ];
 
-        assert_eq!(
-            snapshot
-                .git_diff_hunks_in_range(0..12)
-                .map(|hunk| (hunk.status(), hunk.buffer_range))
-                .collect::<Vec<_>>(),
-            &expected,
-        );
+//         assert_eq!(
+//             snapshot
+//                 .git_diff_hunks_in_range(0..12)
+//                 .map(|hunk| (hunk.status(), hunk.buffer_range))
+//                 .collect::<Vec<_>>(),
+//             &expected,
+//         );
 
-        assert_eq!(
-            snapshot
-                .git_diff_hunks_in_range_rev(0..12)
-                .map(|hunk| (hunk.status(), hunk.buffer_range))
-                .collect::<Vec<_>>(),
-            expected
-                .iter()
-                .rev()
-                .cloned()
-                .collect::<Vec<_>>()
-                .as_slice(),
-        );
-    }
-}
+//         assert_eq!(
+//             snapshot
+//                 .git_diff_hunks_in_range_rev(0..12)
+//                 .map(|hunk| (hunk.status(), hunk.buffer_range))
+//                 .collect::<Vec<_>>(),
+//             expected
+//                 .iter()
+//                 .rev()
+//                 .cloned()
+//                 .collect::<Vec<_>>()
+//                 .as_slice(),
+//         );
+//     }
+// }

crates/editor2/src/highlight_matching_bracket.rs 🔗

@@ -30,109 +30,109 @@ pub fn refresh_matching_bracket_highlights(editor: &mut Editor, cx: &mut ViewCon
     }
 }
 
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::{editor_tests::init_test, test::editor_lsp_test_context::EditorLspTestContext};
-    use indoc::indoc;
-    use language::{BracketPair, BracketPairConfig, Language, LanguageConfig};
+// #[cfg(test)]
+// mod tests {
+//     use super::*;
+//     use crate::{editor_tests::init_test, test::editor_lsp_test_context::EditorLspTestContext};
+//     use indoc::indoc;
+//     use language::{BracketPair, BracketPairConfig, Language, LanguageConfig};
 
-    #[gpui::test]
-    async fn test_matching_bracket_highlights(cx: &mut gpui::TestAppContext) {
-        init_test(cx, |_| {});
+//     #[gpui::test]
+//     async fn test_matching_bracket_highlights(cx: &mut gpui::TestAppContext) {
+//         init_test(cx, |_| {});
 
-        let mut cx = EditorLspTestContext::new(
-            Language::new(
-                LanguageConfig {
-                    name: "Rust".into(),
-                    path_suffixes: vec!["rs".to_string()],
-                    brackets: BracketPairConfig {
-                        pairs: vec![
-                            BracketPair {
-                                start: "{".to_string(),
-                                end: "}".to_string(),
-                                close: false,
-                                newline: true,
-                            },
-                            BracketPair {
-                                start: "(".to_string(),
-                                end: ")".to_string(),
-                                close: false,
-                                newline: true,
-                            },
-                        ],
-                        ..Default::default()
-                    },
-                    ..Default::default()
-                },
-                Some(tree_sitter_rust::language()),
-            )
-            .with_brackets_query(indoc! {r#"
-                ("{" @open "}" @close)
-                ("(" @open ")" @close)
-                "#})
-            .unwrap(),
-            Default::default(),
-            cx,
-        )
-        .await;
+//         let mut cx = EditorLspTestContext::new(
+//             Language::new(
+//                 LanguageConfig {
+//                     name: "Rust".into(),
+//                     path_suffixes: vec!["rs".to_string()],
+//                     brackets: BracketPairConfig {
+//                         pairs: vec![
+//                             BracketPair {
+//                                 start: "{".to_string(),
+//                                 end: "}".to_string(),
+//                                 close: false,
+//                                 newline: true,
+//                             },
+//                             BracketPair {
+//                                 start: "(".to_string(),
+//                                 end: ")".to_string(),
+//                                 close: false,
+//                                 newline: true,
+//                             },
+//                         ],
+//                         ..Default::default()
+//                     },
+//                     ..Default::default()
+//                 },
+//                 Some(tree_sitter_rust::language()),
+//             )
+//             .with_brackets_query(indoc! {r#"
+//                 ("{" @open "}" @close)
+//                 ("(" @open ")" @close)
+//                 "#})
+//             .unwrap(),
+//             Default::default(),
+//             cx,
+//         )
+//         .await;
 
-        // positioning cursor inside bracket highlights both
-        cx.set_state(indoc! {r#"
-            pub fn test("Test ˇargument") {
-                another_test(1, 2, 3);
-            }
-        "#});
-        cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
-            pub fn test«(»"Test argument"«)» {
-                another_test(1, 2, 3);
-            }
-        "#});
+//         // positioning cursor inside bracket highlights both
+//         cx.set_state(indoc! {r#"
+//             pub fn test("Test ˇargument") {
+//                 another_test(1, 2, 3);
+//             }
+//         "#});
+//         cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
+//             pub fn test«(»"Test argument"«)» {
+//                 another_test(1, 2, 3);
+//             }
+//         "#});
 
-        cx.set_state(indoc! {r#"
-            pub fn test("Test argument") {
-                another_test(1, ˇ2, 3);
-            }
-        "#});
-        cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
-            pub fn test("Test argument") {
-                another_test«(»1, 2, 3«)»;
-            }
-        "#});
+//         cx.set_state(indoc! {r#"
+//             pub fn test("Test argument") {
+//                 another_test(1, ˇ2, 3);
+//             }
+//         "#});
+//         cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
+//             pub fn test("Test argument") {
+//                 another_test«(»1, 2, 3«)»;
+//             }
+//         "#});
 
-        cx.set_state(indoc! {r#"
-            pub fn test("Test argument") {
-                anotherˇ_test(1, 2, 3);
-            }
-        "#});
-        cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
-            pub fn test("Test argument") «{»
-                another_test(1, 2, 3);
-            «}»
-        "#});
+//         cx.set_state(indoc! {r#"
+//             pub fn test("Test argument") {
+//                 anotherˇ_test(1, 2, 3);
+//             }
+//         "#});
+//         cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
+//             pub fn test("Test argument") «{»
+//                 another_test(1, 2, 3);
+//             «}»
+//         "#});
 
-        // positioning outside of brackets removes highlight
-        cx.set_state(indoc! {r#"
-            pub fˇn test("Test argument") {
-                another_test(1, 2, 3);
-            }
-        "#});
-        cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
-            pub fn test("Test argument") {
-                another_test(1, 2, 3);
-            }
-        "#});
+//         // positioning outside of brackets removes highlight
+//         cx.set_state(indoc! {r#"
+//             pub fˇn test("Test argument") {
+//                 another_test(1, 2, 3);
+//             }
+//         "#});
+//         cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
+//             pub fn test("Test argument") {
+//                 another_test(1, 2, 3);
+//             }
+//         "#});
 
-        // non empty selection dismisses highlight
-        cx.set_state(indoc! {r#"
-            pub fn test("Te«st argˇ»ument") {
-                another_test(1, 2, 3);
-            }
-        "#});
-        cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
-            pub fn test("Test argument") {
-                another_test(1, 2, 3);
-            }
-        "#});
-    }
-}
+//         // non empty selection dismisses highlight
+//         cx.set_state(indoc! {r#"
+//             pub fn test("Te«st argˇ»ument") {
+//                 another_test(1, 2, 3);
+//             }
+//         "#});
+//         cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
+//             pub fn test("Test argument") {
+//                 another_test(1, 2, 3);
+//             }
+//         "#});
+//     }
+// }

crates/editor2/src/hover_popover.rs 🔗

@@ -6,10 +6,7 @@ use crate::{
 };
 use futures::FutureExt;
 use gpui::{
-    actions,
-    elements::{Flex, MouseEventHandler, Padding, ParentElement, Text},
-    platform::{CursorStyle, MouseButton},
-    AnyElement, AppContext, Element, Model, Task, ViewContext, WeakViewHandle,
+    AnyElement, AppContext, CursorStyle, Element, Model, MouseButton, Task, ViewContext, WeakView,
 };
 use language::{
     markdown, Bias, DiagnosticEntry, DiagnosticSeverity, Language, LanguageRegistry, ParsedMarkdown,
@@ -26,22 +23,23 @@ pub const MIN_POPOVER_CHARACTER_WIDTH: f32 = 20.;
 pub const MIN_POPOVER_LINE_HEIGHT: f32 = 4.;
 pub const HOVER_POPOVER_GAP: f32 = 10.;
 
-actions!(editor, [Hover]);
+// actions!(editor, [Hover]);
 
 pub fn init(cx: &mut AppContext) {
-    cx.add_action(hover);
+    // cx.add_action(hover);
 }
 
-/// Bindable action which uses the most recent selection head to trigger a hover
-pub fn hover(editor: &mut Editor, _: &Hover, cx: &mut ViewContext<Editor>) {
-    let head = editor.selections.newest_display(cx).head();
-    show_hover(editor, head, true, cx);
-}
+// todo!()
+// /// Bindable action which uses the most recent selection head to trigger a hover
+// pub fn hover(editor: &mut Editor, _: &Hover, cx: &mut ViewContext<Editor>) {
+//     let head = editor.selections.newest_display(cx).head();
+//     show_hover(editor, head, true, cx);
+// }
 
 /// The internal hover action dispatches between `show_hover` or `hide_hover`
 /// depending on whether a point to hover over is provided.
 pub fn hover_at(editor: &mut Editor, point: Option<DisplayPoint>, cx: &mut ViewContext<Editor>) {
-    if settings::get::<EditorSettings>(cx).hover_popover_enabled {
+    if EditorSettings::get_global(cx).hover_popover_enabled {
         if let Some(point) = point {
             show_hover(editor, point, false, cx);
         } else {
@@ -79,7 +77,7 @@ pub fn find_hovered_hint_part(
 }
 
 pub fn hover_at_inlay(editor: &mut Editor, inlay_hover: InlayHover, cx: &mut ViewContext<Editor>) {
-    if settings::get::<EditorSettings>(cx).hover_popover_enabled {
+    if EditorSettings::get_global(cx).hover_popover_enabled {
         if editor.pending_rename.is_some() {
             return;
         }
@@ -423,7 +421,7 @@ impl HoverState {
         snapshot: &EditorSnapshot,
         style: &EditorStyle,
         visible_rows: Range<u32>,
-        workspace: Option<WeakViewHandle<Workspace>>,
+        workspace: Option<WeakView<Workspace>>,
         cx: &mut ViewContext<Editor>,
     ) -> Option<(DisplayPoint, Vec<AnyElement<Editor>>)> {
         // If there is a diagnostic, position the popovers based on that.
@@ -462,7 +460,7 @@ impl HoverState {
 
 #[derive(Debug, Clone)]
 pub struct InfoPopover {
-    pub project: ModelHandle<Project>,
+    pub project: Model<Project>,
     symbol_range: RangeInEditor,
     pub blocks: Vec<HoverBlock>,
     parsed_content: ParsedMarkdown,
@@ -472,7 +470,7 @@ impl InfoPopover {
     pub fn render(
         &mut self,
         style: &EditorStyle,
-        workspace: Option<WeakViewHandle<Workspace>>,
+        workspace: Option<WeakView<Workspace>>,
         cx: &mut ViewContext<Editor>,
     ) -> AnyElement<Editor> {
         MouseEventHandler::new::<InfoPopover, _>(0, cx, |_, cx| {
@@ -506,55 +504,56 @@ pub struct DiagnosticPopover {
 
 impl DiagnosticPopover {
     pub fn render(&self, style: &EditorStyle, cx: &mut ViewContext<Editor>) -> AnyElement<Editor> {
-        enum PrimaryDiagnostic {}
-
-        let mut text_style = style.hover_popover.prose.clone();
-        text_style.font_size = style.text.font_size;
-        let diagnostic_source_style = style.hover_popover.diagnostic_source_highlight.clone();
-
-        let text = match &self.local_diagnostic.diagnostic.source {
-            Some(source) => Text::new(
-                format!("{source}: {}", self.local_diagnostic.diagnostic.message),
-                text_style,
-            )
-            .with_highlights(vec![(0..source.len(), diagnostic_source_style)]),
-
-            None => Text::new(self.local_diagnostic.diagnostic.message.clone(), text_style),
-        };
-
-        let container_style = match self.local_diagnostic.diagnostic.severity {
-            DiagnosticSeverity::HINT => style.hover_popover.info_container,
-            DiagnosticSeverity::INFORMATION => style.hover_popover.info_container,
-            DiagnosticSeverity::WARNING => style.hover_popover.warning_container,
-            DiagnosticSeverity::ERROR => style.hover_popover.error_container,
-            _ => style.hover_popover.container,
-        };
-
-        let tooltip_style = theme::current(cx).tooltip.clone();
-
-        MouseEventHandler::new::<DiagnosticPopover, _>(0, cx, |_, _| {
-            text.with_soft_wrap(true)
-                .contained()
-                .with_style(container_style)
-        })
-        .with_padding(Padding {
-            top: HOVER_POPOVER_GAP,
-            bottom: HOVER_POPOVER_GAP,
-            ..Default::default()
-        })
-        .on_move(|_, _, _| {}) // Consume move events so they don't reach regions underneath.
-        .on_click(MouseButton::Left, |_, this, cx| {
-            this.go_to_diagnostic(&Default::default(), cx)
-        })
-        .with_cursor_style(CursorStyle::PointingHand)
-        .with_tooltip::<PrimaryDiagnostic>(
-            0,
-            "Go To Diagnostic".to_string(),
-            Some(Box::new(crate::GoToDiagnostic)),
-            tooltip_style,
-            cx,
-        )
-        .into_any()
+        todo!()
+        // enum PrimaryDiagnostic {}
+
+        // let mut text_style = style.hover_popover.prose.clone();
+        // text_style.font_size = style.text.font_size;
+        // let diagnostic_source_style = style.hover_popover.diagnostic_source_highlight.clone();
+
+        // let text = match &self.local_diagnostic.diagnostic.source {
+        //     Some(source) => Text::new(
+        //         format!("{source}: {}", self.local_diagnostic.diagnostic.message),
+        //         text_style,
+        //     )
+        //     .with_highlights(vec![(0..source.len(), diagnostic_source_style)]),
+
+        //     None => Text::new(self.local_diagnostic.diagnostic.message.clone(), text_style),
+        // };
+
+        // let container_style = match self.local_diagnostic.diagnostic.severity {
+        //     DiagnosticSeverity::HINT => style.hover_popover.info_container,
+        //     DiagnosticSeverity::INFORMATION => style.hover_popover.info_container,
+        //     DiagnosticSeverity::WARNING => style.hover_popover.warning_container,
+        //     DiagnosticSeverity::ERROR => style.hover_popover.error_container,
+        //     _ => style.hover_popover.container,
+        // };
+
+        // let tooltip_style = theme::current(cx).tooltip.clone();
+
+        // MouseEventHandler::new::<DiagnosticPopover, _>(0, cx, |_, _| {
+        //     text.with_soft_wrap(true)
+        //         .contained()
+        //         .with_style(container_style)
+        // })
+        // .with_padding(Padding {
+        //     top: HOVER_POPOVER_GAP,
+        //     bottom: HOVER_POPOVER_GAP,
+        //     ..Default::default()
+        // })
+        // .on_move(|_, _, _| {}) // Consume move events so they don't reach regions underneath.
+        // .on_click(MouseButton::Left, |_, this, cx| {
+        //     this.go_to_diagnostic(&Default::default(), cx)
+        // })
+        // .with_cursor_style(CursorStyle::PointingHand)
+        // .with_tooltip::<PrimaryDiagnostic>(
+        //     0,
+        //     "Go To Diagnostic".to_string(),
+        //     Some(Box::new(crate::GoToDiagnostic)),
+        //     tooltip_style,
+        //     cx,
+        // )
+        // .into_any()
     }
 
     pub fn activation_info(&self) -> (usize, Anchor) {
@@ -567,763 +566,763 @@ impl DiagnosticPopover {
     }
 }
 
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::{
-        editor_tests::init_test,
-        element::PointForPosition,
-        inlay_hint_cache::tests::{cached_hint_labels, visible_hint_labels},
-        link_go_to_definition::update_inlay_link_and_hover_points,
-        test::editor_lsp_test_context::EditorLspTestContext,
-        InlayId,
-    };
-    use collections::BTreeSet;
-    use gpui::fonts::{HighlightStyle, Underline, Weight};
-    use indoc::indoc;
-    use language::{language_settings::InlayHintSettings, Diagnostic, DiagnosticSet};
-    use lsp::LanguageServerId;
-    use project::{HoverBlock, HoverBlockKind};
-    use smol::stream::StreamExt;
-    use unindent::Unindent;
-    use util::test::marked_text_ranges;
-
-    #[gpui::test]
-    async fn test_mouse_hover_info_popover(cx: &mut gpui::TestAppContext) {
-        init_test(cx, |_| {});
-
-        let mut cx = EditorLspTestContext::new_rust(
-            lsp::ServerCapabilities {
-                hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
-                ..Default::default()
-            },
-            cx,
-        )
-        .await;
-
-        // Basic hover delays and then pops without moving the mouse
-        cx.set_state(indoc! {"
-            fn ˇtest() { println!(); }
-        "});
-        let hover_point = cx.display_point(indoc! {"
-            fn test() { printˇln!(); }
-        "});
-
-        cx.update_editor(|editor, cx| hover_at(editor, Some(hover_point), cx));
-        assert!(!cx.editor(|editor, _| editor.hover_state.visible()));
-
-        // After delay, hover should be visible.
-        let symbol_range = cx.lsp_range(indoc! {"
-            fn test() { «println!»(); }
-        "});
-        let mut requests =
-            cx.handle_request::<lsp::request::HoverRequest, _, _>(move |_, _, _| async move {
-                Ok(Some(lsp::Hover {
-                    contents: lsp::HoverContents::Markup(lsp::MarkupContent {
-                        kind: lsp::MarkupKind::Markdown,
-                        value: "some basic docs".to_string(),
-                    }),
-                    range: Some(symbol_range),
-                }))
-            });
-        cx.foreground()
-            .advance_clock(Duration::from_millis(HOVER_DELAY_MILLIS + 100));
-        requests.next().await;
-
-        cx.editor(|editor, _| {
-            assert!(editor.hover_state.visible());
-            assert_eq!(
-                editor.hover_state.info_popover.clone().unwrap().blocks,
-                vec![HoverBlock {
-                    text: "some basic docs".to_string(),
-                    kind: HoverBlockKind::Markdown,
-                },]
-            )
-        });
-
-        // Mouse moved with no hover response dismisses
-        let hover_point = cx.display_point(indoc! {"
-            fn teˇst() { println!(); }
-        "});
-        let mut request = cx
-            .lsp
-            .handle_request::<lsp::request::HoverRequest, _, _>(|_, _| async move { Ok(None) });
-        cx.update_editor(|editor, cx| hover_at(editor, Some(hover_point), cx));
-        cx.foreground()
-            .advance_clock(Duration::from_millis(HOVER_DELAY_MILLIS + 100));
-        request.next().await;
-        cx.editor(|editor, _| {
-            assert!(!editor.hover_state.visible());
-        });
-    }
-
-    #[gpui::test]
-    async fn test_keyboard_hover_info_popover(cx: &mut gpui::TestAppContext) {
-        init_test(cx, |_| {});
-
-        let mut cx = EditorLspTestContext::new_rust(
-            lsp::ServerCapabilities {
-                hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
-                ..Default::default()
-            },
-            cx,
-        )
-        .await;
-
-        // Hover with keyboard has no delay
-        cx.set_state(indoc! {"
-            fˇn test() { println!(); }
-        "});
-        cx.update_editor(|editor, cx| hover(editor, &Hover, cx));
-        let symbol_range = cx.lsp_range(indoc! {"
-            «fn» test() { println!(); }
-        "});
-        cx.handle_request::<lsp::request::HoverRequest, _, _>(move |_, _, _| async move {
-            Ok(Some(lsp::Hover {
-                contents: lsp::HoverContents::Markup(lsp::MarkupContent {
-                    kind: lsp::MarkupKind::Markdown,
-                    value: "some other basic docs".to_string(),
-                }),
-                range: Some(symbol_range),
-            }))
-        })
-        .next()
-        .await;
-
-        cx.condition(|editor, _| editor.hover_state.visible()).await;
-        cx.editor(|editor, _| {
-            assert_eq!(
-                editor.hover_state.info_popover.clone().unwrap().blocks,
-                vec![HoverBlock {
-                    text: "some other basic docs".to_string(),
-                    kind: HoverBlockKind::Markdown,
-                }]
-            )
-        });
-    }
-
-    #[gpui::test]
-    async fn test_empty_hovers_filtered(cx: &mut gpui::TestAppContext) {
-        init_test(cx, |_| {});
-
-        let mut cx = EditorLspTestContext::new_rust(
-            lsp::ServerCapabilities {
-                hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
-                ..Default::default()
-            },
-            cx,
-        )
-        .await;
-
-        // Hover with keyboard has no delay
-        cx.set_state(indoc! {"
-            fˇn test() { println!(); }
-        "});
-        cx.update_editor(|editor, cx| hover(editor, &Hover, cx));
-        let symbol_range = cx.lsp_range(indoc! {"
-            «fn» test() { println!(); }
-        "});
-        cx.handle_request::<lsp::request::HoverRequest, _, _>(move |_, _, _| async move {
-            Ok(Some(lsp::Hover {
-                contents: lsp::HoverContents::Array(vec![
-                    lsp::MarkedString::String("regular text for hover to show".to_string()),
-                    lsp::MarkedString::String("".to_string()),
-                    lsp::MarkedString::LanguageString(lsp::LanguageString {
-                        language: "Rust".to_string(),
-                        value: "".to_string(),
-                    }),
-                ]),
-                range: Some(symbol_range),
-            }))
-        })
-        .next()
-        .await;
-
-        cx.condition(|editor, _| editor.hover_state.visible()).await;
-        cx.editor(|editor, _| {
-            assert_eq!(
-                editor.hover_state.info_popover.clone().unwrap().blocks,
-                vec![HoverBlock {
-                    text: "regular text for hover to show".to_string(),
-                    kind: HoverBlockKind::Markdown,
-                }],
-                "No empty string hovers should be shown"
-            );
-        });
-    }
-
-    #[gpui::test]
-    async fn test_line_ends_trimmed(cx: &mut gpui::TestAppContext) {
-        init_test(cx, |_| {});
-
-        let mut cx = EditorLspTestContext::new_rust(
-            lsp::ServerCapabilities {
-                hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
-                ..Default::default()
-            },
-            cx,
-        )
-        .await;
-
-        // Hover with keyboard has no delay
-        cx.set_state(indoc! {"
-            fˇn test() { println!(); }
-        "});
-        cx.update_editor(|editor, cx| hover(editor, &Hover, cx));
-        let symbol_range = cx.lsp_range(indoc! {"
-            «fn» test() { println!(); }
-        "});
-
-        let code_str = "\nlet hovered_point: Vector2F // size = 8, align = 0x4\n";
-        let markdown_string = format!("\n```rust\n{code_str}```");
-
-        let closure_markdown_string = markdown_string.clone();
-        cx.handle_request::<lsp::request::HoverRequest, _, _>(move |_, _, _| {
-            let future_markdown_string = closure_markdown_string.clone();
-            async move {
-                Ok(Some(lsp::Hover {
-                    contents: lsp::HoverContents::Markup(lsp::MarkupContent {
-                        kind: lsp::MarkupKind::Markdown,
-                        value: future_markdown_string,
-                    }),
-                    range: Some(symbol_range),
-                }))
-            }
-        })
-        .next()
-        .await;
-
-        cx.condition(|editor, _| editor.hover_state.visible()).await;
-        cx.editor(|editor, _| {
-            let blocks = editor.hover_state.info_popover.clone().unwrap().blocks;
-            assert_eq!(
-                blocks,
-                vec![HoverBlock {
-                    text: markdown_string,
-                    kind: HoverBlockKind::Markdown,
-                }],
-            );
-
-            let rendered = smol::block_on(parse_blocks(&blocks, &Default::default(), None));
-            assert_eq!(
-                rendered.text,
-                code_str.trim(),
-                "Should not have extra line breaks at end of rendered hover"
-            );
-        });
-    }
-
-    #[gpui::test]
-    async fn test_hover_diagnostic_and_info_popovers(cx: &mut gpui::TestAppContext) {
-        init_test(cx, |_| {});
-
-        let mut cx = EditorLspTestContext::new_rust(
-            lsp::ServerCapabilities {
-                hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
-                ..Default::default()
-            },
-            cx,
-        )
-        .await;
-
-        // Hover with just diagnostic, pops DiagnosticPopover immediately and then
-        // info popover once request completes
-        cx.set_state(indoc! {"
-            fn teˇst() { println!(); }
-        "});
-
-        // Send diagnostic to client
-        let range = cx.text_anchor_range(indoc! {"
-            fn «test»() { println!(); }
-        "});
-        cx.update_buffer(|buffer, cx| {
-            let snapshot = buffer.text_snapshot();
-            let set = DiagnosticSet::from_sorted_entries(
-                vec![DiagnosticEntry {
-                    range,
-                    diagnostic: Diagnostic {
-                        message: "A test diagnostic message.".to_string(),
-                        ..Default::default()
-                    },
-                }],
-                &snapshot,
-            );
-            buffer.update_diagnostics(LanguageServerId(0), set, cx);
-        });
-
-        // Hover pops diagnostic immediately
-        cx.update_editor(|editor, cx| hover(editor, &Hover, cx));
-        cx.foreground().run_until_parked();
-
-        cx.editor(|Editor { hover_state, .. }, _| {
-            assert!(hover_state.diagnostic_popover.is_some() && hover_state.info_popover.is_none())
-        });
-
-        // Info Popover shows after request responded to
-        let range = cx.lsp_range(indoc! {"
-            fn «test»() { println!(); }
-        "});
-        cx.handle_request::<lsp::request::HoverRequest, _, _>(move |_, _, _| async move {
-            Ok(Some(lsp::Hover {
-                contents: lsp::HoverContents::Markup(lsp::MarkupContent {
-                    kind: lsp::MarkupKind::Markdown,
-                    value: "some new docs".to_string(),
-                }),
-                range: Some(range),
-            }))
-        });
-        cx.foreground()
-            .advance_clock(Duration::from_millis(HOVER_DELAY_MILLIS + 100));
-
-        cx.foreground().run_until_parked();
-        cx.editor(|Editor { hover_state, .. }, _| {
-            hover_state.diagnostic_popover.is_some() && hover_state.info_task.is_some()
-        });
-    }
-
-    #[gpui::test]
-    fn test_render_blocks(cx: &mut gpui::TestAppContext) {
-        init_test(cx, |_| {});
-
-        cx.add_window(|cx| {
-            let editor = Editor::single_line(None, cx);
-            let style = editor.style(cx);
-
-            struct Row {
-                blocks: Vec<HoverBlock>,
-                expected_marked_text: String,
-                expected_styles: Vec<HighlightStyle>,
-            }
-
-            let rows = &[
-                // Strong emphasis
-                Row {
-                    blocks: vec![HoverBlock {
-                        text: "one **two** three".to_string(),
-                        kind: HoverBlockKind::Markdown,
-                    }],
-                    expected_marked_text: "one «two» three".to_string(),
-                    expected_styles: vec![HighlightStyle {
-                        weight: Some(Weight::BOLD),
-                        ..Default::default()
-                    }],
-                },
-                // Links
-                Row {
-                    blocks: vec![HoverBlock {
-                        text: "one [two](https://the-url) three".to_string(),
-                        kind: HoverBlockKind::Markdown,
-                    }],
-                    expected_marked_text: "one «two» three".to_string(),
-                    expected_styles: vec![HighlightStyle {
-                        underline: Some(Underline {
-                            thickness: 1.0.into(),
-                            ..Default::default()
-                        }),
-                        ..Default::default()
-                    }],
-                },
-                // Lists
-                Row {
-                    blocks: vec![HoverBlock {
-                        text: "
-                            lists:
-                            * one
-                                - a
-                                - b
-                            * two
-                                - [c](https://the-url)
-                                - d"
-                        .unindent(),
-                        kind: HoverBlockKind::Markdown,
-                    }],
-                    expected_marked_text: "
-                        lists:
-                        - one
-                          - a
-                          - b
-                        - two
-                          - «c»
-                          - d"
-                    .unindent(),
-                    expected_styles: vec![HighlightStyle {
-                        underline: Some(Underline {
-                            thickness: 1.0.into(),
-                            ..Default::default()
-                        }),
-                        ..Default::default()
-                    }],
-                },
-                // Multi-paragraph list items
-                Row {
-                    blocks: vec![HoverBlock {
-                        text: "
-                            * one two
-                              three
-
-                            * four five
-                                * six seven
-                                  eight
-
-                                  nine
-                                * ten
-                            * six"
-                            .unindent(),
-                        kind: HoverBlockKind::Markdown,
-                    }],
-                    expected_marked_text: "
-                        - one two three
-                        - four five
-                          - six seven eight
-
-                            nine
-                          - ten
-                        - six"
-                        .unindent(),
-                    expected_styles: vec![HighlightStyle {
-                        underline: Some(Underline {
-                            thickness: 1.0.into(),
-                            ..Default::default()
-                        }),
-                        ..Default::default()
-                    }],
-                },
-            ];
-
-            for Row {
-                blocks,
-                expected_marked_text,
-                expected_styles,
-            } in &rows[0..]
-            {
-                let rendered = smol::block_on(parse_blocks(&blocks, &Default::default(), None));
-
-                let (expected_text, ranges) = marked_text_ranges(expected_marked_text, false);
-                let expected_highlights = ranges
-                    .into_iter()
-                    .zip(expected_styles.iter().cloned())
-                    .collect::<Vec<_>>();
-                assert_eq!(
-                    rendered.text, expected_text,
-                    "wrong text for input {blocks:?}"
-                );
-
-                let rendered_highlights: Vec<_> = rendered
-                    .highlights
-                    .iter()
-                    .filter_map(|(range, highlight)| {
-                        let highlight = highlight.to_highlight_style(&style.syntax)?;
-                        Some((range.clone(), highlight))
-                    })
-                    .collect();
-
-                assert_eq!(
-                    rendered_highlights, expected_highlights,
-                    "wrong highlights for input {blocks:?}"
-                );
-            }
-
-            editor
-        });
-    }
-
-    #[gpui::test]
-    async fn test_hover_inlay_label_parts(cx: &mut gpui::TestAppContext) {
-        init_test(cx, |settings| {
-            settings.defaults.inlay_hints = Some(InlayHintSettings {
-                enabled: true,
-                show_type_hints: true,
-                show_parameter_hints: true,
-                show_other_hints: true,
-            })
-        });
-
-        let mut cx = EditorLspTestContext::new_rust(
-            lsp::ServerCapabilities {
-                inlay_hint_provider: Some(lsp::OneOf::Right(
-                    lsp::InlayHintServerCapabilities::Options(lsp::InlayHintOptions {
-                        resolve_provider: Some(true),
-                        ..Default::default()
-                    }),
-                )),
-                ..Default::default()
-            },
-            cx,
-        )
-        .await;
-
-        cx.set_state(indoc! {"
-            struct TestStruct;
-
-            // ==================
-
-            struct TestNewType<T>(T);
-
-            fn main() {
-                let variableˇ = TestNewType(TestStruct);
-            }
-        "});
-
-        let hint_start_offset = cx.ranges(indoc! {"
-            struct TestStruct;
-
-            // ==================
-
-            struct TestNewType<T>(T);
-
-            fn main() {
-                let variableˇ = TestNewType(TestStruct);
-            }
-        "})[0]
-            .start;
-        let hint_position = cx.to_lsp(hint_start_offset);
-        let new_type_target_range = cx.lsp_range(indoc! {"
-            struct TestStruct;
-
-            // ==================
-
-            struct «TestNewType»<T>(T);
-
-            fn main() {
-                let variable = TestNewType(TestStruct);
-            }
-        "});
-        let struct_target_range = cx.lsp_range(indoc! {"
-            struct «TestStruct»;
-
-            // ==================
-
-            struct TestNewType<T>(T);
-
-            fn main() {
-                let variable = TestNewType(TestStruct);
-            }
-        "});
-
-        let uri = cx.buffer_lsp_url.clone();
-        let new_type_label = "TestNewType";
-        let struct_label = "TestStruct";
-        let entire_hint_label = ": TestNewType<TestStruct>";
-        let closure_uri = uri.clone();
-        cx.lsp
-            .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
-                let task_uri = closure_uri.clone();
-                async move {
-                    assert_eq!(params.text_document.uri, task_uri);
-                    Ok(Some(vec![lsp::InlayHint {
-                        position: hint_position,
-                        label: lsp::InlayHintLabel::LabelParts(vec![lsp::InlayHintLabelPart {
-                            value: entire_hint_label.to_string(),
-                            ..Default::default()
-                        }]),
-                        kind: Some(lsp::InlayHintKind::TYPE),
-                        text_edits: None,
-                        tooltip: None,
-                        padding_left: Some(false),
-                        padding_right: Some(false),
-                        data: None,
-                    }]))
-                }
-            })
-            .next()
-            .await;
-        cx.foreground().run_until_parked();
-        cx.update_editor(|editor, cx| {
-            let expected_layers = vec![entire_hint_label.to_string()];
-            assert_eq!(expected_layers, cached_hint_labels(editor));
-            assert_eq!(expected_layers, visible_hint_labels(editor, cx));
-        });
-
-        let inlay_range = cx
-            .ranges(indoc! {"
-                struct TestStruct;
-
-                // ==================
-
-                struct TestNewType<T>(T);
-
-                fn main() {
-                    let variable« »= TestNewType(TestStruct);
-                }
-        "})
-            .get(0)
-            .cloned()
-            .unwrap();
-        let new_type_hint_part_hover_position = cx.update_editor(|editor, cx| {
-            let snapshot = editor.snapshot(cx);
-            let previous_valid = inlay_range.start.to_display_point(&snapshot);
-            let next_valid = inlay_range.end.to_display_point(&snapshot);
-            assert_eq!(previous_valid.row(), next_valid.row());
-            assert!(previous_valid.column() < next_valid.column());
-            let exact_unclipped = DisplayPoint::new(
-                previous_valid.row(),
-                previous_valid.column()
-                    + (entire_hint_label.find(new_type_label).unwrap() + new_type_label.len() / 2)
-                        as u32,
-            );
-            PointForPosition {
-                previous_valid,
-                next_valid,
-                exact_unclipped,
-                column_overshoot_after_line_end: 0,
-            }
-        });
-        cx.update_editor(|editor, cx| {
-            update_inlay_link_and_hover_points(
-                &editor.snapshot(cx),
-                new_type_hint_part_hover_position,
-                editor,
-                true,
-                false,
-                cx,
-            );
-        });
-
-        let resolve_closure_uri = uri.clone();
-        cx.lsp
-            .handle_request::<lsp::request::InlayHintResolveRequest, _, _>(
-                move |mut hint_to_resolve, _| {
-                    let mut resolved_hint_positions = BTreeSet::new();
-                    let task_uri = resolve_closure_uri.clone();
-                    async move {
-                        let inserted = resolved_hint_positions.insert(hint_to_resolve.position);
-                        assert!(inserted, "Hint {hint_to_resolve:?} was resolved twice");
-
-                        // `: TestNewType<TestStruct>`
-                        hint_to_resolve.label = lsp::InlayHintLabel::LabelParts(vec![
-                            lsp::InlayHintLabelPart {
-                                value: ": ".to_string(),
-                                ..Default::default()
-                            },
-                            lsp::InlayHintLabelPart {
-                                value: new_type_label.to_string(),
-                                location: Some(lsp::Location {
-                                    uri: task_uri.clone(),
-                                    range: new_type_target_range,
-                                }),
-                                tooltip: Some(lsp::InlayHintLabelPartTooltip::String(format!(
-                                    "A tooltip for `{new_type_label}`"
-                                ))),
-                                ..Default::default()
-                            },
-                            lsp::InlayHintLabelPart {
-                                value: "<".to_string(),
-                                ..Default::default()
-                            },
-                            lsp::InlayHintLabelPart {
-                                value: struct_label.to_string(),
-                                location: Some(lsp::Location {
-                                    uri: task_uri,
-                                    range: struct_target_range,
-                                }),
-                                tooltip: Some(lsp::InlayHintLabelPartTooltip::MarkupContent(
-                                    lsp::MarkupContent {
-                                        kind: lsp::MarkupKind::Markdown,
-                                        value: format!("A tooltip for `{struct_label}`"),
-                                    },
-                                )),
-                                ..Default::default()
-                            },
-                            lsp::InlayHintLabelPart {
-                                value: ">".to_string(),
-                                ..Default::default()
-                            },
-                        ]);
-
-                        Ok(hint_to_resolve)
-                    }
-                },
-            )
-            .next()
-            .await;
-        cx.foreground().run_until_parked();
-
-        cx.update_editor(|editor, cx| {
-            update_inlay_link_and_hover_points(
-                &editor.snapshot(cx),
-                new_type_hint_part_hover_position,
-                editor,
-                true,
-                false,
-                cx,
-            );
-        });
-        cx.foreground()
-            .advance_clock(Duration::from_millis(HOVER_DELAY_MILLIS + 100));
-        cx.foreground().run_until_parked();
-        cx.update_editor(|editor, cx| {
-            let hover_state = &editor.hover_state;
-            assert!(hover_state.diagnostic_popover.is_none() && hover_state.info_popover.is_some());
-            let popover = hover_state.info_popover.as_ref().unwrap();
-            let buffer_snapshot = editor.buffer().update(cx, |buffer, cx| buffer.snapshot(cx));
-            assert_eq!(
-                popover.symbol_range,
-                RangeInEditor::Inlay(InlayHighlight {
-                    inlay: InlayId::Hint(0),
-                    inlay_position: buffer_snapshot.anchor_at(inlay_range.start, Bias::Right),
-                    range: ": ".len()..": ".len() + new_type_label.len(),
-                }),
-                "Popover range should match the new type label part"
-            );
-            assert_eq!(
-                popover.parsed_content.text,
-                format!("A tooltip for `{new_type_label}`"),
-                "Rendered text should not anyhow alter backticks"
-            );
-        });
-
-        let struct_hint_part_hover_position = cx.update_editor(|editor, cx| {
-            let snapshot = editor.snapshot(cx);
-            let previous_valid = inlay_range.start.to_display_point(&snapshot);
-            let next_valid = inlay_range.end.to_display_point(&snapshot);
-            assert_eq!(previous_valid.row(), next_valid.row());
-            assert!(previous_valid.column() < next_valid.column());
-            let exact_unclipped = DisplayPoint::new(
-                previous_valid.row(),
-                previous_valid.column()
-                    + (entire_hint_label.find(struct_label).unwrap() + struct_label.len() / 2)
-                        as u32,
-            );
-            PointForPosition {
-                previous_valid,
-                next_valid,
-                exact_unclipped,
-                column_overshoot_after_line_end: 0,
-            }
-        });
-        cx.update_editor(|editor, cx| {
-            update_inlay_link_and_hover_points(
-                &editor.snapshot(cx),
-                struct_hint_part_hover_position,
-                editor,
-                true,
-                false,
-                cx,
-            );
-        });
-        cx.foreground()
-            .advance_clock(Duration::from_millis(HOVER_DELAY_MILLIS + 100));
-        cx.foreground().run_until_parked();
-        cx.update_editor(|editor, cx| {
-            let hover_state = &editor.hover_state;
-            assert!(hover_state.diagnostic_popover.is_none() && hover_state.info_popover.is_some());
-            let popover = hover_state.info_popover.as_ref().unwrap();
-            let buffer_snapshot = editor.buffer().update(cx, |buffer, cx| buffer.snapshot(cx));
-            assert_eq!(
-                popover.symbol_range,
-                RangeInEditor::Inlay(InlayHighlight {
-                    inlay: InlayId::Hint(0),
-                    inlay_position: buffer_snapshot.anchor_at(inlay_range.start, Bias::Right),
-                    range: ": ".len() + new_type_label.len() + "<".len()
-                        ..": ".len() + new_type_label.len() + "<".len() + struct_label.len(),
-                }),
-                "Popover range should match the struct label part"
-            );
-            assert_eq!(
-                popover.parsed_content.text,
-                format!("A tooltip for {struct_label}"),
-                "Rendered markdown element should remove backticks from text"
-            );
-        });
-    }
-}
+// #[cfg(test)]
+// mod tests {
+//     use super::*;
+//     use crate::{
+//         editor_tests::init_test,
+//         element::PointForPosition,
+//         inlay_hint_cache::tests::{cached_hint_labels, visible_hint_labels},
+//         link_go_to_definition::update_inlay_link_and_hover_points,
+//         test::editor_lsp_test_context::EditorLspTestContext,
+//         InlayId,
+//     };
+//     use collections::BTreeSet;
+//     use gpui::fonts::{HighlightStyle, Underline, Weight};
+//     use indoc::indoc;
+//     use language::{language_settings::InlayHintSettings, Diagnostic, DiagnosticSet};
+//     use lsp::LanguageServerId;
+//     use project::{HoverBlock, HoverBlockKind};
+//     use smol::stream::StreamExt;
+//     use unindent::Unindent;
+//     use util::test::marked_text_ranges;
+
+//     #[gpui::test]
+//     async fn test_mouse_hover_info_popover(cx: &mut gpui::TestAppContext) {
+//         init_test(cx, |_| {});
+
+//         let mut cx = EditorLspTestContext::new_rust(
+//             lsp::ServerCapabilities {
+//                 hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
+//                 ..Default::default()
+//             },
+//             cx,
+//         )
+//         .await;
+
+//         // Basic hover delays and then pops without moving the mouse
+//         cx.set_state(indoc! {"
+//             fn ˇtest() { println!(); }
+//         "});
+//         let hover_point = cx.display_point(indoc! {"
+//             fn test() { printˇln!(); }
+//         "});
+
+//         cx.update_editor(|editor, cx| hover_at(editor, Some(hover_point), cx));
+//         assert!(!cx.editor(|editor, _| editor.hover_state.visible()));
+
+//         // After delay, hover should be visible.
+//         let symbol_range = cx.lsp_range(indoc! {"
+//             fn test() { «println!»(); }
+//         "});
+//         let mut requests =
+//             cx.handle_request::<lsp::request::HoverRequest, _, _>(move |_, _, _| async move {
+//                 Ok(Some(lsp::Hover {
+//                     contents: lsp::HoverContents::Markup(lsp::MarkupContent {
+//                         kind: lsp::MarkupKind::Markdown,
+//                         value: "some basic docs".to_string(),
+//                     }),
+//                     range: Some(symbol_range),
+//                 }))
+//             });
+//         cx.foreground()
+//             .advance_clock(Duration::from_millis(HOVER_DELAY_MILLIS + 100));
+//         requests.next().await;
+
+//         cx.editor(|editor, _| {
+//             assert!(editor.hover_state.visible());
+//             assert_eq!(
+//                 editor.hover_state.info_popover.clone().unwrap().blocks,
+//                 vec![HoverBlock {
+//                     text: "some basic docs".to_string(),
+//                     kind: HoverBlockKind::Markdown,
+//                 },]
+//             )
+//         });
+
+//         // Mouse moved with no hover response dismisses
+//         let hover_point = cx.display_point(indoc! {"
+//             fn teˇst() { println!(); }
+//         "});
+//         let mut request = cx
+//             .lsp
+//             .handle_request::<lsp::request::HoverRequest, _, _>(|_, _| async move { Ok(None) });
+//         cx.update_editor(|editor, cx| hover_at(editor, Some(hover_point), cx));
+//         cx.foreground()
+//             .advance_clock(Duration::from_millis(HOVER_DELAY_MILLIS + 100));
+//         request.next().await;
+//         cx.editor(|editor, _| {
+//             assert!(!editor.hover_state.visible());
+//         });
+//     }
+
+//     #[gpui::test]
+//     async fn test_keyboard_hover_info_popover(cx: &mut gpui::TestAppContext) {
+//         init_test(cx, |_| {});
+
+//         let mut cx = EditorLspTestContext::new_rust(
+//             lsp::ServerCapabilities {
+//                 hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
+//                 ..Default::default()
+//             },
+//             cx,
+//         )
+//         .await;
+
+//         // Hover with keyboard has no delay
+//         cx.set_state(indoc! {"
+//             fˇn test() { println!(); }
+//         "});
+//         cx.update_editor(|editor, cx| hover(editor, &Hover, cx));
+//         let symbol_range = cx.lsp_range(indoc! {"
+//             «fn» test() { println!(); }
+//         "});
+//         cx.handle_request::<lsp::request::HoverRequest, _, _>(move |_, _, _| async move {
+//             Ok(Some(lsp::Hover {
+//                 contents: lsp::HoverContents::Markup(lsp::MarkupContent {
+//                     kind: lsp::MarkupKind::Markdown,
+//                     value: "some other basic docs".to_string(),
+//                 }),
+//                 range: Some(symbol_range),
+//             }))
+//         })
+//         .next()
+//         .await;
+
+//         cx.condition(|editor, _| editor.hover_state.visible()).await;
+//         cx.editor(|editor, _| {
+//             assert_eq!(
+//                 editor.hover_state.info_popover.clone().unwrap().blocks,
+//                 vec![HoverBlock {
+//                     text: "some other basic docs".to_string(),
+//                     kind: HoverBlockKind::Markdown,
+//                 }]
+//             )
+//         });
+//     }
+
+//     #[gpui::test]
+//     async fn test_empty_hovers_filtered(cx: &mut gpui::TestAppContext) {
+//         init_test(cx, |_| {});
+
+//         let mut cx = EditorLspTestContext::new_rust(
+//             lsp::ServerCapabilities {
+//                 hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
+//                 ..Default::default()
+//             },
+//             cx,
+//         )
+//         .await;
+
+//         // Hover with keyboard has no delay
+//         cx.set_state(indoc! {"
+//             fˇn test() { println!(); }
+//         "});
+//         cx.update_editor(|editor, cx| hover(editor, &Hover, cx));
+//         let symbol_range = cx.lsp_range(indoc! {"
+//             «fn» test() { println!(); }
+//         "});
+//         cx.handle_request::<lsp::request::HoverRequest, _, _>(move |_, _, _| async move {
+//             Ok(Some(lsp::Hover {
+//                 contents: lsp::HoverContents::Array(vec![
+//                     lsp::MarkedString::String("regular text for hover to show".to_string()),
+//                     lsp::MarkedString::String("".to_string()),
+//                     lsp::MarkedString::LanguageString(lsp::LanguageString {
+//                         language: "Rust".to_string(),
+//                         value: "".to_string(),
+//                     }),
+//                 ]),
+//                 range: Some(symbol_range),
+//             }))
+//         })
+//         .next()
+//         .await;
+
+//         cx.condition(|editor, _| editor.hover_state.visible()).await;
+//         cx.editor(|editor, _| {
+//             assert_eq!(
+//                 editor.hover_state.info_popover.clone().unwrap().blocks,
+//                 vec![HoverBlock {
+//                     text: "regular text for hover to show".to_string(),
+//                     kind: HoverBlockKind::Markdown,
+//                 }],
+//                 "No empty string hovers should be shown"
+//             );
+//         });
+//     }
+
+//     #[gpui::test]
+//     async fn test_line_ends_trimmed(cx: &mut gpui::TestAppContext) {
+//         init_test(cx, |_| {});
+
+//         let mut cx = EditorLspTestContext::new_rust(
+//             lsp::ServerCapabilities {
+//                 hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
+//                 ..Default::default()
+//             },
+//             cx,
+//         )
+//         .await;
+
+//         // Hover with keyboard has no delay
+//         cx.set_state(indoc! {"
+//             fˇn test() { println!(); }
+//         "});
+//         cx.update_editor(|editor, cx| hover(editor, &Hover, cx));
+//         let symbol_range = cx.lsp_range(indoc! {"
+//             «fn» test() { println!(); }
+//         "});
+
+//         let code_str = "\nlet hovered_point: Vector2F // size = 8, align = 0x4\n";
+//         let markdown_string = format!("\n```rust\n{code_str}```");
+
+//         let closure_markdown_string = markdown_string.clone();
+//         cx.handle_request::<lsp::request::HoverRequest, _, _>(move |_, _, _| {
+//             let future_markdown_string = closure_markdown_string.clone();
+//             async move {
+//                 Ok(Some(lsp::Hover {
+//                     contents: lsp::HoverContents::Markup(lsp::MarkupContent {
+//                         kind: lsp::MarkupKind::Markdown,
+//                         value: future_markdown_string,
+//                     }),
+//                     range: Some(symbol_range),
+//                 }))
+//             }
+//         })
+//         .next()
+//         .await;
+
+//         cx.condition(|editor, _| editor.hover_state.visible()).await;
+//         cx.editor(|editor, _| {
+//             let blocks = editor.hover_state.info_popover.clone().unwrap().blocks;
+//             assert_eq!(
+//                 blocks,
+//                 vec![HoverBlock {
+//                     text: markdown_string,
+//                     kind: HoverBlockKind::Markdown,
+//                 }],
+//             );
+
+//             let rendered = smol::block_on(parse_blocks(&blocks, &Default::default(), None));
+//             assert_eq!(
+//                 rendered.text,
+//                 code_str.trim(),
+//                 "Should not have extra line breaks at end of rendered hover"
+//             );
+//         });
+//     }
+
+//     #[gpui::test]
+//     async fn test_hover_diagnostic_and_info_popovers(cx: &mut gpui::TestAppContext) {
+//         init_test(cx, |_| {});
+
+//         let mut cx = EditorLspTestContext::new_rust(
+//             lsp::ServerCapabilities {
+//                 hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
+//                 ..Default::default()
+//             },
+//             cx,
+//         )
+//         .await;
+
+//         // Hover with just diagnostic, pops DiagnosticPopover immediately and then
+//         // info popover once request completes
+//         cx.set_state(indoc! {"
+//             fn teˇst() { println!(); }
+//         "});
+
+//         // Send diagnostic to client
+//         let range = cx.text_anchor_range(indoc! {"
+//             fn «test»() { println!(); }
+//         "});
+//         cx.update_buffer(|buffer, cx| {
+//             let snapshot = buffer.text_snapshot();
+//             let set = DiagnosticSet::from_sorted_entries(
+//                 vec![DiagnosticEntry {
+//                     range,
+//                     diagnostic: Diagnostic {
+//                         message: "A test diagnostic message.".to_string(),
+//                         ..Default::default()
+//                     },
+//                 }],
+//                 &snapshot,
+//             );
+//             buffer.update_diagnostics(LanguageServerId(0), set, cx);
+//         });
+
+//         // Hover pops diagnostic immediately
+//         cx.update_editor(|editor, cx| hover(editor, &Hover, cx));
+//         cx.foreground().run_until_parked();
+
+//         cx.editor(|Editor { hover_state, .. }, _| {
+//             assert!(hover_state.diagnostic_popover.is_some() && hover_state.info_popover.is_none())
+//         });
+
+//         // Info Popover shows after request responded to
+//         let range = cx.lsp_range(indoc! {"
+//             fn «test»() { println!(); }
+//         "});
+//         cx.handle_request::<lsp::request::HoverRequest, _, _>(move |_, _, _| async move {
+//             Ok(Some(lsp::Hover {
+//                 contents: lsp::HoverContents::Markup(lsp::MarkupContent {
+//                     kind: lsp::MarkupKind::Markdown,
+//                     value: "some new docs".to_string(),
+//                 }),
+//                 range: Some(range),
+//             }))
+//         });
+//         cx.foreground()
+//             .advance_clock(Duration::from_millis(HOVER_DELAY_MILLIS + 100));
+
+//         cx.foreground().run_until_parked();
+//         cx.editor(|Editor { hover_state, .. }, _| {
+//             hover_state.diagnostic_popover.is_some() && hover_state.info_task.is_some()
+//         });
+//     }
+
+//     #[gpui::test]
+//     fn test_render_blocks(cx: &mut gpui::TestAppContext) {
+//         init_test(cx, |_| {});
+
+//         cx.add_window(|cx| {
+//             let editor = Editor::single_line(None, cx);
+//             let style = editor.style(cx);
+
+//             struct Row {
+//                 blocks: Vec<HoverBlock>,
+//                 expected_marked_text: String,
+//                 expected_styles: Vec<HighlightStyle>,
+//             }
+
+//             let rows = &[
+//                 // Strong emphasis
+//                 Row {
+//                     blocks: vec![HoverBlock {
+//                         text: "one **two** three".to_string(),
+//                         kind: HoverBlockKind::Markdown,
+//                     }],
+//                     expected_marked_text: "one «two» three".to_string(),
+//                     expected_styles: vec![HighlightStyle {
+//                         weight: Some(Weight::BOLD),
+//                         ..Default::default()
+//                     }],
+//                 },
+//                 // Links
+//                 Row {
+//                     blocks: vec![HoverBlock {
+//                         text: "one [two](https://the-url) three".to_string(),
+//                         kind: HoverBlockKind::Markdown,
+//                     }],
+//                     expected_marked_text: "one «two» three".to_string(),
+//                     expected_styles: vec![HighlightStyle {
+//                         underline: Some(Underline {
+//                             thickness: 1.0.into(),
+//                             ..Default::default()
+//                         }),
+//                         ..Default::default()
+//                     }],
+//                 },
+//                 // Lists
+//                 Row {
+//                     blocks: vec![HoverBlock {
+//                         text: "
+//                             lists:
+//                             * one
+//                                 - a
+//                                 - b
+//                             * two
+//                                 - [c](https://the-url)
+//                                 - d"
+//                         .unindent(),
+//                         kind: HoverBlockKind::Markdown,
+//                     }],
+//                     expected_marked_text: "
+//                         lists:
+//                         - one
+//                           - a
+//                           - b
+//                         - two
+//                           - «c»
+//                           - d"
+//                     .unindent(),
+//                     expected_styles: vec![HighlightStyle {
+//                         underline: Some(Underline {
+//                             thickness: 1.0.into(),
+//                             ..Default::default()
+//                         }),
+//                         ..Default::default()
+//                     }],
+//                 },
+//                 // Multi-paragraph list items
+//                 Row {
+//                     blocks: vec![HoverBlock {
+//                         text: "
+//                             * one two
+//                               three
+
+//                             * four five
+//                                 * six seven
+//                                   eight
+
+//                                   nine
+//                                 * ten
+//                             * six"
+//                             .unindent(),
+//                         kind: HoverBlockKind::Markdown,
+//                     }],
+//                     expected_marked_text: "
+//                         - one two three
+//                         - four five
+//                           - six seven eight
+
+//                             nine
+//                           - ten
+//                         - six"
+//                         .unindent(),
+//                     expected_styles: vec![HighlightStyle {
+//                         underline: Some(Underline {
+//                             thickness: 1.0.into(),
+//                             ..Default::default()
+//                         }),
+//                         ..Default::default()
+//                     }],
+//                 },
+//             ];
+
+//             for Row {
+//                 blocks,
+//                 expected_marked_text,
+//                 expected_styles,
+//             } in &rows[0..]
+//             {
+//                 let rendered = smol::block_on(parse_blocks(&blocks, &Default::default(), None));
+
+//                 let (expected_text, ranges) = marked_text_ranges(expected_marked_text, false);
+//                 let expected_highlights = ranges
+//                     .into_iter()
+//                     .zip(expected_styles.iter().cloned())
+//                     .collect::<Vec<_>>();
+//                 assert_eq!(
+//                     rendered.text, expected_text,
+//                     "wrong text for input {blocks:?}"
+//                 );
+
+//                 let rendered_highlights: Vec<_> = rendered
+//                     .highlights
+//                     .iter()
+//                     .filter_map(|(range, highlight)| {
+//                         let highlight = highlight.to_highlight_style(&style.syntax)?;
+//                         Some((range.clone(), highlight))
+//                     })
+//                     .collect();
+
+//                 assert_eq!(
+//                     rendered_highlights, expected_highlights,
+//                     "wrong highlights for input {blocks:?}"
+//                 );
+//             }
+
+//             editor
+//         });
+//     }
+
+//     #[gpui::test]
+//     async fn test_hover_inlay_label_parts(cx: &mut gpui::TestAppContext) {
+//         init_test(cx, |settings| {
+//             settings.defaults.inlay_hints = Some(InlayHintSettings {
+//                 enabled: true,
+//                 show_type_hints: true,
+//                 show_parameter_hints: true,
+//                 show_other_hints: true,
+//             })
+//         });
+
+//         let mut cx = EditorLspTestContext::new_rust(
+//             lsp::ServerCapabilities {
+//                 inlay_hint_provider: Some(lsp::OneOf::Right(
+//                     lsp::InlayHintServerCapabilities::Options(lsp::InlayHintOptions {
+//                         resolve_provider: Some(true),
+//                         ..Default::default()
+//                     }),
+//                 )),
+//                 ..Default::default()
+//             },
+//             cx,
+//         )
+//         .await;
+
+//         cx.set_state(indoc! {"
+//             struct TestStruct;
+
+//             // ==================
+
+//             struct TestNewType<T>(T);
+
+//             fn main() {
+//                 let variableˇ = TestNewType(TestStruct);
+//             }
+//         "});
+
+//         let hint_start_offset = cx.ranges(indoc! {"
+//             struct TestStruct;
+
+//             // ==================
+
+//             struct TestNewType<T>(T);
+
+//             fn main() {
+//                 let variableˇ = TestNewType(TestStruct);
+//             }
+//         "})[0]
+//             .start;
+//         let hint_position = cx.to_lsp(hint_start_offset);
+//         let new_type_target_range = cx.lsp_range(indoc! {"
+//             struct TestStruct;
+
+//             // ==================
+
+//             struct «TestNewType»<T>(T);
+
+//             fn main() {
+//                 let variable = TestNewType(TestStruct);
+//             }
+//         "});
+//         let struct_target_range = cx.lsp_range(indoc! {"
+//             struct «TestStruct»;
+
+//             // ==================
+
+//             struct TestNewType<T>(T);
+
+//             fn main() {
+//                 let variable = TestNewType(TestStruct);
+//             }
+//         "});
+
+//         let uri = cx.buffer_lsp_url.clone();
+//         let new_type_label = "TestNewType";
+//         let struct_label = "TestStruct";
+//         let entire_hint_label = ": TestNewType<TestStruct>";
+//         let closure_uri = uri.clone();
+//         cx.lsp
+//             .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
+//                 let task_uri = closure_uri.clone();
+//                 async move {
+//                     assert_eq!(params.text_document.uri, task_uri);
+//                     Ok(Some(vec![lsp::InlayHint {
+//                         position: hint_position,
+//                         label: lsp::InlayHintLabel::LabelParts(vec![lsp::InlayHintLabelPart {
+//                             value: entire_hint_label.to_string(),
+//                             ..Default::default()
+//                         }]),
+//                         kind: Some(lsp::InlayHintKind::TYPE),
+//                         text_edits: None,
+//                         tooltip: None,
+//                         padding_left: Some(false),
+//                         padding_right: Some(false),
+//                         data: None,
+//                     }]))
+//                 }
+//             })
+//             .next()
+//             .await;
+//         cx.foreground().run_until_parked();
+//         cx.update_editor(|editor, cx| {
+//             let expected_layers = vec![entire_hint_label.to_string()];
+//             assert_eq!(expected_layers, cached_hint_labels(editor));
+//             assert_eq!(expected_layers, visible_hint_labels(editor, cx));
+//         });
+
+//         let inlay_range = cx
+//             .ranges(indoc! {"
+//                 struct TestStruct;
+
+//                 // ==================
+
+//                 struct TestNewType<T>(T);
+
+//                 fn main() {
+//                     let variable« »= TestNewType(TestStruct);
+//                 }
+//         "})
+//             .get(0)
+//             .cloned()
+//             .unwrap();
+//         let new_type_hint_part_hover_position = cx.update_editor(|editor, cx| {
+//             let snapshot = editor.snapshot(cx);
+//             let previous_valid = inlay_range.start.to_display_point(&snapshot);
+//             let next_valid = inlay_range.end.to_display_point(&snapshot);
+//             assert_eq!(previous_valid.row(), next_valid.row());
+//             assert!(previous_valid.column() < next_valid.column());
+//             let exact_unclipped = DisplayPoint::new(
+//                 previous_valid.row(),
+//                 previous_valid.column()
+//                     + (entire_hint_label.find(new_type_label).unwrap() + new_type_label.len() / 2)
+//                         as u32,
+//             );
+//             PointForPosition {
+//                 previous_valid,
+//                 next_valid,
+//                 exact_unclipped,
+//                 column_overshoot_after_line_end: 0,
+//             }
+//         });
+//         cx.update_editor(|editor, cx| {
+//             update_inlay_link_and_hover_points(
+//                 &editor.snapshot(cx),
+//                 new_type_hint_part_hover_position,
+//                 editor,
+//                 true,
+//                 false,
+//                 cx,
+//             );
+//         });
+
+//         let resolve_closure_uri = uri.clone();
+//         cx.lsp
+//             .handle_request::<lsp::request::InlayHintResolveRequest, _, _>(
+//                 move |mut hint_to_resolve, _| {
+//                     let mut resolved_hint_positions = BTreeSet::new();
+//                     let task_uri = resolve_closure_uri.clone();
+//                     async move {
+//                         let inserted = resolved_hint_positions.insert(hint_to_resolve.position);
+//                         assert!(inserted, "Hint {hint_to_resolve:?} was resolved twice");
+
+//                         // `: TestNewType<TestStruct>`
+//                         hint_to_resolve.label = lsp::InlayHintLabel::LabelParts(vec![
+//                             lsp::InlayHintLabelPart {
+//                                 value: ": ".to_string(),
+//                                 ..Default::default()
+//                             },
+//                             lsp::InlayHintLabelPart {
+//                                 value: new_type_label.to_string(),
+//                                 location: Some(lsp::Location {
+//                                     uri: task_uri.clone(),
+//                                     range: new_type_target_range,
+//                                 }),
+//                                 tooltip: Some(lsp::InlayHintLabelPartTooltip::String(format!(
+//                                     "A tooltip for `{new_type_label}`"
+//                                 ))),
+//                                 ..Default::default()
+//                             },
+//                             lsp::InlayHintLabelPart {
+//                                 value: "<".to_string(),
+//                                 ..Default::default()
+//                             },
+//                             lsp::InlayHintLabelPart {
+//                                 value: struct_label.to_string(),
+//                                 location: Some(lsp::Location {
+//                                     uri: task_uri,
+//                                     range: struct_target_range,
+//                                 }),
+//                                 tooltip: Some(lsp::InlayHintLabelPartTooltip::MarkupContent(
+//                                     lsp::MarkupContent {
+//                                         kind: lsp::MarkupKind::Markdown,
+//                                         value: format!("A tooltip for `{struct_label}`"),
+//                                     },
+//                                 )),
+//                                 ..Default::default()
+//                             },
+//                             lsp::InlayHintLabelPart {
+//                                 value: ">".to_string(),
+//                                 ..Default::default()
+//                             },
+//                         ]);
+
+//                         Ok(hint_to_resolve)
+//                     }
+//                 },
+//             )
+//             .next()
+//             .await;
+//         cx.foreground().run_until_parked();
+
+//         cx.update_editor(|editor, cx| {
+//             update_inlay_link_and_hover_points(
+//                 &editor.snapshot(cx),
+//                 new_type_hint_part_hover_position,
+//                 editor,
+//                 true,
+//                 false,
+//                 cx,
+//             );
+//         });
+//         cx.foreground()
+//             .advance_clock(Duration::from_millis(HOVER_DELAY_MILLIS + 100));
+//         cx.foreground().run_until_parked();
+//         cx.update_editor(|editor, cx| {
+//             let hover_state = &editor.hover_state;
+//             assert!(hover_state.diagnostic_popover.is_none() && hover_state.info_popover.is_some());
+//             let popover = hover_state.info_popover.as_ref().unwrap();
+//             let buffer_snapshot = editor.buffer().update(cx, |buffer, cx| buffer.snapshot(cx));
+//             assert_eq!(
+//                 popover.symbol_range,
+//                 RangeInEditor::Inlay(InlayHighlight {
+//                     inlay: InlayId::Hint(0),
+//                     inlay_position: buffer_snapshot.anchor_at(inlay_range.start, Bias::Right),
+//                     range: ": ".len()..": ".len() + new_type_label.len(),
+//                 }),
+//                 "Popover range should match the new type label part"
+//             );
+//             assert_eq!(
+//                 popover.parsed_content.text,
+//                 format!("A tooltip for `{new_type_label}`"),
+//                 "Rendered text should not anyhow alter backticks"
+//             );
+//         });
+
+//         let struct_hint_part_hover_position = cx.update_editor(|editor, cx| {
+//             let snapshot = editor.snapshot(cx);
+//             let previous_valid = inlay_range.start.to_display_point(&snapshot);
+//             let next_valid = inlay_range.end.to_display_point(&snapshot);
+//             assert_eq!(previous_valid.row(), next_valid.row());
+//             assert!(previous_valid.column() < next_valid.column());
+//             let exact_unclipped = DisplayPoint::new(
+//                 previous_valid.row(),
+//                 previous_valid.column()
+//                     + (entire_hint_label.find(struct_label).unwrap() + struct_label.len() / 2)
+//                         as u32,
+//             );
+//             PointForPosition {
+//                 previous_valid,
+//                 next_valid,
+//                 exact_unclipped,
+//                 column_overshoot_after_line_end: 0,
+//             }
+//         });
+//         cx.update_editor(|editor, cx| {
+//             update_inlay_link_and_hover_points(
+//                 &editor.snapshot(cx),
+//                 struct_hint_part_hover_position,
+//                 editor,
+//                 true,
+//                 false,
+//                 cx,
+//             );
+//         });
+//         cx.foreground()
+//             .advance_clock(Duration::from_millis(HOVER_DELAY_MILLIS + 100));
+//         cx.foreground().run_until_parked();
+//         cx.update_editor(|editor, cx| {
+//             let hover_state = &editor.hover_state;
+//             assert!(hover_state.diagnostic_popover.is_none() && hover_state.info_popover.is_some());
+//             let popover = hover_state.info_popover.as_ref().unwrap();
+//             let buffer_snapshot = editor.buffer().update(cx, |buffer, cx| buffer.snapshot(cx));
+//             assert_eq!(
+//                 popover.symbol_range,
+//                 RangeInEditor::Inlay(InlayHighlight {
+//                     inlay: InlayId::Hint(0),
+//                     inlay_position: buffer_snapshot.anchor_at(inlay_range.start, Bias::Right),
+//                     range: ": ".len() + new_type_label.len() + "<".len()
+//                         ..": ".len() + new_type_label.len() + "<".len() + struct_label.len(),
+//                 }),
+//                 "Popover range should match the struct label part"
+//             );
+//             assert_eq!(
+//                 popover.parsed_content.text,
+//                 format!("A tooltip for {struct_label}"),
+//                 "Rendered markdown element should remove backticks from text"
+//             );
+//         });
+//     }
+// }

crates/editor2/src/inlay_hint_cache.rs 🔗

@@ -250,7 +250,7 @@ impl InlayHintCache {
 
     pub fn update_settings(
         &mut self,
-        multi_buffer: &ModelHandle<MultiBuffer>,
+        multi_buffer: &Model<MultiBuffer>,
         new_hint_settings: InlayHintSettings,
         visible_hints: Vec<Inlay>,
         cx: &mut ViewContext<Editor>,
@@ -302,7 +302,7 @@ impl InlayHintCache {
     pub fn spawn_hint_refresh(
         &mut self,
         reason: &'static str,
-        excerpts_to_query: HashMap<ExcerptId, (ModelHandle<Buffer>, Global, Range<usize>)>,
+        excerpts_to_query: HashMap<ExcerptId, (Model<Buffer>, Global, Range<usize>)>,
         invalidate: InvalidationStrategy,
         cx: &mut ViewContext<Editor>,
     ) -> Option<InlaySplice> {
@@ -355,7 +355,7 @@ impl InlayHintCache {
 
     fn new_allowed_hint_kinds_splice(
         &self,
-        multi_buffer: &ModelHandle<MultiBuffer>,
+        multi_buffer: &Model<MultiBuffer>,
         visible_hints: &[Inlay],
         new_kinds: &HashSet<Option<InlayHintKind>>,
         cx: &mut ViewContext<Editor>,
@@ -579,7 +579,7 @@ impl InlayHintCache {
 fn spawn_new_update_tasks(
     editor: &mut Editor,
     reason: &'static str,
-    excerpts_to_query: HashMap<ExcerptId, (ModelHandle<Buffer>, Global, Range<usize>)>,
+    excerpts_to_query: HashMap<ExcerptId, (Model<Buffer>, Global, Range<usize>)>,
     invalidate: InvalidationStrategy,
     update_cache_version: usize,
     cx: &mut ViewContext<'_, '_, Editor>,
@@ -684,7 +684,7 @@ impl QueryRanges {
 fn determine_query_ranges(
     multi_buffer: &mut MultiBuffer,
     excerpt_id: ExcerptId,
-    excerpt_buffer: &ModelHandle<Buffer>,
+    excerpt_buffer: &Model<Buffer>,
     excerpt_visible_range: Range<usize>,
     cx: &mut ModelContext<'_, MultiBuffer>,
 ) -> Option<QueryRanges> {
@@ -837,7 +837,7 @@ fn new_update_task(
 }
 
 async fn fetch_and_update_hints(
-    editor: gpui::WeakViewHandle<Editor>,
+    editor: gpui::WeakView<Editor>,
     multi_buffer_snapshot: MultiBufferSnapshot,
     buffer_snapshot: BufferSnapshot,
     visible_hints: Arc<Vec<Inlay>>,
@@ -1194,2156 +1194,2156 @@ fn apply_hint_update(
     }
 }
 
-#[cfg(test)]
-pub mod tests {
-    use std::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize, Ordering};
-
-    use crate::{
-        scroll::{autoscroll::Autoscroll, scroll_amount::ScrollAmount},
-        serde_json::json,
-        ExcerptRange,
-    };
-    use futures::StreamExt;
-    use gpui::{executor::Deterministic, TestAppContext, ViewHandle};
-    use itertools::Itertools;
-    use language::{
-        language_settings::AllLanguageSettingsContent, FakeLspAdapter, Language, LanguageConfig,
-    };
-    use lsp::FakeLanguageServer;
-    use parking_lot::Mutex;
-    use project::{FakeFs, Project};
-    use settings::SettingsStore;
-    use text::{Point, ToPoint};
-    use workspace::Workspace;
-
-    use crate::editor_tests::update_test_language_settings;
-
-    use super::*;
-
-    #[gpui::test]
-    async fn test_basic_cache_update_with_duplicate_hints(cx: &mut gpui::TestAppContext) {
-        let allowed_hint_kinds = HashSet::from_iter([None, Some(InlayHintKind::Type)]);
-        init_test(cx, |settings| {
-            settings.defaults.inlay_hints = Some(InlayHintSettings {
-                enabled: true,
-                show_type_hints: allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
-                show_parameter_hints: allowed_hint_kinds.contains(&Some(InlayHintKind::Parameter)),
-                show_other_hints: allowed_hint_kinds.contains(&None),
-            })
-        });
-
-        let (file_with_hints, editor, fake_server) = prepare_test_objects(cx).await;
-        let lsp_request_count = Arc::new(AtomicU32::new(0));
-        fake_server
-            .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
-                let task_lsp_request_count = Arc::clone(&lsp_request_count);
-                async move {
-                    assert_eq!(
-                        params.text_document.uri,
-                        lsp::Url::from_file_path(file_with_hints).unwrap(),
-                    );
-                    let current_call_id =
-                        Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst);
-                    let mut new_hints = Vec::with_capacity(2 * current_call_id as usize);
-                    for _ in 0..2 {
-                        let mut i = current_call_id;
-                        loop {
-                            new_hints.push(lsp::InlayHint {
-                                position: lsp::Position::new(0, i),
-                                label: lsp::InlayHintLabel::String(i.to_string()),
-                                kind: None,
-                                text_edits: None,
-                                tooltip: None,
-                                padding_left: None,
-                                padding_right: None,
-                                data: None,
-                            });
-                            if i == 0 {
-                                break;
-                            }
-                            i -= 1;
-                        }
-                    }
-
-                    Ok(Some(new_hints))
-                }
-            })
-            .next()
-            .await;
-        cx.foreground().run_until_parked();
-
-        let mut edits_made = 1;
-        editor.update(cx, |editor, cx| {
-            let expected_hints = vec!["0".to_string()];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "Should get its first hints when opening the editor"
-            );
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            let inlay_cache = editor.inlay_hint_cache();
-            assert_eq!(
-                inlay_cache.allowed_hint_kinds, allowed_hint_kinds,
-                "Cache should use editor settings to get the allowed hint kinds"
-            );
-            assert_eq!(
-                inlay_cache.version, edits_made,
-                "The editor update the cache version after every cache/view change"
-            );
-        });
-
-        editor.update(cx, |editor, cx| {
-            editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
-            editor.handle_input("some change", cx);
-            edits_made += 1;
-        });
-        cx.foreground().run_until_parked();
-        editor.update(cx, |editor, cx| {
-            let expected_hints = vec!["0".to_string(), "1".to_string()];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "Should get new hints after an edit"
-            );
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            let inlay_cache = editor.inlay_hint_cache();
-            assert_eq!(
-                inlay_cache.allowed_hint_kinds, allowed_hint_kinds,
-                "Cache should use editor settings to get the allowed hint kinds"
-            );
-            assert_eq!(
-                inlay_cache.version, edits_made,
-                "The editor update the cache version after every cache/view change"
-            );
-        });
-
-        fake_server
-            .request::<lsp::request::InlayHintRefreshRequest>(())
-            .await
-            .expect("inlay refresh request failed");
-        edits_made += 1;
-        cx.foreground().run_until_parked();
-        editor.update(cx, |editor, cx| {
-            let expected_hints = vec!["0".to_string(), "1".to_string(), "2".to_string()];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "Should get new hints after hint refresh/ request"
-            );
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            let inlay_cache = editor.inlay_hint_cache();
-            assert_eq!(
-                inlay_cache.allowed_hint_kinds, allowed_hint_kinds,
-                "Cache should use editor settings to get the allowed hint kinds"
-            );
-            assert_eq!(
-                inlay_cache.version, edits_made,
-                "The editor update the cache version after every cache/view change"
-            );
-        });
-    }
-
-    #[gpui::test]
-    async fn test_cache_update_on_lsp_completion_tasks(cx: &mut gpui::TestAppContext) {
-        init_test(cx, |settings| {
-            settings.defaults.inlay_hints = Some(InlayHintSettings {
-                enabled: true,
-                show_type_hints: true,
-                show_parameter_hints: true,
-                show_other_hints: true,
-            })
-        });
-
-        let (file_with_hints, editor, fake_server) = prepare_test_objects(cx).await;
-        let lsp_request_count = Arc::new(AtomicU32::new(0));
-        fake_server
-            .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
-                let task_lsp_request_count = Arc::clone(&lsp_request_count);
-                async move {
-                    assert_eq!(
-                        params.text_document.uri,
-                        lsp::Url::from_file_path(file_with_hints).unwrap(),
-                    );
-                    let current_call_id =
-                        Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst);
-                    Ok(Some(vec![lsp::InlayHint {
-                        position: lsp::Position::new(0, current_call_id),
-                        label: lsp::InlayHintLabel::String(current_call_id.to_string()),
-                        kind: None,
-                        text_edits: None,
-                        tooltip: None,
-                        padding_left: None,
-                        padding_right: None,
-                        data: None,
-                    }]))
-                }
-            })
-            .next()
-            .await;
-        cx.foreground().run_until_parked();
-
-        let mut edits_made = 1;
-        editor.update(cx, |editor, cx| {
-            let expected_hints = vec!["0".to_string()];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "Should get its first hints when opening the editor"
-            );
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            assert_eq!(
-                editor.inlay_hint_cache().version,
-                edits_made,
-                "The editor update the cache version after every cache/view change"
-            );
-        });
-
-        let progress_token = "test_progress_token";
-        fake_server
-            .request::<lsp::request::WorkDoneProgressCreate>(lsp::WorkDoneProgressCreateParams {
-                token: lsp::ProgressToken::String(progress_token.to_string()),
-            })
-            .await
-            .expect("work done progress create request failed");
-        cx.foreground().run_until_parked();
-        fake_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
-            token: lsp::ProgressToken::String(progress_token.to_string()),
-            value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Begin(
-                lsp::WorkDoneProgressBegin::default(),
-            )),
-        });
-        cx.foreground().run_until_parked();
-
-        editor.update(cx, |editor, cx| {
-            let expected_hints = vec!["0".to_string()];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "Should not update hints while the work task is running"
-            );
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            assert_eq!(
-                editor.inlay_hint_cache().version,
-                edits_made,
-                "Should not update the cache while the work task is running"
-            );
-        });
-
-        fake_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
-            token: lsp::ProgressToken::String(progress_token.to_string()),
-            value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::End(
-                lsp::WorkDoneProgressEnd::default(),
-            )),
-        });
-        cx.foreground().run_until_parked();
-
-        edits_made += 1;
-        editor.update(cx, |editor, cx| {
-            let expected_hints = vec!["1".to_string()];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "New hints should be queried after the work task is done"
-            );
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            assert_eq!(
-                editor.inlay_hint_cache().version,
-                edits_made,
-                "Cache version should udpate once after the work task is done"
-            );
-        });
-    }
-
-    #[gpui::test]
-    async fn test_no_hint_updates_for_unrelated_language_files(cx: &mut gpui::TestAppContext) {
-        init_test(cx, |settings| {
-            settings.defaults.inlay_hints = Some(InlayHintSettings {
-                enabled: true,
-                show_type_hints: true,
-                show_parameter_hints: true,
-                show_other_hints: true,
-            })
-        });
-
-        let fs = FakeFs::new(cx.background());
-        fs.insert_tree(
-                    "/a",
-                    json!({
-                        "main.rs": "fn main() { a } // and some long comment to ensure inlays are not trimmed out",
-                        "other.md": "Test md file with some text",
-                    }),
-                )
-                .await;
-        let project = Project::test(fs, ["/a".as_ref()], cx).await;
-        let workspace = cx
-            .add_window(|cx| Workspace::test_new(project.clone(), cx))
-            .root(cx);
-        let worktree_id = workspace.update(cx, |workspace, cx| {
-            workspace.project().read_with(cx, |project, cx| {
-                project.worktrees(cx).next().unwrap().read(cx).id()
-            })
-        });
-
-        let mut rs_fake_servers = None;
-        let mut md_fake_servers = None;
-        for (name, path_suffix) in [("Rust", "rs"), ("Markdown", "md")] {
-            let mut language = Language::new(
-                LanguageConfig {
-                    name: name.into(),
-                    path_suffixes: vec![path_suffix.to_string()],
-                    ..Default::default()
-                },
-                Some(tree_sitter_rust::language()),
-            );
-            let fake_servers = language
-                .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
-                    name,
-                    capabilities: lsp::ServerCapabilities {
-                        inlay_hint_provider: Some(lsp::OneOf::Left(true)),
-                        ..Default::default()
-                    },
-                    ..Default::default()
-                }))
-                .await;
-            match name {
-                "Rust" => rs_fake_servers = Some(fake_servers),
-                "Markdown" => md_fake_servers = Some(fake_servers),
-                _ => unreachable!(),
-            }
-            project.update(cx, |project, _| {
-                project.languages().add(Arc::new(language));
-            });
-        }
-
-        let _rs_buffer = project
-            .update(cx, |project, cx| {
-                project.open_local_buffer("/a/main.rs", cx)
-            })
-            .await
-            .unwrap();
-        cx.foreground().run_until_parked();
-        cx.foreground().start_waiting();
-        let rs_fake_server = rs_fake_servers.unwrap().next().await.unwrap();
-        let rs_editor = workspace
-            .update(cx, |workspace, cx| {
-                workspace.open_path((worktree_id, "main.rs"), None, true, cx)
-            })
-            .await
-            .unwrap()
-            .downcast::<Editor>()
-            .unwrap();
-        let rs_lsp_request_count = Arc::new(AtomicU32::new(0));
-        rs_fake_server
-            .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
-                let task_lsp_request_count = Arc::clone(&rs_lsp_request_count);
-                async move {
-                    assert_eq!(
-                        params.text_document.uri,
-                        lsp::Url::from_file_path("/a/main.rs").unwrap(),
-                    );
-                    let i = Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst);
-                    Ok(Some(vec![lsp::InlayHint {
-                        position: lsp::Position::new(0, i),
-                        label: lsp::InlayHintLabel::String(i.to_string()),
-                        kind: None,
-                        text_edits: None,
-                        tooltip: None,
-                        padding_left: None,
-                        padding_right: None,
-                        data: None,
-                    }]))
-                }
-            })
-            .next()
-            .await;
-        cx.foreground().run_until_parked();
-        rs_editor.update(cx, |editor, cx| {
-            let expected_hints = vec!["0".to_string()];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "Should get its first hints when opening the editor"
-            );
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            assert_eq!(
-                editor.inlay_hint_cache().version,
-                1,
-                "Rust editor update the cache version after every cache/view change"
-            );
-        });
-
-        cx.foreground().run_until_parked();
-        let _md_buffer = project
-            .update(cx, |project, cx| {
-                project.open_local_buffer("/a/other.md", cx)
-            })
-            .await
-            .unwrap();
-        cx.foreground().run_until_parked();
-        cx.foreground().start_waiting();
-        let md_fake_server = md_fake_servers.unwrap().next().await.unwrap();
-        let md_editor = workspace
-            .update(cx, |workspace, cx| {
-                workspace.open_path((worktree_id, "other.md"), None, true, cx)
-            })
-            .await
-            .unwrap()
-            .downcast::<Editor>()
-            .unwrap();
-        let md_lsp_request_count = Arc::new(AtomicU32::new(0));
-        md_fake_server
-            .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
-                let task_lsp_request_count = Arc::clone(&md_lsp_request_count);
-                async move {
-                    assert_eq!(
-                        params.text_document.uri,
-                        lsp::Url::from_file_path("/a/other.md").unwrap(),
-                    );
-                    let i = Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst);
-                    Ok(Some(vec![lsp::InlayHint {
-                        position: lsp::Position::new(0, i),
-                        label: lsp::InlayHintLabel::String(i.to_string()),
-                        kind: None,
-                        text_edits: None,
-                        tooltip: None,
-                        padding_left: None,
-                        padding_right: None,
-                        data: None,
-                    }]))
-                }
-            })
-            .next()
-            .await;
-        cx.foreground().run_until_parked();
-        md_editor.update(cx, |editor, cx| {
-            let expected_hints = vec!["0".to_string()];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "Markdown editor should have a separate verison, repeating Rust editor rules"
-            );
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            assert_eq!(editor.inlay_hint_cache().version, 1);
-        });
-
-        rs_editor.update(cx, |editor, cx| {
-            editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
-            editor.handle_input("some rs change", cx);
-        });
-        cx.foreground().run_until_parked();
-        rs_editor.update(cx, |editor, cx| {
-            let expected_hints = vec!["1".to_string()];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "Rust inlay cache should change after the edit"
-            );
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            assert_eq!(
-                editor.inlay_hint_cache().version,
-                2,
-                "Every time hint cache changes, cache version should be incremented"
-            );
-        });
-        md_editor.update(cx, |editor, cx| {
-            let expected_hints = vec!["0".to_string()];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "Markdown editor should not be affected by Rust editor changes"
-            );
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            assert_eq!(editor.inlay_hint_cache().version, 1);
-        });
-
-        md_editor.update(cx, |editor, cx| {
-            editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
-            editor.handle_input("some md change", cx);
-        });
-        cx.foreground().run_until_parked();
-        md_editor.update(cx, |editor, cx| {
-            let expected_hints = vec!["1".to_string()];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "Rust editor should not be affected by Markdown editor changes"
-            );
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            assert_eq!(editor.inlay_hint_cache().version, 2);
-        });
-        rs_editor.update(cx, |editor, cx| {
-            let expected_hints = vec!["1".to_string()];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "Markdown editor should also change independently"
-            );
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            assert_eq!(editor.inlay_hint_cache().version, 2);
-        });
-    }
-
-    #[gpui::test]
-    async fn test_hint_setting_changes(cx: &mut gpui::TestAppContext) {
-        let allowed_hint_kinds = HashSet::from_iter([None, Some(InlayHintKind::Type)]);
-        init_test(cx, |settings| {
-            settings.defaults.inlay_hints = Some(InlayHintSettings {
-                enabled: true,
-                show_type_hints: allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
-                show_parameter_hints: allowed_hint_kinds.contains(&Some(InlayHintKind::Parameter)),
-                show_other_hints: allowed_hint_kinds.contains(&None),
-            })
-        });
-
-        let (file_with_hints, editor, fake_server) = prepare_test_objects(cx).await;
-        let lsp_request_count = Arc::new(AtomicU32::new(0));
-        let another_lsp_request_count = Arc::clone(&lsp_request_count);
-        fake_server
-            .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
-                let task_lsp_request_count = Arc::clone(&another_lsp_request_count);
-                async move {
-                    Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst);
-                    assert_eq!(
-                        params.text_document.uri,
-                        lsp::Url::from_file_path(file_with_hints).unwrap(),
-                    );
-                    Ok(Some(vec![
-                        lsp::InlayHint {
-                            position: lsp::Position::new(0, 1),
-                            label: lsp::InlayHintLabel::String("type hint".to_string()),
-                            kind: Some(lsp::InlayHintKind::TYPE),
-                            text_edits: None,
-                            tooltip: None,
-                            padding_left: None,
-                            padding_right: None,
-                            data: None,
-                        },
-                        lsp::InlayHint {
-                            position: lsp::Position::new(0, 2),
-                            label: lsp::InlayHintLabel::String("parameter hint".to_string()),
-                            kind: Some(lsp::InlayHintKind::PARAMETER),
-                            text_edits: None,
-                            tooltip: None,
-                            padding_left: None,
-                            padding_right: None,
-                            data: None,
-                        },
-                        lsp::InlayHint {
-                            position: lsp::Position::new(0, 3),
-                            label: lsp::InlayHintLabel::String("other hint".to_string()),
-                            kind: None,
-                            text_edits: None,
-                            tooltip: None,
-                            padding_left: None,
-                            padding_right: None,
-                            data: None,
-                        },
-                    ]))
-                }
-            })
-            .next()
-            .await;
-        cx.foreground().run_until_parked();
-
-        let mut edits_made = 1;
-        editor.update(cx, |editor, cx| {
-            assert_eq!(
-                lsp_request_count.load(Ordering::Relaxed),
-                1,
-                "Should query new hints once"
-            );
-            assert_eq!(
-                vec![
-                    "other hint".to_string(),
-                    "parameter hint".to_string(),
-                    "type hint".to_string(),
-                ],
-                cached_hint_labels(editor),
-                "Should get its first hints when opening the editor"
-            );
-            assert_eq!(
-                vec!["other hint".to_string(), "type hint".to_string()],
-                visible_hint_labels(editor, cx)
-            );
-            let inlay_cache = editor.inlay_hint_cache();
-            assert_eq!(
-                inlay_cache.allowed_hint_kinds, allowed_hint_kinds,
-                "Cache should use editor settings to get the allowed hint kinds"
-            );
-            assert_eq!(
-                inlay_cache.version, edits_made,
-                "The editor update the cache version after every cache/view change"
-            );
-        });
-
-        fake_server
-            .request::<lsp::request::InlayHintRefreshRequest>(())
-            .await
-            .expect("inlay refresh request failed");
-        cx.foreground().run_until_parked();
-        editor.update(cx, |editor, cx| {
-            assert_eq!(
-                lsp_request_count.load(Ordering::Relaxed),
-                2,
-                "Should load new hints twice"
-            );
-            assert_eq!(
-                vec![
-                    "other hint".to_string(),
-                    "parameter hint".to_string(),
-                    "type hint".to_string(),
-                ],
-                cached_hint_labels(editor),
-                "Cached hints should not change due to allowed hint kinds settings update"
-            );
-            assert_eq!(
-                vec!["other hint".to_string(), "type hint".to_string()],
-                visible_hint_labels(editor, cx)
-            );
-            assert_eq!(
-                editor.inlay_hint_cache().version,
-                edits_made,
-                "Should not update cache version due to new loaded hints being the same"
-            );
-        });
-
-        for (new_allowed_hint_kinds, expected_visible_hints) in [
-            (HashSet::from_iter([None]), vec!["other hint".to_string()]),
-            (
-                HashSet::from_iter([Some(InlayHintKind::Type)]),
-                vec!["type hint".to_string()],
-            ),
-            (
-                HashSet::from_iter([Some(InlayHintKind::Parameter)]),
-                vec!["parameter hint".to_string()],
-            ),
-            (
-                HashSet::from_iter([None, Some(InlayHintKind::Type)]),
-                vec!["other hint".to_string(), "type hint".to_string()],
-            ),
-            (
-                HashSet::from_iter([None, Some(InlayHintKind::Parameter)]),
-                vec!["other hint".to_string(), "parameter hint".to_string()],
-            ),
-            (
-                HashSet::from_iter([Some(InlayHintKind::Type), Some(InlayHintKind::Parameter)]),
-                vec!["parameter hint".to_string(), "type hint".to_string()],
-            ),
-            (
-                HashSet::from_iter([
-                    None,
-                    Some(InlayHintKind::Type),
-                    Some(InlayHintKind::Parameter),
-                ]),
-                vec![
-                    "other hint".to_string(),
-                    "parameter hint".to_string(),
-                    "type hint".to_string(),
-                ],
-            ),
-        ] {
-            edits_made += 1;
-            update_test_language_settings(cx, |settings| {
-                settings.defaults.inlay_hints = Some(InlayHintSettings {
-                    enabled: true,
-                    show_type_hints: new_allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
-                    show_parameter_hints: new_allowed_hint_kinds
-                        .contains(&Some(InlayHintKind::Parameter)),
-                    show_other_hints: new_allowed_hint_kinds.contains(&None),
-                })
-            });
-            cx.foreground().run_until_parked();
-            editor.update(cx, |editor, cx| {
-                assert_eq!(
-                    lsp_request_count.load(Ordering::Relaxed),
-                    2,
-                    "Should not load new hints on allowed hint kinds change for hint kinds {new_allowed_hint_kinds:?}"
-                );
-                assert_eq!(
-                    vec![
-                        "other hint".to_string(),
-                        "parameter hint".to_string(),
-                        "type hint".to_string(),
-                    ],
-                    cached_hint_labels(editor),
-                    "Should get its cached hints unchanged after the settings change for hint kinds {new_allowed_hint_kinds:?}"
-                );
-                assert_eq!(
-                    expected_visible_hints,
-                    visible_hint_labels(editor, cx),
-                    "Should get its visible hints filtered after the settings change for hint kinds {new_allowed_hint_kinds:?}"
-                );
-                let inlay_cache = editor.inlay_hint_cache();
-                assert_eq!(
-                    inlay_cache.allowed_hint_kinds, new_allowed_hint_kinds,
-                    "Cache should use editor settings to get the allowed hint kinds for hint kinds {new_allowed_hint_kinds:?}"
-                );
-                assert_eq!(
-                    inlay_cache.version, edits_made,
-                    "The editor should update the cache version after every cache/view change for hint kinds {new_allowed_hint_kinds:?} due to visible hints change"
-                );
-            });
-        }
-
-        edits_made += 1;
-        let another_allowed_hint_kinds = HashSet::from_iter([Some(InlayHintKind::Type)]);
-        update_test_language_settings(cx, |settings| {
-            settings.defaults.inlay_hints = Some(InlayHintSettings {
-                enabled: false,
-                show_type_hints: another_allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
-                show_parameter_hints: another_allowed_hint_kinds
-                    .contains(&Some(InlayHintKind::Parameter)),
-                show_other_hints: another_allowed_hint_kinds.contains(&None),
-            })
-        });
-        cx.foreground().run_until_parked();
-        editor.update(cx, |editor, cx| {
-            assert_eq!(
-                lsp_request_count.load(Ordering::Relaxed),
-                2,
-                "Should not load new hints when hints got disabled"
-            );
-            assert!(
-                cached_hint_labels(editor).is_empty(),
-                "Should clear the cache when hints got disabled"
-            );
-            assert!(
-                visible_hint_labels(editor, cx).is_empty(),
-                "Should clear visible hints when hints got disabled"
-            );
-            let inlay_cache = editor.inlay_hint_cache();
-            assert_eq!(
-                inlay_cache.allowed_hint_kinds, another_allowed_hint_kinds,
-                "Should update its allowed hint kinds even when hints got disabled"
-            );
-            assert_eq!(
-                inlay_cache.version, edits_made,
-                "The editor should update the cache version after hints got disabled"
-            );
-        });
-
-        fake_server
-            .request::<lsp::request::InlayHintRefreshRequest>(())
-            .await
-            .expect("inlay refresh request failed");
-        cx.foreground().run_until_parked();
-        editor.update(cx, |editor, cx| {
-            assert_eq!(
-                lsp_request_count.load(Ordering::Relaxed),
-                2,
-                "Should not load new hints when they got disabled"
-            );
-            assert!(cached_hint_labels(editor).is_empty());
-            assert!(visible_hint_labels(editor, cx).is_empty());
-            assert_eq!(
-                editor.inlay_hint_cache().version, edits_made,
-                "The editor should not update the cache version after /refresh query without updates"
-            );
-        });
-
-        let final_allowed_hint_kinds = HashSet::from_iter([Some(InlayHintKind::Parameter)]);
-        edits_made += 1;
-        update_test_language_settings(cx, |settings| {
-            settings.defaults.inlay_hints = Some(InlayHintSettings {
-                enabled: true,
-                show_type_hints: final_allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
-                show_parameter_hints: final_allowed_hint_kinds
-                    .contains(&Some(InlayHintKind::Parameter)),
-                show_other_hints: final_allowed_hint_kinds.contains(&None),
-            })
-        });
-        cx.foreground().run_until_parked();
-        editor.update(cx, |editor, cx| {
-            assert_eq!(
-                lsp_request_count.load(Ordering::Relaxed),
-                3,
-                "Should query for new hints when they got reenabled"
-            );
-            assert_eq!(
-                vec![
-                    "other hint".to_string(),
-                    "parameter hint".to_string(),
-                    "type hint".to_string(),
-                ],
-                cached_hint_labels(editor),
-                "Should get its cached hints fully repopulated after the hints got reenabled"
-            );
-            assert_eq!(
-                vec!["parameter hint".to_string()],
-                visible_hint_labels(editor, cx),
-                "Should get its visible hints repopulated and filtered after the h"
-            );
-            let inlay_cache = editor.inlay_hint_cache();
-            assert_eq!(
-                inlay_cache.allowed_hint_kinds, final_allowed_hint_kinds,
-                "Cache should update editor settings when hints got reenabled"
-            );
-            assert_eq!(
-                inlay_cache.version, edits_made,
-                "Cache should update its version after hints got reenabled"
-            );
-        });
-
-        fake_server
-            .request::<lsp::request::InlayHintRefreshRequest>(())
-            .await
-            .expect("inlay refresh request failed");
-        cx.foreground().run_until_parked();
-        editor.update(cx, |editor, cx| {
-            assert_eq!(
-                lsp_request_count.load(Ordering::Relaxed),
-                4,
-                "Should query for new hints again"
-            );
-            assert_eq!(
-                vec![
-                    "other hint".to_string(),
-                    "parameter hint".to_string(),
-                    "type hint".to_string(),
-                ],
-                cached_hint_labels(editor),
-            );
-            assert_eq!(
-                vec!["parameter hint".to_string()],
-                visible_hint_labels(editor, cx),
-            );
-            assert_eq!(editor.inlay_hint_cache().version, edits_made);
-        });
-    }
-
-    #[gpui::test]
-    async fn test_hint_request_cancellation(cx: &mut gpui::TestAppContext) {
-        init_test(cx, |settings| {
-            settings.defaults.inlay_hints = Some(InlayHintSettings {
-                enabled: true,
-                show_type_hints: true,
-                show_parameter_hints: true,
-                show_other_hints: true,
-            })
-        });
-
-        let (file_with_hints, editor, fake_server) = prepare_test_objects(cx).await;
-        let fake_server = Arc::new(fake_server);
-        let lsp_request_count = Arc::new(AtomicU32::new(0));
-        let another_lsp_request_count = Arc::clone(&lsp_request_count);
-        fake_server
-            .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
-                let task_lsp_request_count = Arc::clone(&another_lsp_request_count);
-                async move {
-                    let i = Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst) + 1;
-                    assert_eq!(
-                        params.text_document.uri,
-                        lsp::Url::from_file_path(file_with_hints).unwrap(),
-                    );
-                    Ok(Some(vec![lsp::InlayHint {
-                        position: lsp::Position::new(0, i),
-                        label: lsp::InlayHintLabel::String(i.to_string()),
-                        kind: None,
-                        text_edits: None,
-                        tooltip: None,
-                        padding_left: None,
-                        padding_right: None,
-                        data: None,
-                    }]))
-                }
-            })
-            .next()
-            .await;
-
-        let mut expected_changes = Vec::new();
-        for change_after_opening in [
-            "initial change #1",
-            "initial change #2",
-            "initial change #3",
-        ] {
-            editor.update(cx, |editor, cx| {
-                editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
-                editor.handle_input(change_after_opening, cx);
-            });
-            expected_changes.push(change_after_opening);
-        }
-
-        cx.foreground().run_until_parked();
-
-        editor.update(cx, |editor, cx| {
-            let current_text = editor.text(cx);
-            for change in &expected_changes {
-                assert!(
-                    current_text.contains(change),
-                    "Should apply all changes made"
-                );
-            }
-            assert_eq!(
-                lsp_request_count.load(Ordering::Relaxed),
-                2,
-                "Should query new hints twice: for editor init and for the last edit that interrupted all others"
-            );
-            let expected_hints = vec!["2".to_string()];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "Should get hints from the last edit landed only"
-            );
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            assert_eq!(
-                editor.inlay_hint_cache().version, 1,
-                "Only one update should be registered in the cache after all cancellations"
-            );
-        });
-
-        let mut edits = Vec::new();
-        for async_later_change in [
-            "another change #1",
-            "another change #2",
-            "another change #3",
-        ] {
-            expected_changes.push(async_later_change);
-            let task_editor = editor.clone();
-            let mut task_cx = cx.clone();
-            edits.push(cx.foreground().spawn(async move {
-                task_editor.update(&mut task_cx, |editor, cx| {
-                    editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
-                    editor.handle_input(async_later_change, cx);
-                });
-            }));
-        }
-        let _ = future::join_all(edits).await;
-        cx.foreground().run_until_parked();
-
-        editor.update(cx, |editor, cx| {
-            let current_text = editor.text(cx);
-            for change in &expected_changes {
-                assert!(
-                    current_text.contains(change),
-                    "Should apply all changes made"
-                );
-            }
-            assert_eq!(
-                lsp_request_count.load(Ordering::SeqCst),
-                3,
-                "Should query new hints one more time, for the last edit only"
-            );
-            let expected_hints = vec!["3".to_string()];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "Should get hints from the last edit landed only"
-            );
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            assert_eq!(
-                editor.inlay_hint_cache().version,
-                2,
-                "Should update the cache version once more, for the new change"
-            );
-        });
-    }
-
-    #[gpui::test(iterations = 10)]
-    async fn test_large_buffer_inlay_requests_split(cx: &mut gpui::TestAppContext) {
-        init_test(cx, |settings| {
-            settings.defaults.inlay_hints = Some(InlayHintSettings {
-                enabled: true,
-                show_type_hints: true,
-                show_parameter_hints: true,
-                show_other_hints: true,
-            })
-        });
-
-        let mut language = Language::new(
-            LanguageConfig {
-                name: "Rust".into(),
-                path_suffixes: vec!["rs".to_string()],
-                ..Default::default()
-            },
-            Some(tree_sitter_rust::language()),
-        );
-        let mut fake_servers = language
-            .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
-                capabilities: lsp::ServerCapabilities {
-                    inlay_hint_provider: Some(lsp::OneOf::Left(true)),
-                    ..Default::default()
-                },
-                ..Default::default()
-            }))
-            .await;
-        let fs = FakeFs::new(cx.background());
-        fs.insert_tree(
-            "/a",
-            json!({
-                "main.rs": format!("fn main() {{\n{}\n}}", "let i = 5;\n".repeat(500)),
-                "other.rs": "// Test file",
-            }),
-        )
-        .await;
-        let project = Project::test(fs, ["/a".as_ref()], cx).await;
-        project.update(cx, |project, _| project.languages().add(Arc::new(language)));
-        let workspace = cx
-            .add_window(|cx| Workspace::test_new(project.clone(), cx))
-            .root(cx);
-        let worktree_id = workspace.update(cx, |workspace, cx| {
-            workspace.project().read_with(cx, |project, cx| {
-                project.worktrees(cx).next().unwrap().read(cx).id()
-            })
-        });
-
-        let _buffer = project
-            .update(cx, |project, cx| {
-                project.open_local_buffer("/a/main.rs", cx)
-            })
-            .await
-            .unwrap();
-        cx.foreground().run_until_parked();
-        cx.foreground().start_waiting();
-        let fake_server = fake_servers.next().await.unwrap();
-        let editor = workspace
-            .update(cx, |workspace, cx| {
-                workspace.open_path((worktree_id, "main.rs"), None, true, cx)
-            })
-            .await
-            .unwrap()
-            .downcast::<Editor>()
-            .unwrap();
-        let lsp_request_ranges = Arc::new(Mutex::new(Vec::new()));
-        let lsp_request_count = Arc::new(AtomicUsize::new(0));
-        let closure_lsp_request_ranges = Arc::clone(&lsp_request_ranges);
-        let closure_lsp_request_count = Arc::clone(&lsp_request_count);
-        fake_server
-            .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
-                let task_lsp_request_ranges = Arc::clone(&closure_lsp_request_ranges);
-                let task_lsp_request_count = Arc::clone(&closure_lsp_request_count);
-                async move {
-                    assert_eq!(
-                        params.text_document.uri,
-                        lsp::Url::from_file_path("/a/main.rs").unwrap(),
-                    );
-
-                    task_lsp_request_ranges.lock().push(params.range);
-                    let i = Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::Release) + 1;
-                    Ok(Some(vec![lsp::InlayHint {
-                        position: params.range.end,
-                        label: lsp::InlayHintLabel::String(i.to_string()),
-                        kind: None,
-                        text_edits: None,
-                        tooltip: None,
-                        padding_left: None,
-                        padding_right: None,
-                        data: None,
-                    }]))
-                }
-            })
-            .next()
-            .await;
-        fn editor_visible_range(
-            editor: &ViewHandle<Editor>,
-            cx: &mut gpui::TestAppContext,
-        ) -> Range<Point> {
-            let ranges = editor.update(cx, |editor, cx| editor.excerpt_visible_offsets(None, cx));
-            assert_eq!(
-                ranges.len(),
-                1,
-                "Single buffer should produce a single excerpt with visible range"
-            );
-            let (_, (excerpt_buffer, _, excerpt_visible_range)) =
-                ranges.into_iter().next().unwrap();
-            excerpt_buffer.update(cx, |buffer, _| {
-                let snapshot = buffer.snapshot();
-                let start = buffer
-                    .anchor_before(excerpt_visible_range.start)
-                    .to_point(&snapshot);
-                let end = buffer
-                    .anchor_after(excerpt_visible_range.end)
-                    .to_point(&snapshot);
-                start..end
-            })
-        }
-
-        // in large buffers, requests are made for more than visible range of a buffer.
-        // invisible parts are queried later, to avoid excessive requests on quick typing.
-        // wait the timeout needed to get all requests.
-        cx.foreground().advance_clock(Duration::from_millis(
-            INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100,
-        ));
-        cx.foreground().run_until_parked();
-        let initial_visible_range = editor_visible_range(&editor, cx);
-        let lsp_initial_visible_range = lsp::Range::new(
-            lsp::Position::new(
-                initial_visible_range.start.row,
-                initial_visible_range.start.column,
-            ),
-            lsp::Position::new(
-                initial_visible_range.end.row,
-                initial_visible_range.end.column,
-            ),
-        );
-        let expected_initial_query_range_end =
-            lsp::Position::new(initial_visible_range.end.row * 2, 2);
-        let mut expected_invisible_query_start = lsp_initial_visible_range.end;
-        expected_invisible_query_start.character += 1;
-        editor.update(cx, |editor, cx| {
-            let ranges = lsp_request_ranges.lock().drain(..).collect::<Vec<_>>();
-            assert_eq!(ranges.len(), 2,
-                "When scroll is at the edge of a big document, its visible part and the same range further should be queried in order, but got: {ranges:?}");
-            let visible_query_range = &ranges[0];
-            assert_eq!(visible_query_range.start, lsp_initial_visible_range.start);
-            assert_eq!(visible_query_range.end, lsp_initial_visible_range.end);
-            let invisible_query_range = &ranges[1];
-
-            assert_eq!(invisible_query_range.start, expected_invisible_query_start, "Should initially query visible edge of the document");
-            assert_eq!(invisible_query_range.end, expected_initial_query_range_end, "Should initially query visible edge of the document");
-
-            let requests_count = lsp_request_count.load(Ordering::Acquire);
-            assert_eq!(requests_count, 2, "Visible + invisible request");
-            let expected_hints = vec!["1".to_string(), "2".to_string()];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "Should have hints from both LSP requests made for a big file"
-            );
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx), "Should display only hints from the visible range");
-            assert_eq!(
-                editor.inlay_hint_cache().version, requests_count,
-                "LSP queries should've bumped the cache version"
-            );
-        });
-
-        editor.update(cx, |editor, cx| {
-            editor.scroll_screen(&ScrollAmount::Page(1.0), cx);
-            editor.scroll_screen(&ScrollAmount::Page(1.0), cx);
-        });
-        cx.foreground().advance_clock(Duration::from_millis(
-            INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100,
-        ));
-        cx.foreground().run_until_parked();
-        let visible_range_after_scrolls = editor_visible_range(&editor, cx);
-        let visible_line_count =
-            editor.update(cx, |editor, _| editor.visible_line_count().unwrap());
-        let selection_in_cached_range = editor.update(cx, |editor, cx| {
-            let ranges = lsp_request_ranges
-                .lock()
-                .drain(..)
-                .sorted_by_key(|r| r.start)
-                .collect::<Vec<_>>();
-            assert_eq!(
-                ranges.len(),
-                2,
-                "Should query 2 ranges after both scrolls, but got: {ranges:?}"
-            );
-            let first_scroll = &ranges[0];
-            let second_scroll = &ranges[1];
-            assert_eq!(
-                first_scroll.end, second_scroll.start,
-                "Should query 2 adjacent ranges after the scrolls, but got: {ranges:?}"
-            );
-            assert_eq!(
-                first_scroll.start, expected_initial_query_range_end,
-                "First scroll should start the query right after the end of the original scroll",
-            );
-            assert_eq!(
-                second_scroll.end,
-                lsp::Position::new(
-                    visible_range_after_scrolls.end.row
-                        + visible_line_count.ceil() as u32,
-                    1,
-                ),
-                "Second scroll should query one more screen down after the end of the visible range"
-            );
-
-            let lsp_requests = lsp_request_count.load(Ordering::Acquire);
-            assert_eq!(lsp_requests, 4, "Should query for hints after every scroll");
-            let expected_hints = vec![
-                "1".to_string(),
-                "2".to_string(),
-                "3".to_string(),
-                "4".to_string(),
-            ];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "Should have hints from the new LSP response after the edit"
-            );
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            assert_eq!(
-                editor.inlay_hint_cache().version,
-                lsp_requests,
-                "Should update the cache for every LSP response with hints added"
-            );
-
-            let mut selection_in_cached_range = visible_range_after_scrolls.end;
-            selection_in_cached_range.row -= visible_line_count.ceil() as u32;
-            selection_in_cached_range
-        });
-
-        editor.update(cx, |editor, cx| {
-            editor.change_selections(Some(Autoscroll::center()), cx, |s| {
-                s.select_ranges([selection_in_cached_range..selection_in_cached_range])
-            });
-        });
-        cx.foreground().advance_clock(Duration::from_millis(
-            INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100,
-        ));
-        cx.foreground().run_until_parked();
-        editor.update(cx, |_, _| {
-            let ranges = lsp_request_ranges
-                .lock()
-                .drain(..)
-                .sorted_by_key(|r| r.start)
-                .collect::<Vec<_>>();
-            assert!(ranges.is_empty(), "No new ranges or LSP queries should be made after returning to the selection with cached hints");
-            assert_eq!(lsp_request_count.load(Ordering::Acquire), 4);
-        });
-
-        editor.update(cx, |editor, cx| {
-            editor.handle_input("++++more text++++", cx);
-        });
-        cx.foreground().advance_clock(Duration::from_millis(
-            INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100,
-        ));
-        cx.foreground().run_until_parked();
-        editor.update(cx, |editor, cx| {
-            let mut ranges = lsp_request_ranges.lock().drain(..).collect::<Vec<_>>();
-            ranges.sort_by_key(|r| r.start);
-
-            assert_eq!(ranges.len(), 3,
-                "On edit, should scroll to selection and query a range around it: visible + same range above and below. Instead, got query ranges {ranges:?}");
-            let above_query_range = &ranges[0];
-            let visible_query_range = &ranges[1];
-            let below_query_range = &ranges[2];
-            assert!(above_query_range.end.character < visible_query_range.start.character || above_query_range.end.line + 1 == visible_query_range.start.line,
-                "Above range {above_query_range:?} should be before visible range {visible_query_range:?}");
-            assert!(visible_query_range.end.character < below_query_range.start.character || visible_query_range.end.line  + 1 == below_query_range.start.line,
-                "Visible range {visible_query_range:?} should be before below range {below_query_range:?}");
-            assert!(above_query_range.start.line < selection_in_cached_range.row,
-                "Hints should be queried with the selected range after the query range start");
-            assert!(below_query_range.end.line > selection_in_cached_range.row,
-                "Hints should be queried with the selected range before the query range end");
-            assert!(above_query_range.start.line <= selection_in_cached_range.row - (visible_line_count * 3.0 / 2.0) as u32,
-                "Hints query range should contain one more screen before");
-            assert!(below_query_range.end.line >= selection_in_cached_range.row + (visible_line_count * 3.0 / 2.0) as u32,
-                "Hints query range should contain one more screen after");
-
-            let lsp_requests = lsp_request_count.load(Ordering::Acquire);
-            assert_eq!(lsp_requests, 7, "There should be a visible range and two ranges above and below it queried");
-            let expected_hints = vec!["5".to_string(), "6".to_string(), "7".to_string()];
-            assert_eq!(expected_hints, cached_hint_labels(editor),
-                "Should have hints from the new LSP response after the edit");
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            assert_eq!(editor.inlay_hint_cache().version, lsp_requests, "Should update the cache for every LSP response with hints added");
-        });
-    }
-
-    #[gpui::test(iterations = 10)]
-    async fn test_multiple_excerpts_large_multibuffer(
-        deterministic: Arc<Deterministic>,
-        cx: &mut gpui::TestAppContext,
-    ) {
-        init_test(cx, |settings| {
-            settings.defaults.inlay_hints = Some(InlayHintSettings {
-                enabled: true,
-                show_type_hints: true,
-                show_parameter_hints: true,
-                show_other_hints: true,
-            })
-        });
-
-        let mut language = Language::new(
-            LanguageConfig {
-                name: "Rust".into(),
-                path_suffixes: vec!["rs".to_string()],
-                ..Default::default()
-            },
-            Some(tree_sitter_rust::language()),
-        );
-        let mut fake_servers = language
-            .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
-                capabilities: lsp::ServerCapabilities {
-                    inlay_hint_provider: Some(lsp::OneOf::Left(true)),
-                    ..Default::default()
-                },
-                ..Default::default()
-            }))
-            .await;
-        let language = Arc::new(language);
-        let fs = FakeFs::new(cx.background());
-        fs.insert_tree(
-            "/a",
-            json!({
-                "main.rs": format!("fn main() {{\n{}\n}}", (0..501).map(|i| format!("let i = {i};\n")).collect::<Vec<_>>().join("")),
-                "other.rs": format!("fn main() {{\n{}\n}}", (0..501).map(|j| format!("let j = {j};\n")).collect::<Vec<_>>().join("")),
-            }),
-        )
-        .await;
-        let project = Project::test(fs, ["/a".as_ref()], cx).await;
-        project.update(cx, |project, _| {
-            project.languages().add(Arc::clone(&language))
-        });
-        let workspace = cx
-            .add_window(|cx| Workspace::test_new(project.clone(), cx))
-            .root(cx);
-        let worktree_id = workspace.update(cx, |workspace, cx| {
-            workspace.project().read_with(cx, |project, cx| {
-                project.worktrees(cx).next().unwrap().read(cx).id()
-            })
-        });
-
-        let buffer_1 = project
-            .update(cx, |project, cx| {
-                project.open_buffer((worktree_id, "main.rs"), cx)
-            })
-            .await
-            .unwrap();
-        let buffer_2 = project
-            .update(cx, |project, cx| {
-                project.open_buffer((worktree_id, "other.rs"), cx)
-            })
-            .await
-            .unwrap();
-        let multibuffer = cx.add_model(|cx| {
-            let mut multibuffer = MultiBuffer::new(0);
-            multibuffer.push_excerpts(
-                buffer_1.clone(),
-                [
-                    ExcerptRange {
-                        context: Point::new(0, 0)..Point::new(2, 0),
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: Point::new(4, 0)..Point::new(11, 0),
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: Point::new(22, 0)..Point::new(33, 0),
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: Point::new(44, 0)..Point::new(55, 0),
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: Point::new(56, 0)..Point::new(66, 0),
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: Point::new(67, 0)..Point::new(77, 0),
-                        primary: None,
-                    },
-                ],
-                cx,
-            );
-            multibuffer.push_excerpts(
-                buffer_2.clone(),
-                [
-                    ExcerptRange {
-                        context: Point::new(0, 1)..Point::new(2, 1),
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: Point::new(4, 1)..Point::new(11, 1),
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: Point::new(22, 1)..Point::new(33, 1),
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: Point::new(44, 1)..Point::new(55, 1),
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: Point::new(56, 1)..Point::new(66, 1),
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: Point::new(67, 1)..Point::new(77, 1),
-                        primary: None,
-                    },
-                ],
-                cx,
-            );
-            multibuffer
-        });
-
-        deterministic.run_until_parked();
-        cx.foreground().run_until_parked();
-        let editor = cx
-            .add_window(|cx| Editor::for_multibuffer(multibuffer, Some(project.clone()), cx))
-            .root(cx);
-        let editor_edited = Arc::new(AtomicBool::new(false));
-        let fake_server = fake_servers.next().await.unwrap();
-        let closure_editor_edited = Arc::clone(&editor_edited);
-        fake_server
-            .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
-                let task_editor_edited = Arc::clone(&closure_editor_edited);
-                async move {
-                    let hint_text = if params.text_document.uri
-                        == lsp::Url::from_file_path("/a/main.rs").unwrap()
-                    {
-                        "main hint"
-                    } else if params.text_document.uri
-                        == lsp::Url::from_file_path("/a/other.rs").unwrap()
-                    {
-                        "other hint"
-                    } else {
-                        panic!("unexpected uri: {:?}", params.text_document.uri);
-                    };
-
-                    // one hint per excerpt
-                    let positions = [
-                        lsp::Position::new(0, 2),
-                        lsp::Position::new(4, 2),
-                        lsp::Position::new(22, 2),
-                        lsp::Position::new(44, 2),
-                        lsp::Position::new(56, 2),
-                        lsp::Position::new(67, 2),
-                    ];
-                    let out_of_range_hint = lsp::InlayHint {
-                        position: lsp::Position::new(
-                            params.range.start.line + 99,
-                            params.range.start.character + 99,
-                        ),
-                        label: lsp::InlayHintLabel::String(
-                            "out of excerpt range, should be ignored".to_string(),
-                        ),
-                        kind: None,
-                        text_edits: None,
-                        tooltip: None,
-                        padding_left: None,
-                        padding_right: None,
-                        data: None,
-                    };
-
-                    let edited = task_editor_edited.load(Ordering::Acquire);
-                    Ok(Some(
-                        std::iter::once(out_of_range_hint)
-                            .chain(positions.into_iter().enumerate().map(|(i, position)| {
-                                lsp::InlayHint {
-                                    position,
-                                    label: lsp::InlayHintLabel::String(format!(
-                                        "{hint_text}{} #{i}",
-                                        if edited { "(edited)" } else { "" },
-                                    )),
-                                    kind: None,
-                                    text_edits: None,
-                                    tooltip: None,
-                                    padding_left: None,
-                                    padding_right: None,
-                                    data: None,
-                                }
-                            }))
-                            .collect(),
-                    ))
-                }
-            })
-            .next()
-            .await;
-        cx.foreground().run_until_parked();
-
-        editor.update(cx, |editor, cx| {
-            let expected_hints = vec![
-                "main hint #0".to_string(),
-                "main hint #1".to_string(),
-                "main hint #2".to_string(),
-                "main hint #3".to_string(),
-            ];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "When scroll is at the edge of a multibuffer, its visible excerpts only should be queried for inlay hints"
-            );
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            assert_eq!(editor.inlay_hint_cache().version, expected_hints.len(), "Every visible excerpt hints should bump the verison");
-        });
-
-        editor.update(cx, |editor, cx| {
-            editor.change_selections(Some(Autoscroll::Next), cx, |s| {
-                s.select_ranges([Point::new(4, 0)..Point::new(4, 0)])
-            });
-            editor.change_selections(Some(Autoscroll::Next), cx, |s| {
-                s.select_ranges([Point::new(22, 0)..Point::new(22, 0)])
-            });
-            editor.change_selections(Some(Autoscroll::Next), cx, |s| {
-                s.select_ranges([Point::new(50, 0)..Point::new(50, 0)])
-            });
-        });
-        cx.foreground().run_until_parked();
-        editor.update(cx, |editor, cx| {
-            let expected_hints = vec![
-                "main hint #0".to_string(),
-                "main hint #1".to_string(),
-                "main hint #2".to_string(),
-                "main hint #3".to_string(),
-                "main hint #4".to_string(),
-                "main hint #5".to_string(),
-                "other hint #0".to_string(),
-                "other hint #1".to_string(),
-                "other hint #2".to_string(),
-            ];
-            assert_eq!(expected_hints, cached_hint_labels(editor),
-                "With more scrolls of the multibuffer, more hints should be added into the cache and nothing invalidated without edits");
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            assert_eq!(editor.inlay_hint_cache().version, expected_hints.len(),
-                "Due to every excerpt having one hint, we update cache per new excerpt scrolled");
-        });
-
-        editor.update(cx, |editor, cx| {
-            editor.change_selections(Some(Autoscroll::Next), cx, |s| {
-                s.select_ranges([Point::new(100, 0)..Point::new(100, 0)])
-            });
-        });
-        cx.foreground().advance_clock(Duration::from_millis(
-            INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100,
-        ));
-        cx.foreground().run_until_parked();
-        let last_scroll_update_version = editor.update(cx, |editor, cx| {
-            let expected_hints = vec![
-                "main hint #0".to_string(),
-                "main hint #1".to_string(),
-                "main hint #2".to_string(),
-                "main hint #3".to_string(),
-                "main hint #4".to_string(),
-                "main hint #5".to_string(),
-                "other hint #0".to_string(),
-                "other hint #1".to_string(),
-                "other hint #2".to_string(),
-                "other hint #3".to_string(),
-                "other hint #4".to_string(),
-                "other hint #5".to_string(),
-            ];
-            assert_eq!(expected_hints, cached_hint_labels(editor),
-                "After multibuffer was scrolled to the end, all hints for all excerpts should be fetched");
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            assert_eq!(editor.inlay_hint_cache().version, expected_hints.len());
-            expected_hints.len()
-        });
-
-        editor.update(cx, |editor, cx| {
-            editor.change_selections(Some(Autoscroll::Next), cx, |s| {
-                s.select_ranges([Point::new(4, 0)..Point::new(4, 0)])
-            });
-        });
-        cx.foreground().run_until_parked();
-        editor.update(cx, |editor, cx| {
-            let expected_hints = vec![
-                "main hint #0".to_string(),
-                "main hint #1".to_string(),
-                "main hint #2".to_string(),
-                "main hint #3".to_string(),
-                "main hint #4".to_string(),
-                "main hint #5".to_string(),
-                "other hint #0".to_string(),
-                "other hint #1".to_string(),
-                "other hint #2".to_string(),
-                "other hint #3".to_string(),
-                "other hint #4".to_string(),
-                "other hint #5".to_string(),
-            ];
-            assert_eq!(expected_hints, cached_hint_labels(editor),
-                "After multibuffer was scrolled to the end, further scrolls up should not bring more hints");
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            assert_eq!(editor.inlay_hint_cache().version, last_scroll_update_version, "No updates should happen during scrolling already scolled buffer");
-        });
-
-        editor_edited.store(true, Ordering::Release);
-        editor.update(cx, |editor, cx| {
-            editor.change_selections(None, cx, |s| {
-                s.select_ranges([Point::new(56, 0)..Point::new(56, 0)])
-            });
-            editor.handle_input("++++more text++++", cx);
-        });
-        cx.foreground().run_until_parked();
-        editor.update(cx, |editor, cx| {
-            let expected_hints = vec![
-                "main hint(edited) #0".to_string(),
-                "main hint(edited) #1".to_string(),
-                "main hint(edited) #2".to_string(),
-                "main hint(edited) #3".to_string(),
-                "main hint(edited) #4".to_string(),
-                "main hint(edited) #5".to_string(),
-                "other hint(edited) #0".to_string(),
-                "other hint(edited) #1".to_string(),
-            ];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "After multibuffer edit, editor gets scolled back to the last selection; \
-all hints should be invalidated and requeried for all of its visible excerpts"
-            );
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-
-            let current_cache_version = editor.inlay_hint_cache().version;
-            let minimum_expected_version = last_scroll_update_version + expected_hints.len();
-            assert!(
-                current_cache_version == minimum_expected_version || current_cache_version == minimum_expected_version + 1,
-                "Due to every excerpt having one hint, cache should update per new excerpt received + 1 potential sporadic update"
-            );
-        });
-    }
-
-    #[gpui::test]
-    async fn test_excerpts_removed(
-        deterministic: Arc<Deterministic>,
-        cx: &mut gpui::TestAppContext,
-    ) {
-        init_test(cx, |settings| {
-            settings.defaults.inlay_hints = Some(InlayHintSettings {
-                enabled: true,
-                show_type_hints: false,
-                show_parameter_hints: false,
-                show_other_hints: false,
-            })
-        });
-
-        let mut language = Language::new(
-            LanguageConfig {
-                name: "Rust".into(),
-                path_suffixes: vec!["rs".to_string()],
-                ..Default::default()
-            },
-            Some(tree_sitter_rust::language()),
-        );
-        let mut fake_servers = language
-            .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
-                capabilities: lsp::ServerCapabilities {
-                    inlay_hint_provider: Some(lsp::OneOf::Left(true)),
-                    ..Default::default()
-                },
-                ..Default::default()
-            }))
-            .await;
-        let language = Arc::new(language);
-        let fs = FakeFs::new(cx.background());
-        fs.insert_tree(
-            "/a",
-            json!({
-                "main.rs": format!("fn main() {{\n{}\n}}", (0..501).map(|i| format!("let i = {i};\n")).collect::<Vec<_>>().join("")),
-                "other.rs": format!("fn main() {{\n{}\n}}", (0..501).map(|j| format!("let j = {j};\n")).collect::<Vec<_>>().join("")),
-            }),
-        )
-        .await;
-        let project = Project::test(fs, ["/a".as_ref()], cx).await;
-        project.update(cx, |project, _| {
-            project.languages().add(Arc::clone(&language))
-        });
-        let workspace = cx
-            .add_window(|cx| Workspace::test_new(project.clone(), cx))
-            .root(cx);
-        let worktree_id = workspace.update(cx, |workspace, cx| {
-            workspace.project().read_with(cx, |project, cx| {
-                project.worktrees(cx).next().unwrap().read(cx).id()
-            })
-        });
-
-        let buffer_1 = project
-            .update(cx, |project, cx| {
-                project.open_buffer((worktree_id, "main.rs"), cx)
-            })
-            .await
-            .unwrap();
-        let buffer_2 = project
-            .update(cx, |project, cx| {
-                project.open_buffer((worktree_id, "other.rs"), cx)
-            })
-            .await
-            .unwrap();
-        let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
-        let (buffer_1_excerpts, buffer_2_excerpts) = multibuffer.update(cx, |multibuffer, cx| {
-            let buffer_1_excerpts = multibuffer.push_excerpts(
-                buffer_1.clone(),
-                [ExcerptRange {
-                    context: Point::new(0, 0)..Point::new(2, 0),
-                    primary: None,
-                }],
-                cx,
-            );
-            let buffer_2_excerpts = multibuffer.push_excerpts(
-                buffer_2.clone(),
-                [ExcerptRange {
-                    context: Point::new(0, 1)..Point::new(2, 1),
-                    primary: None,
-                }],
-                cx,
-            );
-            (buffer_1_excerpts, buffer_2_excerpts)
-        });
-
-        assert!(!buffer_1_excerpts.is_empty());
-        assert!(!buffer_2_excerpts.is_empty());
-
-        deterministic.run_until_parked();
-        cx.foreground().run_until_parked();
-        let editor = cx
-            .add_window(|cx| Editor::for_multibuffer(multibuffer, Some(project.clone()), cx))
-            .root(cx);
-        let editor_edited = Arc::new(AtomicBool::new(false));
-        let fake_server = fake_servers.next().await.unwrap();
-        let closure_editor_edited = Arc::clone(&editor_edited);
-        fake_server
-            .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
-                let task_editor_edited = Arc::clone(&closure_editor_edited);
-                async move {
-                    let hint_text = if params.text_document.uri
-                        == lsp::Url::from_file_path("/a/main.rs").unwrap()
-                    {
-                        "main hint"
-                    } else if params.text_document.uri
-                        == lsp::Url::from_file_path("/a/other.rs").unwrap()
-                    {
-                        "other hint"
-                    } else {
-                        panic!("unexpected uri: {:?}", params.text_document.uri);
-                    };
-
-                    let positions = [
-                        lsp::Position::new(0, 2),
-                        lsp::Position::new(4, 2),
-                        lsp::Position::new(22, 2),
-                        lsp::Position::new(44, 2),
-                        lsp::Position::new(56, 2),
-                        lsp::Position::new(67, 2),
-                    ];
-                    let out_of_range_hint = lsp::InlayHint {
-                        position: lsp::Position::new(
-                            params.range.start.line + 99,
-                            params.range.start.character + 99,
-                        ),
-                        label: lsp::InlayHintLabel::String(
-                            "out of excerpt range, should be ignored".to_string(),
-                        ),
-                        kind: None,
-                        text_edits: None,
-                        tooltip: None,
-                        padding_left: None,
-                        padding_right: None,
-                        data: None,
-                    };
-
-                    let edited = task_editor_edited.load(Ordering::Acquire);
-                    Ok(Some(
-                        std::iter::once(out_of_range_hint)
-                            .chain(positions.into_iter().enumerate().map(|(i, position)| {
-                                lsp::InlayHint {
-                                    position,
-                                    label: lsp::InlayHintLabel::String(format!(
-                                        "{hint_text}{} #{i}",
-                                        if edited { "(edited)" } else { "" },
-                                    )),
-                                    kind: None,
-                                    text_edits: None,
-                                    tooltip: None,
-                                    padding_left: None,
-                                    padding_right: None,
-                                    data: None,
-                                }
-                            }))
-                            .collect(),
-                    ))
-                }
-            })
-            .next()
-            .await;
-        cx.foreground().run_until_parked();
-
-        editor.update(cx, |editor, cx| {
-            assert_eq!(
-                vec!["main hint #0".to_string(), "other hint #0".to_string()],
-                cached_hint_labels(editor),
-                "Cache should update for both excerpts despite hints display was disabled"
-            );
-            assert!(
-                visible_hint_labels(editor, cx).is_empty(),
-                "All hints are disabled and should not be shown despite being present in the cache"
-            );
-            assert_eq!(
-                editor.inlay_hint_cache().version,
-                2,
-                "Cache should update once per excerpt query"
-            );
-        });
-
-        editor.update(cx, |editor, cx| {
-            editor.buffer().update(cx, |multibuffer, cx| {
-                multibuffer.remove_excerpts(buffer_2_excerpts, cx)
-            })
-        });
-        cx.foreground().run_until_parked();
-        editor.update(cx, |editor, cx| {
-            assert_eq!(
-                vec!["main hint #0".to_string()],
-                cached_hint_labels(editor),
-                "For the removed excerpt, should clean corresponding cached hints"
-            );
-            assert!(
-                visible_hint_labels(editor, cx).is_empty(),
-                "All hints are disabled and should not be shown despite being present in the cache"
-            );
-            assert_eq!(
-                editor.inlay_hint_cache().version,
-                3,
-                "Excerpt removal should trigger a cache update"
-            );
-        });
-
-        update_test_language_settings(cx, |settings| {
-            settings.defaults.inlay_hints = Some(InlayHintSettings {
-                enabled: true,
-                show_type_hints: true,
-                show_parameter_hints: true,
-                show_other_hints: true,
-            })
-        });
-        cx.foreground().run_until_parked();
-        editor.update(cx, |editor, cx| {
-            let expected_hints = vec!["main hint #0".to_string()];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "Hint display settings change should not change the cache"
-            );
-            assert_eq!(
-                expected_hints,
-                visible_hint_labels(editor, cx),
-                "Settings change should make cached hints visible"
-            );
-            assert_eq!(
-                editor.inlay_hint_cache().version,
-                4,
-                "Settings change should trigger a cache update"
-            );
-        });
-    }
-
-    #[gpui::test]
-    async fn test_inside_char_boundary_range_hints(cx: &mut gpui::TestAppContext) {
-        init_test(cx, |settings| {
-            settings.defaults.inlay_hints = Some(InlayHintSettings {
-                enabled: true,
-                show_type_hints: true,
-                show_parameter_hints: true,
-                show_other_hints: true,
-            })
-        });
-
-        let mut language = Language::new(
-            LanguageConfig {
-                name: "Rust".into(),
-                path_suffixes: vec!["rs".to_string()],
-                ..Default::default()
-            },
-            Some(tree_sitter_rust::language()),
-        );
-        let mut fake_servers = language
-            .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
-                capabilities: lsp::ServerCapabilities {
-                    inlay_hint_provider: Some(lsp::OneOf::Left(true)),
-                    ..Default::default()
-                },
-                ..Default::default()
-            }))
-            .await;
-        let fs = FakeFs::new(cx.background());
-        fs.insert_tree(
-            "/a",
-            json!({
-                "main.rs": format!(r#"fn main() {{\n{}\n}}"#, format!("let i = {};\n", "√".repeat(10)).repeat(500)),
-                "other.rs": "// Test file",
-            }),
-        )
-        .await;
-        let project = Project::test(fs, ["/a".as_ref()], cx).await;
-        project.update(cx, |project, _| project.languages().add(Arc::new(language)));
-        let workspace = cx
-            .add_window(|cx| Workspace::test_new(project.clone(), cx))
-            .root(cx);
-        let worktree_id = workspace.update(cx, |workspace, cx| {
-            workspace.project().read_with(cx, |project, cx| {
-                project.worktrees(cx).next().unwrap().read(cx).id()
-            })
-        });
-
-        let _buffer = project
-            .update(cx, |project, cx| {
-                project.open_local_buffer("/a/main.rs", cx)
-            })
-            .await
-            .unwrap();
-        cx.foreground().run_until_parked();
-        cx.foreground().start_waiting();
-        let fake_server = fake_servers.next().await.unwrap();
-        let editor = workspace
-            .update(cx, |workspace, cx| {
-                workspace.open_path((worktree_id, "main.rs"), None, true, cx)
-            })
-            .await
-            .unwrap()
-            .downcast::<Editor>()
-            .unwrap();
-        let lsp_request_count = Arc::new(AtomicU32::new(0));
-        let closure_lsp_request_count = Arc::clone(&lsp_request_count);
-        fake_server
-            .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
-                let task_lsp_request_count = Arc::clone(&closure_lsp_request_count);
-                async move {
-                    assert_eq!(
-                        params.text_document.uri,
-                        lsp::Url::from_file_path("/a/main.rs").unwrap(),
-                    );
-                    let query_start = params.range.start;
-                    let i = Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::Release) + 1;
-                    Ok(Some(vec![lsp::InlayHint {
-                        position: query_start,
-                        label: lsp::InlayHintLabel::String(i.to_string()),
-                        kind: None,
-                        text_edits: None,
-                        tooltip: None,
-                        padding_left: None,
-                        padding_right: None,
-                        data: None,
-                    }]))
-                }
-            })
-            .next()
-            .await;
-
-        cx.foreground().run_until_parked();
-        editor.update(cx, |editor, cx| {
-            editor.change_selections(None, cx, |s| {
-                s.select_ranges([Point::new(10, 0)..Point::new(10, 0)])
-            })
-        });
-        cx.foreground().run_until_parked();
-        editor.update(cx, |editor, cx| {
-            let expected_hints = vec!["1".to_string()];
-            assert_eq!(expected_hints, cached_hint_labels(editor));
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            assert_eq!(editor.inlay_hint_cache().version, 1);
-        });
-    }
-
-    #[gpui::test]
-    async fn test_toggle_inlay_hints(cx: &mut gpui::TestAppContext) {
-        init_test(cx, |settings| {
-            settings.defaults.inlay_hints = Some(InlayHintSettings {
-                enabled: false,
-                show_type_hints: true,
-                show_parameter_hints: true,
-                show_other_hints: true,
-            })
-        });
-
-        let (file_with_hints, editor, fake_server) = prepare_test_objects(cx).await;
-
-        editor.update(cx, |editor, cx| {
-            editor.toggle_inlay_hints(&crate::ToggleInlayHints, cx)
-        });
-        cx.foreground().start_waiting();
-        let lsp_request_count = Arc::new(AtomicU32::new(0));
-        let closure_lsp_request_count = Arc::clone(&lsp_request_count);
-        fake_server
-            .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
-                let task_lsp_request_count = Arc::clone(&closure_lsp_request_count);
-                async move {
-                    assert_eq!(
-                        params.text_document.uri,
-                        lsp::Url::from_file_path(file_with_hints).unwrap(),
-                    );
-
-                    let i = Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst) + 1;
-                    Ok(Some(vec![lsp::InlayHint {
-                        position: lsp::Position::new(0, i),
-                        label: lsp::InlayHintLabel::String(i.to_string()),
-                        kind: None,
-                        text_edits: None,
-                        tooltip: None,
-                        padding_left: None,
-                        padding_right: None,
-                        data: None,
-                    }]))
-                }
-            })
-            .next()
-            .await;
-        cx.foreground().run_until_parked();
-        editor.update(cx, |editor, cx| {
-            let expected_hints = vec!["1".to_string()];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "Should display inlays after toggle despite them disabled in settings"
-            );
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            assert_eq!(
-                editor.inlay_hint_cache().version,
-                1,
-                "First toggle should be cache's first update"
-            );
-        });
-
-        editor.update(cx, |editor, cx| {
-            editor.toggle_inlay_hints(&crate::ToggleInlayHints, cx)
-        });
-        cx.foreground().run_until_parked();
-        editor.update(cx, |editor, cx| {
-            assert!(
-                cached_hint_labels(editor).is_empty(),
-                "Should clear hints after 2nd toggle"
-            );
-            assert!(visible_hint_labels(editor, cx).is_empty());
-            assert_eq!(editor.inlay_hint_cache().version, 2);
-        });
-
-        update_test_language_settings(cx, |settings| {
-            settings.defaults.inlay_hints = Some(InlayHintSettings {
-                enabled: true,
-                show_type_hints: true,
-                show_parameter_hints: true,
-                show_other_hints: true,
-            })
-        });
-        cx.foreground().run_until_parked();
-        editor.update(cx, |editor, cx| {
-            let expected_hints = vec!["2".to_string()];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "Should query LSP hints for the 2nd time after enabling hints in settings"
-            );
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            assert_eq!(editor.inlay_hint_cache().version, 3);
-        });
-
-        editor.update(cx, |editor, cx| {
-            editor.toggle_inlay_hints(&crate::ToggleInlayHints, cx)
-        });
-        cx.foreground().run_until_parked();
-        editor.update(cx, |editor, cx| {
-            assert!(
-                cached_hint_labels(editor).is_empty(),
-                "Should clear hints after enabling in settings and a 3rd toggle"
-            );
-            assert!(visible_hint_labels(editor, cx).is_empty());
-            assert_eq!(editor.inlay_hint_cache().version, 4);
-        });
-
-        editor.update(cx, |editor, cx| {
-            editor.toggle_inlay_hints(&crate::ToggleInlayHints, cx)
-        });
-        cx.foreground().run_until_parked();
-        editor.update(cx, |editor, cx| {
-            let expected_hints = vec!["3".to_string()];
-            assert_eq!(
-                expected_hints,
-                cached_hint_labels(editor),
-                "Should query LSP hints for the 3rd time after enabling hints in settings and toggling them back on"
-            );
-            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-            assert_eq!(editor.inlay_hint_cache().version, 5);
-        });
-    }
-
-    pub(crate) fn init_test(cx: &mut TestAppContext, f: impl Fn(&mut AllLanguageSettingsContent)) {
-        cx.foreground().forbid_parking();
-
-        cx.update(|cx| {
-            cx.set_global(SettingsStore::test(cx));
-            theme::init(cx);
-            client::init_settings(cx);
-            language::init(cx);
-            Project::init_settings(cx);
-            workspace::init_settings(cx);
-            crate::init(cx);
-        });
-
-        update_test_language_settings(cx, f);
-    }
-
-    async fn prepare_test_objects(
-        cx: &mut TestAppContext,
-    ) -> (&'static str, ViewHandle<Editor>, FakeLanguageServer) {
-        let mut language = Language::new(
-            LanguageConfig {
-                name: "Rust".into(),
-                path_suffixes: vec!["rs".to_string()],
-                ..Default::default()
-            },
-            Some(tree_sitter_rust::language()),
-        );
-        let mut fake_servers = language
-            .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
-                capabilities: lsp::ServerCapabilities {
-                    inlay_hint_provider: Some(lsp::OneOf::Left(true)),
-                    ..Default::default()
-                },
-                ..Default::default()
-            }))
-            .await;
-
-        let fs = FakeFs::new(cx.background());
-        fs.insert_tree(
-            "/a",
-            json!({
-                "main.rs": "fn main() { a } // and some long comment to ensure inlays are not trimmed out",
-                "other.rs": "// Test file",
-            }),
-        )
-        .await;
-
-        let project = Project::test(fs, ["/a".as_ref()], cx).await;
-        project.update(cx, |project, _| project.languages().add(Arc::new(language)));
-        let workspace = cx
-            .add_window(|cx| Workspace::test_new(project.clone(), cx))
-            .root(cx);
-        let worktree_id = workspace.update(cx, |workspace, cx| {
-            workspace.project().read_with(cx, |project, cx| {
-                project.worktrees(cx).next().unwrap().read(cx).id()
-            })
-        });
-
-        let _buffer = project
-            .update(cx, |project, cx| {
-                project.open_local_buffer("/a/main.rs", cx)
-            })
-            .await
-            .unwrap();
-        cx.foreground().run_until_parked();
-        cx.foreground().start_waiting();
-        let fake_server = fake_servers.next().await.unwrap();
-        let editor = workspace
-            .update(cx, |workspace, cx| {
-                workspace.open_path((worktree_id, "main.rs"), None, true, cx)
-            })
-            .await
-            .unwrap()
-            .downcast::<Editor>()
-            .unwrap();
-
-        editor.update(cx, |editor, cx| {
-            assert!(cached_hint_labels(editor).is_empty());
-            assert!(visible_hint_labels(editor, cx).is_empty());
-            assert_eq!(editor.inlay_hint_cache().version, 0);
-        });
-
-        ("/a/main.rs", editor, fake_server)
-    }
-
-    pub fn cached_hint_labels(editor: &Editor) -> Vec<String> {
-        let mut labels = Vec::new();
-        for (_, excerpt_hints) in &editor.inlay_hint_cache().hints {
-            let excerpt_hints = excerpt_hints.read();
-            for id in &excerpt_hints.ordered_hints {
-                labels.push(excerpt_hints.hints_by_id[id].text());
-            }
-        }
-
-        labels.sort();
-        labels
-    }
-
-    pub fn visible_hint_labels(editor: &Editor, cx: &ViewContext<'_, '_, Editor>) -> Vec<String> {
-        let mut hints = editor
-            .visible_inlay_hints(cx)
-            .into_iter()
-            .map(|hint| hint.text.to_string())
-            .collect::<Vec<_>>();
-        hints.sort();
-        hints
-    }
-}
+// #[cfg(test)]
+// pub mod tests {
+//     use std::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize, Ordering};
+
+//     use crate::{
+//         scroll::{autoscroll::Autoscroll, scroll_amount::ScrollAmount},
+//         serde_json::json,
+//         ExcerptRange,
+//     };
+//     use futures::StreamExt;
+//     use gpui::{executor::Deterministic, TestAppContext, View};
+//     use itertools::Itertools;
+//     use language::{
+//         language_settings::AllLanguageSettingsContent, FakeLspAdapter, Language, LanguageConfig,
+//     };
+//     use lsp::FakeLanguageServer;
+//     use parking_lot::Mutex;
+//     use project::{FakeFs, Project};
+//     use settings::SettingsStore;
+//     use text::{Point, ToPoint};
+//     use workspace::Workspace;
+
+//     use crate::editor_tests::update_test_language_settings;
+
+//     use super::*;
+
+//     #[gpui::test]
+//     async fn test_basic_cache_update_with_duplicate_hints(cx: &mut gpui::TestAppContext) {
+//         let allowed_hint_kinds = HashSet::from_iter([None, Some(InlayHintKind::Type)]);
+//         init_test(cx, |settings| {
+//             settings.defaults.inlay_hints = Some(InlayHintSettings {
+//                 enabled: true,
+//                 show_type_hints: allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
+//                 show_parameter_hints: allowed_hint_kinds.contains(&Some(InlayHintKind::Parameter)),
+//                 show_other_hints: allowed_hint_kinds.contains(&None),
+//             })
+//         });
+
+//         let (file_with_hints, editor, fake_server) = prepare_test_objects(cx).await;
+//         let lsp_request_count = Arc::new(AtomicU32::new(0));
+//         fake_server
+//             .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
+//                 let task_lsp_request_count = Arc::clone(&lsp_request_count);
+//                 async move {
+//                     assert_eq!(
+//                         params.text_document.uri,
+//                         lsp::Url::from_file_path(file_with_hints).unwrap(),
+//                     );
+//                     let current_call_id =
+//                         Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst);
+//                     let mut new_hints = Vec::with_capacity(2 * current_call_id as usize);
+//                     for _ in 0..2 {
+//                         let mut i = current_call_id;
+//                         loop {
+//                             new_hints.push(lsp::InlayHint {
+//                                 position: lsp::Position::new(0, i),
+//                                 label: lsp::InlayHintLabel::String(i.to_string()),
+//                                 kind: None,
+//                                 text_edits: None,
+//                                 tooltip: None,
+//                                 padding_left: None,
+//                                 padding_right: None,
+//                                 data: None,
+//                             });
+//                             if i == 0 {
+//                                 break;
+//                             }
+//                             i -= 1;
+//                         }
+//                     }
+
+//                     Ok(Some(new_hints))
+//                 }
+//             })
+//             .next()
+//             .await;
+//         cx.foreground().run_until_parked();
+
+//         let mut edits_made = 1;
+//         editor.update(cx, |editor, cx| {
+//             let expected_hints = vec!["0".to_string()];
+//             assert_eq!(
+//                 expected_hints,
+//                 cached_hint_labels(editor),
+//                 "Should get its first hints when opening the editor"
+//             );
+//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+//             let inlay_cache = editor.inlay_hint_cache();
+//             assert_eq!(
+//                 inlay_cache.allowed_hint_kinds, allowed_hint_kinds,
+//                 "Cache should use editor settings to get the allowed hint kinds"
+//             );
+//             assert_eq!(
+//                 inlay_cache.version, edits_made,
+//                 "The editor update the cache version after every cache/view change"
+//             );
+//         });
+
+//         editor.update(cx, |editor, cx| {
+//             editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
+//             editor.handle_input("some change", cx);
+//             edits_made += 1;
+//         });
+//         cx.foreground().run_until_parked();
+//         editor.update(cx, |editor, cx| {
+//             let expected_hints = vec!["0".to_string(), "1".to_string()];
+//             assert_eq!(
+//                 expected_hints,
+//                 cached_hint_labels(editor),
+//                 "Should get new hints after an edit"
+//             );
+//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+//             let inlay_cache = editor.inlay_hint_cache();
+//             assert_eq!(
+//                 inlay_cache.allowed_hint_kinds, allowed_hint_kinds,
+//                 "Cache should use editor settings to get the allowed hint kinds"
+//             );
+//             assert_eq!(
+//                 inlay_cache.version, edits_made,
+//                 "The editor update the cache version after every cache/view change"
+//             );
+//         });
+
+//         fake_server
+//             .request::<lsp::request::InlayHintRefreshRequest>(())
+//             .await
+//             .expect("inlay refresh request failed");
+//         edits_made += 1;
+//         cx.foreground().run_until_parked();
+//         editor.update(cx, |editor, cx| {
+//             let expected_hints = vec!["0".to_string(), "1".to_string(), "2".to_string()];
+//             assert_eq!(
+//                 expected_hints,
+//                 cached_hint_labels(editor),
+//                 "Should get new hints after hint refresh/ request"
+//             );
+//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+//             let inlay_cache = editor.inlay_hint_cache();
+//             assert_eq!(
+//                 inlay_cache.allowed_hint_kinds, allowed_hint_kinds,
+//                 "Cache should use editor settings to get the allowed hint kinds"
+//             );
+//             assert_eq!(
+//                 inlay_cache.version, edits_made,
+//                 "The editor update the cache version after every cache/view change"
+//             );
+//         });
+//     }
+
+//     #[gpui::test]
+//     async fn test_cache_update_on_lsp_completion_tasks(cx: &mut gpui::TestAppContext) {
+//         init_test(cx, |settings| {
+//             settings.defaults.inlay_hints = Some(InlayHintSettings {
+//                 enabled: true,
+//                 show_type_hints: true,
+//                 show_parameter_hints: true,
+//                 show_other_hints: true,
+//             })
+//         });
+
+//         let (file_with_hints, editor, fake_server) = prepare_test_objects(cx).await;
+//         let lsp_request_count = Arc::new(AtomicU32::new(0));
+//         fake_server
+//             .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
+//                 let task_lsp_request_count = Arc::clone(&lsp_request_count);
+//                 async move {
+//                     assert_eq!(
+//                         params.text_document.uri,
+//                         lsp::Url::from_file_path(file_with_hints).unwrap(),
+//                     );
+//                     let current_call_id =
+//                         Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst);
+//                     Ok(Some(vec![lsp::InlayHint {
+//                         position: lsp::Position::new(0, current_call_id),
+//                         label: lsp::InlayHintLabel::String(current_call_id.to_string()),
+//                         kind: None,
+//                         text_edits: None,
+//                         tooltip: None,
+//                         padding_left: None,
+//                         padding_right: None,
+//                         data: None,
+//                     }]))
+//                 }
+//             })
+//             .next()
+//             .await;
+//         cx.foreground().run_until_parked();
+
+//         let mut edits_made = 1;
+//         editor.update(cx, |editor, cx| {
+//             let expected_hints = vec!["0".to_string()];
+//             assert_eq!(
+//                 expected_hints,
+//                 cached_hint_labels(editor),
+//                 "Should get its first hints when opening the editor"
+//             );
+//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+//             assert_eq!(
+//                 editor.inlay_hint_cache().version,
+//                 edits_made,
+//                 "The editor update the cache version after every cache/view change"
+//             );
+//         });
+
+//         let progress_token = "test_progress_token";
+//         fake_server
+//             .request::<lsp::request::WorkDoneProgressCreate>(lsp::WorkDoneProgressCreateParams {
+//                 token: lsp::ProgressToken::String(progress_token.to_string()),
+//             })
+//             .await
+//             .expect("work done progress create request failed");
+//         cx.foreground().run_until_parked();
+//         fake_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
+//             token: lsp::ProgressToken::String(progress_token.to_string()),
+//             value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Begin(
+//                 lsp::WorkDoneProgressBegin::default(),
+//             )),
+//         });
+//         cx.foreground().run_until_parked();
+
+//         editor.update(cx, |editor, cx| {
+//             let expected_hints = vec!["0".to_string()];
+//             assert_eq!(
+//                 expected_hints,
+//                 cached_hint_labels(editor),
+//                 "Should not update hints while the work task is running"
+//             );
+//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+//             assert_eq!(
+//                 editor.inlay_hint_cache().version,
+//                 edits_made,
+//                 "Should not update the cache while the work task is running"
+//             );
+//         });
+
+//         fake_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
+//             token: lsp::ProgressToken::String(progress_token.to_string()),
+//             value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::End(
+//                 lsp::WorkDoneProgressEnd::default(),
+//             )),
+//         });
+//         cx.foreground().run_until_parked();
+
+//         edits_made += 1;
+//         editor.update(cx, |editor, cx| {
+//             let expected_hints = vec!["1".to_string()];
+//             assert_eq!(
+//                 expected_hints,
+//                 cached_hint_labels(editor),
+//                 "New hints should be queried after the work task is done"
+//             );
+//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+//             assert_eq!(
+//                 editor.inlay_hint_cache().version,
+//                 edits_made,
+//                 "Cache version should udpate once after the work task is done"
+//             );
+//         });
+//     }
+
+//     #[gpui::test]
+//     async fn test_no_hint_updates_for_unrelated_language_files(cx: &mut gpui::TestAppContext) {
+//         init_test(cx, |settings| {
+//             settings.defaults.inlay_hints = Some(InlayHintSettings {
+//                 enabled: true,
+//                 show_type_hints: true,
+//                 show_parameter_hints: true,
+//                 show_other_hints: true,
+//             })
+//         });
+
+//         let fs = FakeFs::new(cx.background());
+//         fs.insert_tree(
+//                     "/a",
+//                     json!({
+//                         "main.rs": "fn main() { a } // and some long comment to ensure inlays are not trimmed out",
+//                         "other.md": "Test md file with some text",
+//                     }),
+//                 )
+//                 .await;
+//         let project = Project::test(fs, ["/a".as_ref()], cx).await;
+//         let workspace = cx
+//             .add_window(|cx| Workspace::test_new(project.clone(), cx))
+//             .root(cx);
+//         let worktree_id = workspace.update(cx, |workspace, cx| {
+//             workspace.project().read_with(cx, |project, cx| {
+//                 project.worktrees(cx).next().unwrap().read(cx).id()
+//             })
+//         });
+
+//         let mut rs_fake_servers = None;
+//         let mut md_fake_servers = None;
+//         for (name, path_suffix) in [("Rust", "rs"), ("Markdown", "md")] {
+//             let mut language = Language::new(
+//                 LanguageConfig {
+//                     name: name.into(),
+//                     path_suffixes: vec![path_suffix.to_string()],
+//                     ..Default::default()
+//                 },
+//                 Some(tree_sitter_rust::language()),
+//             );
+//             let fake_servers = language
+//                 .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
+//                     name,
+//                     capabilities: lsp::ServerCapabilities {
+//                         inlay_hint_provider: Some(lsp::OneOf::Left(true)),
+//                         ..Default::default()
+//                     },
+//                     ..Default::default()
+//                 }))
+//                 .await;
+//             match name {
+//                 "Rust" => rs_fake_servers = Some(fake_servers),
+//                 "Markdown" => md_fake_servers = Some(fake_servers),
+//                 _ => unreachable!(),
+//             }
+//             project.update(cx, |project, _| {
+//                 project.languages().add(Arc::new(language));
+//             });
+//         }
+
+//         let _rs_buffer = project
+//             .update(cx, |project, cx| {
+//                 project.open_local_buffer("/a/main.rs", cx)
+//             })
+//             .await
+//             .unwrap();
+//         cx.foreground().run_until_parked();
+//         cx.foreground().start_waiting();
+//         let rs_fake_server = rs_fake_servers.unwrap().next().await.unwrap();
+//         let rs_editor = workspace
+//             .update(cx, |workspace, cx| {
+//                 workspace.open_path((worktree_id, "main.rs"), None, true, cx)
+//             })
+//             .await
+//             .unwrap()
+//             .downcast::<Editor>()
+//             .unwrap();
+//         let rs_lsp_request_count = Arc::new(AtomicU32::new(0));
+//         rs_fake_server
+//             .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
+//                 let task_lsp_request_count = Arc::clone(&rs_lsp_request_count);
+//                 async move {
+//                     assert_eq!(
+//                         params.text_document.uri,
+//                         lsp::Url::from_file_path("/a/main.rs").unwrap(),
+//                     );
+//                     let i = Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst);
+//                     Ok(Some(vec![lsp::InlayHint {
+//                         position: lsp::Position::new(0, i),
+//                         label: lsp::InlayHintLabel::String(i.to_string()),
+//                         kind: None,
+//                         text_edits: None,
+//                         tooltip: None,
+//                         padding_left: None,
+//                         padding_right: None,
+//                         data: None,
+//                     }]))
+//                 }
+//             })
+//             .next()
+//             .await;
+//         cx.foreground().run_until_parked();
+//         rs_editor.update(cx, |editor, cx| {
+//             let expected_hints = vec!["0".to_string()];
+//             assert_eq!(
+//                 expected_hints,
+//                 cached_hint_labels(editor),
+//                 "Should get its first hints when opening the editor"
+//             );
+//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+//             assert_eq!(
+//                 editor.inlay_hint_cache().version,
+//                 1,
+//                 "Rust editor update the cache version after every cache/view change"
+//             );
+//         });
+
+//         cx.foreground().run_until_parked();
+//         let _md_buffer = project
+//             .update(cx, |project, cx| {
+//                 project.open_local_buffer("/a/other.md", cx)
+//             })
+//             .await
+//             .unwrap();
+//         cx.foreground().run_until_parked();
+//         cx.foreground().start_waiting();
+//         let md_fake_server = md_fake_servers.unwrap().next().await.unwrap();
+//         let md_editor = workspace
+//             .update(cx, |workspace, cx| {
+//                 workspace.open_path((worktree_id, "other.md"), None, true, cx)
+//             })
+//             .await
+//             .unwrap()
+//             .downcast::<Editor>()
+//             .unwrap();
+//         let md_lsp_request_count = Arc::new(AtomicU32::new(0));
+//         md_fake_server
+//             .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
+//                 let task_lsp_request_count = Arc::clone(&md_lsp_request_count);
+//                 async move {
+//                     assert_eq!(
+//                         params.text_document.uri,
+//                         lsp::Url::from_file_path("/a/other.md").unwrap(),
+//                     );
+//                     let i = Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst);
+//                     Ok(Some(vec![lsp::InlayHint {
+//                         position: lsp::Position::new(0, i),
+//                         label: lsp::InlayHintLabel::String(i.to_string()),
+//                         kind: None,
+//                         text_edits: None,
+//                         tooltip: None,
+//                         padding_left: None,
+//                         padding_right: None,
+//                         data: None,
+//                     }]))
+//                 }
+//             })
+//             .next()
+//             .await;
+//         cx.foreground().run_until_parked();
+//         md_editor.update(cx, |editor, cx| {
+//             let expected_hints = vec!["0".to_string()];
+//             assert_eq!(
+//                 expected_hints,
+//                 cached_hint_labels(editor),
+//                 "Markdown editor should have a separate verison, repeating Rust editor rules"
+//             );
+//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+//             assert_eq!(editor.inlay_hint_cache().version, 1);
+//         });
+
+//         rs_editor.update(cx, |editor, cx| {
+//             editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
+//             editor.handle_input("some rs change", cx);
+//         });
+//         cx.foreground().run_until_parked();
+//         rs_editor.update(cx, |editor, cx| {
+//             let expected_hints = vec!["1".to_string()];
+//             assert_eq!(
+//                 expected_hints,
+//                 cached_hint_labels(editor),
+//                 "Rust inlay cache should change after the edit"
+//             );
+//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+//             assert_eq!(
+//                 editor.inlay_hint_cache().version,
+//                 2,
+//                 "Every time hint cache changes, cache version should be incremented"
+//             );
+//         });
+//         md_editor.update(cx, |editor, cx| {
+//             let expected_hints = vec!["0".to_string()];
+//             assert_eq!(
+//                 expected_hints,
+//                 cached_hint_labels(editor),
+//                 "Markdown editor should not be affected by Rust editor changes"
+//             );
+//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+//             assert_eq!(editor.inlay_hint_cache().version, 1);
+//         });
+
+//         md_editor.update(cx, |editor, cx| {
+//             editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
+//             editor.handle_input("some md change", cx);
+//         });
+//         cx.foreground().run_until_parked();
+//         md_editor.update(cx, |editor, cx| {
+//             let expected_hints = vec!["1".to_string()];
+//             assert_eq!(
+//                 expected_hints,
+//                 cached_hint_labels(editor),
+//                 "Rust editor should not be affected by Markdown editor changes"
+//             );
+//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+//             assert_eq!(editor.inlay_hint_cache().version, 2);
+//         });
+//         rs_editor.update(cx, |editor, cx| {
+//             let expected_hints = vec!["1".to_string()];
+//             assert_eq!(
+//                 expected_hints,
+//                 cached_hint_labels(editor),
+//                 "Markdown editor should also change independently"
+//             );
+//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+//             assert_eq!(editor.inlay_hint_cache().version, 2);
+//         });
+//     }
+
+//     #[gpui::test]
+//     async fn test_hint_setting_changes(cx: &mut gpui::TestAppContext) {
+//         let allowed_hint_kinds = HashSet::from_iter([None, Some(InlayHintKind::Type)]);
+//         init_test(cx, |settings| {
+//             settings.defaults.inlay_hints = Some(InlayHintSettings {
+//                 enabled: true,
+//                 show_type_hints: allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
+//                 show_parameter_hints: allowed_hint_kinds.contains(&Some(InlayHintKind::Parameter)),
+//                 show_other_hints: allowed_hint_kinds.contains(&None),
+//             })
+//         });
+
+//         let (file_with_hints, editor, fake_server) = prepare_test_objects(cx).await;
+//         let lsp_request_count = Arc::new(AtomicU32::new(0));
+//         let another_lsp_request_count = Arc::clone(&lsp_request_count);
+//         fake_server
+//             .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
+//                 let task_lsp_request_count = Arc::clone(&another_lsp_request_count);
+//                 async move {
+//                     Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst);
+//                     assert_eq!(
+//                         params.text_document.uri,
+//                         lsp::Url::from_file_path(file_with_hints).unwrap(),
+//                     );
+//                     Ok(Some(vec![
+//                         lsp::InlayHint {
+//                             position: lsp::Position::new(0, 1),
+//                             label: lsp::InlayHintLabel::String("type hint".to_string()),
+//                             kind: Some(lsp::InlayHintKind::TYPE),
+//                             text_edits: None,
+//                             tooltip: None,
+//                             padding_left: None,
+//                             padding_right: None,
+//                             data: None,
+//                         },
+//                         lsp::InlayHint {
+//                             position: lsp::Position::new(0, 2),
+//                             label: lsp::InlayHintLabel::String("parameter hint".to_string()),
+//                             kind: Some(lsp::InlayHintKind::PARAMETER),
+//                             text_edits: None,
+//                             tooltip: None,
+//                             padding_left: None,
+//                             padding_right: None,
+//                             data: None,
+//                         },
+//                         lsp::InlayHint {
+//                             position: lsp::Position::new(0, 3),
+//                             label: lsp::InlayHintLabel::String("other hint".to_string()),
+//                             kind: None,
+//                             text_edits: None,
+//                             tooltip: None,
+//                             padding_left: None,
+//                             padding_right: None,
+//                             data: None,
+//                         },
+//                     ]))
+//                 }
+//             })
+//             .next()
+//             .await;
+//         cx.foreground().run_until_parked();
+
+//         let mut edits_made = 1;
+//         editor.update(cx, |editor, cx| {
+//             assert_eq!(
+//                 lsp_request_count.load(Ordering::Relaxed),
+//                 1,
+//                 "Should query new hints once"
+//             );
+//             assert_eq!(
+//                 vec![
+//                     "other hint".to_string(),
+//                     "parameter hint".to_string(),
+//                     "type hint".to_string(),
+//                 ],
+//                 cached_hint_labels(editor),
+//                 "Should get its first hints when opening the editor"
+//             );
+//             assert_eq!(
+//                 vec!["other hint".to_string(), "type hint".to_string()],
+//                 visible_hint_labels(editor, cx)
+//             );
+//             let inlay_cache = editor.inlay_hint_cache();
+//             assert_eq!(
+//                 inlay_cache.allowed_hint_kinds, allowed_hint_kinds,
+//                 "Cache should use editor settings to get the allowed hint kinds"
+//             );
+//             assert_eq!(
+//                 inlay_cache.version, edits_made,
+//                 "The editor update the cache version after every cache/view change"
+//             );
+//         });
+
+//         fake_server
+//             .request::<lsp::request::InlayHintRefreshRequest>(())
+//             .await
+//             .expect("inlay refresh request failed");
+//         cx.foreground().run_until_parked();
+//         editor.update(cx, |editor, cx| {
+//             assert_eq!(
+//                 lsp_request_count.load(Ordering::Relaxed),
+//                 2,
+//                 "Should load new hints twice"
+//             );
+//             assert_eq!(
+//                 vec![
+//                     "other hint".to_string(),
+//                     "parameter hint".to_string(),
+//                     "type hint".to_string(),
+//                 ],
+//                 cached_hint_labels(editor),
+//                 "Cached hints should not change due to allowed hint kinds settings update"
+//             );
+//             assert_eq!(
+//                 vec!["other hint".to_string(), "type hint".to_string()],
+//                 visible_hint_labels(editor, cx)
+//             );
+//             assert_eq!(
+//                 editor.inlay_hint_cache().version,
+//                 edits_made,
+//                 "Should not update cache version due to new loaded hints being the same"
+//             );
+//         });
+
+//         for (new_allowed_hint_kinds, expected_visible_hints) in [
+//             (HashSet::from_iter([None]), vec!["other hint".to_string()]),
+//             (
+//                 HashSet::from_iter([Some(InlayHintKind::Type)]),
+//                 vec!["type hint".to_string()],
+//             ),
+//             (
+//                 HashSet::from_iter([Some(InlayHintKind::Parameter)]),
+//                 vec!["parameter hint".to_string()],
+//             ),
+//             (
+//                 HashSet::from_iter([None, Some(InlayHintKind::Type)]),
+//                 vec!["other hint".to_string(), "type hint".to_string()],
+//             ),
+//             (
+//                 HashSet::from_iter([None, Some(InlayHintKind::Parameter)]),
+//                 vec!["other hint".to_string(), "parameter hint".to_string()],
+//             ),
+//             (
+//                 HashSet::from_iter([Some(InlayHintKind::Type), Some(InlayHintKind::Parameter)]),
+//                 vec!["parameter hint".to_string(), "type hint".to_string()],
+//             ),
+//             (
+//                 HashSet::from_iter([
+//                     None,
+//                     Some(InlayHintKind::Type),
+//                     Some(InlayHintKind::Parameter),
+//                 ]),
+//                 vec![
+//                     "other hint".to_string(),
+//                     "parameter hint".to_string(),
+//                     "type hint".to_string(),
+//                 ],
+//             ),
+//         ] {
+//             edits_made += 1;
+//             update_test_language_settings(cx, |settings| {
+//                 settings.defaults.inlay_hints = Some(InlayHintSettings {
+//                     enabled: true,
+//                     show_type_hints: new_allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
+//                     show_parameter_hints: new_allowed_hint_kinds
+//                         .contains(&Some(InlayHintKind::Parameter)),
+//                     show_other_hints: new_allowed_hint_kinds.contains(&None),
+//                 })
+//             });
+//             cx.foreground().run_until_parked();
+//             editor.update(cx, |editor, cx| {
+//                 assert_eq!(
+//                     lsp_request_count.load(Ordering::Relaxed),
+//                     2,
+//                     "Should not load new hints on allowed hint kinds change for hint kinds {new_allowed_hint_kinds:?}"
+//                 );
+//                 assert_eq!(
+//                     vec![
+//                         "other hint".to_string(),
+//                         "parameter hint".to_string(),
+//                         "type hint".to_string(),
+//                     ],
+//                     cached_hint_labels(editor),
+//                     "Should get its cached hints unchanged after the settings change for hint kinds {new_allowed_hint_kinds:?}"
+//                 );
+//                 assert_eq!(
+//                     expected_visible_hints,
+//                     visible_hint_labels(editor, cx),
+//                     "Should get its visible hints filtered after the settings change for hint kinds {new_allowed_hint_kinds:?}"
+//                 );
+//                 let inlay_cache = editor.inlay_hint_cache();
+//                 assert_eq!(
+//                     inlay_cache.allowed_hint_kinds, new_allowed_hint_kinds,
+//                     "Cache should use editor settings to get the allowed hint kinds for hint kinds {new_allowed_hint_kinds:?}"
+//                 );
+//                 assert_eq!(
+//                     inlay_cache.version, edits_made,
+//                     "The editor should update the cache version after every cache/view change for hint kinds {new_allowed_hint_kinds:?} due to visible hints change"
+//                 );
+//             });
+//         }
+
+//         edits_made += 1;
+//         let another_allowed_hint_kinds = HashSet::from_iter([Some(InlayHintKind::Type)]);
+//         update_test_language_settings(cx, |settings| {
+//             settings.defaults.inlay_hints = Some(InlayHintSettings {
+//                 enabled: false,
+//                 show_type_hints: another_allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
+//                 show_parameter_hints: another_allowed_hint_kinds
+//                     .contains(&Some(InlayHintKind::Parameter)),
+//                 show_other_hints: another_allowed_hint_kinds.contains(&None),
+//             })
+//         });
+//         cx.foreground().run_until_parked();
+//         editor.update(cx, |editor, cx| {
+//             assert_eq!(
+//                 lsp_request_count.load(Ordering::Relaxed),
+//                 2,
+//                 "Should not load new hints when hints got disabled"
+//             );
+//             assert!(
+//                 cached_hint_labels(editor).is_empty(),
+//                 "Should clear the cache when hints got disabled"
+//             );
+//             assert!(
+//                 visible_hint_labels(editor, cx).is_empty(),
+//                 "Should clear visible hints when hints got disabled"
+//             );
+//             let inlay_cache = editor.inlay_hint_cache();
+//             assert_eq!(
+//                 inlay_cache.allowed_hint_kinds, another_allowed_hint_kinds,
+//                 "Should update its allowed hint kinds even when hints got disabled"
+//             );
+//             assert_eq!(
+//                 inlay_cache.version, edits_made,
+//                 "The editor should update the cache version after hints got disabled"
+//             );
+//         });
+
+//         fake_server
+//             .request::<lsp::request::InlayHintRefreshRequest>(())
+//             .await
+//             .expect("inlay refresh request failed");
+//         cx.foreground().run_until_parked();
+//         editor.update(cx, |editor, cx| {
+//             assert_eq!(
+//                 lsp_request_count.load(Ordering::Relaxed),
+//                 2,
+//                 "Should not load new hints when they got disabled"
+//             );
+//             assert!(cached_hint_labels(editor).is_empty());
+//             assert!(visible_hint_labels(editor, cx).is_empty());
+//             assert_eq!(
+//                 editor.inlay_hint_cache().version, edits_made,
+//                 "The editor should not update the cache version after /refresh query without updates"
+//             );
+//         });
+
+//         let final_allowed_hint_kinds = HashSet::from_iter([Some(InlayHintKind::Parameter)]);
+//         edits_made += 1;
+//         update_test_language_settings(cx, |settings| {
+//             settings.defaults.inlay_hints = Some(InlayHintSettings {
+//                 enabled: true,
+//                 show_type_hints: final_allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
+//                 show_parameter_hints: final_allowed_hint_kinds
+//                     .contains(&Some(InlayHintKind::Parameter)),
+//                 show_other_hints: final_allowed_hint_kinds.contains(&None),
+//             })
+//         });
+//         cx.foreground().run_until_parked();
+//         editor.update(cx, |editor, cx| {
+//             assert_eq!(
+//                 lsp_request_count.load(Ordering::Relaxed),
+//                 3,
+//                 "Should query for new hints when they got reenabled"
+//             );
+//             assert_eq!(
+//                 vec![
+//                     "other hint".to_string(),
+//                     "parameter hint".to_string(),
+//                     "type hint".to_string(),
+//                 ],
+//                 cached_hint_labels(editor),
+//                 "Should get its cached hints fully repopulated after the hints got reenabled"
+//             );
+//             assert_eq!(
+//                 vec!["parameter hint".to_string()],
+//                 visible_hint_labels(editor, cx),
+//                 "Should get its visible hints repopulated and filtered after the h"
+//             );
+//             let inlay_cache = editor.inlay_hint_cache();
+//             assert_eq!(
+//                 inlay_cache.allowed_hint_kinds, final_allowed_hint_kinds,
+//                 "Cache should update editor settings when hints got reenabled"
+//             );
+//             assert_eq!(
+//                 inlay_cache.version, edits_made,
+//                 "Cache should update its version after hints got reenabled"
+//             );
+//         });
+
+//         fake_server
+//             .request::<lsp::request::InlayHintRefreshRequest>(())
+//             .await
+//             .expect("inlay refresh request failed");
+//         cx.foreground().run_until_parked();
+//         editor.update(cx, |editor, cx| {
+//             assert_eq!(
+//                 lsp_request_count.load(Ordering::Relaxed),
+//                 4,
+//                 "Should query for new hints again"
+//             );
+//             assert_eq!(
+//                 vec![
+//                     "other hint".to_string(),
+//                     "parameter hint".to_string(),
+//                     "type hint".to_string(),
+//                 ],
+//                 cached_hint_labels(editor),
+//             );
+//             assert_eq!(
+//                 vec!["parameter hint".to_string()],
+//                 visible_hint_labels(editor, cx),
+//             );
+//             assert_eq!(editor.inlay_hint_cache().version, edits_made);
+//         });
+//     }
+
+//     #[gpui::test]
+//     async fn test_hint_request_cancellation(cx: &mut gpui::TestAppContext) {
+//         init_test(cx, |settings| {
+//             settings.defaults.inlay_hints = Some(InlayHintSettings {
+//                 enabled: true,
+//                 show_type_hints: true,
+//                 show_parameter_hints: true,
+//                 show_other_hints: true,
+//             })
+//         });
+
+//         let (file_with_hints, editor, fake_server) = prepare_test_objects(cx).await;
+//         let fake_server = Arc::new(fake_server);
+//         let lsp_request_count = Arc::new(AtomicU32::new(0));
+//         let another_lsp_request_count = Arc::clone(&lsp_request_count);
+//         fake_server
+//             .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
+//                 let task_lsp_request_count = Arc::clone(&another_lsp_request_count);
+//                 async move {
+//                     let i = Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst) + 1;
+//                     assert_eq!(
+//                         params.text_document.uri,
+//                         lsp::Url::from_file_path(file_with_hints).unwrap(),
+//                     );
+//                     Ok(Some(vec![lsp::InlayHint {
+//                         position: lsp::Position::new(0, i),
+//                         label: lsp::InlayHintLabel::String(i.to_string()),
+//                         kind: None,
+//                         text_edits: None,
+//                         tooltip: None,
+//                         padding_left: None,
+//                         padding_right: None,
+//                         data: None,
+//                     }]))
+//                 }
+//             })
+//             .next()
+//             .await;
+
+//         let mut expected_changes = Vec::new();
+//         for change_after_opening in [
+//             "initial change #1",
+//             "initial change #2",
+//             "initial change #3",
+//         ] {
+//             editor.update(cx, |editor, cx| {
+//                 editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
+//                 editor.handle_input(change_after_opening, cx);
+//             });
+//             expected_changes.push(change_after_opening);
+//         }
+
+//         cx.foreground().run_until_parked();
+
+//         editor.update(cx, |editor, cx| {
+//             let current_text = editor.text(cx);
+//             for change in &expected_changes {
+//                 assert!(
+//                     current_text.contains(change),
+//                     "Should apply all changes made"
+//                 );
+//             }
+//             assert_eq!(
+//                 lsp_request_count.load(Ordering::Relaxed),
+//                 2,
+//                 "Should query new hints twice: for editor init and for the last edit that interrupted all others"
+//             );
+//             let expected_hints = vec!["2".to_string()];
+//             assert_eq!(
+//                 expected_hints,
+//                 cached_hint_labels(editor),
+//                 "Should get hints from the last edit landed only"
+//             );
+//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+//             assert_eq!(
+//                 editor.inlay_hint_cache().version, 1,
+//                 "Only one update should be registered in the cache after all cancellations"
+//             );
+//         });
+
+//         let mut edits = Vec::new();
+//         for async_later_change in [
+//             "another change #1",
+//             "another change #2",
+//             "another change #3",
+//         ] {
+//             expected_changes.push(async_later_change);
+//             let task_editor = editor.clone();
+//             let mut task_cx = cx.clone();
+//             edits.push(cx.foreground().spawn(async move {
+//                 task_editor.update(&mut task_cx, |editor, cx| {
+//                     editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
+//                     editor.handle_input(async_later_change, cx);
+//                 });
+//             }));
+//         }
+//         let _ = future::join_all(edits).await;
+//         cx.foreground().run_until_parked();
+
+//         editor.update(cx, |editor, cx| {
+//             let current_text = editor.text(cx);
+//             for change in &expected_changes {
+//                 assert!(
+//                     current_text.contains(change),
+//                     "Should apply all changes made"
+//                 );
+//             }
+//             assert_eq!(
+//                 lsp_request_count.load(Ordering::SeqCst),
+//                 3,
+//                 "Should query new hints one more time, for the last edit only"
+//             );
+//             let expected_hints = vec!["3".to_string()];
+//             assert_eq!(
+//                 expected_hints,
+//                 cached_hint_labels(editor),
+//                 "Should get hints from the last edit landed only"
+//             );
+//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+//             assert_eq!(
+//                 editor.inlay_hint_cache().version,
+//                 2,
+//                 "Should update the cache version once more, for the new change"
+//             );
+//         });
+//     }
+
+//     #[gpui::test(iterations = 10)]
+//     async fn test_large_buffer_inlay_requests_split(cx: &mut gpui::TestAppContext) {
+//         init_test(cx, |settings| {
+//             settings.defaults.inlay_hints = Some(InlayHintSettings {
+//                 enabled: true,
+//                 show_type_hints: true,
+//                 show_parameter_hints: true,
+//                 show_other_hints: true,
+//             })
+//         });
+
+//         let mut language = Language::new(
+//             LanguageConfig {
+//                 name: "Rust".into(),
+//                 path_suffixes: vec!["rs".to_string()],
+//                 ..Default::default()
+//             },
+//             Some(tree_sitter_rust::language()),
+//         );
+//         let mut fake_servers = language
+//             .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
+//                 capabilities: lsp::ServerCapabilities {
+//                     inlay_hint_provider: Some(lsp::OneOf::Left(true)),
+//                     ..Default::default()
+//                 },
+//                 ..Default::default()
+//             }))
+//             .await;
+//         let fs = FakeFs::new(cx.background());
+//         fs.insert_tree(
+//             "/a",
+//             json!({
+//                 "main.rs": format!("fn main() {{\n{}\n}}", "let i = 5;\n".repeat(500)),
+//                 "other.rs": "// Test file",
+//             }),
+//         )
+//         .await;
+//         let project = Project::test(fs, ["/a".as_ref()], cx).await;
+//         project.update(cx, |project, _| project.languages().add(Arc::new(language)));
+//         let workspace = cx
+//             .add_window(|cx| Workspace::test_new(project.clone(), cx))
+//             .root(cx);
+//         let worktree_id = workspace.update(cx, |workspace, cx| {
+//             workspace.project().read_with(cx, |project, cx| {
+//                 project.worktrees(cx).next().unwrap().read(cx).id()
+//             })
+//         });
+
+//         let _buffer = project
+//             .update(cx, |project, cx| {
+//                 project.open_local_buffer("/a/main.rs", cx)
+//             })
+//             .await
+//             .unwrap();
+//         cx.foreground().run_until_parked();
+//         cx.foreground().start_waiting();
+//         let fake_server = fake_servers.next().await.unwrap();
+//         let editor = workspace
+//             .update(cx, |workspace, cx| {
+//                 workspace.open_path((worktree_id, "main.rs"), None, true, cx)
+//             })
+//             .await
+//             .unwrap()
+//             .downcast::<Editor>()
+//             .unwrap();
+//         let lsp_request_ranges = Arc::new(Mutex::new(Vec::new()));
+//         let lsp_request_count = Arc::new(AtomicUsize::new(0));
+//         let closure_lsp_request_ranges = Arc::clone(&lsp_request_ranges);
+//         let closure_lsp_request_count = Arc::clone(&lsp_request_count);
+//         fake_server
+//             .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
+//                 let task_lsp_request_ranges = Arc::clone(&closure_lsp_request_ranges);
+//                 let task_lsp_request_count = Arc::clone(&closure_lsp_request_count);
+//                 async move {
+//                     assert_eq!(
+//                         params.text_document.uri,
+//                         lsp::Url::from_file_path("/a/main.rs").unwrap(),
+//                     );
+
+//                     task_lsp_request_ranges.lock().push(params.range);
+//                     let i = Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::Release) + 1;
+//                     Ok(Some(vec![lsp::InlayHint {
+//                         position: params.range.end,
+//                         label: lsp::InlayHintLabel::String(i.to_string()),
+//                         kind: None,
+//                         text_edits: None,
+//                         tooltip: None,
+//                         padding_left: None,
+//                         padding_right: None,
+//                         data: None,
+//                     }]))
+//                 }
+//             })
+//             .next()
+//             .await;
+//         fn editor_visible_range(
+//             editor: &ViewHandle<Editor>,
+//             cx: &mut gpui::TestAppContext,
+//         ) -> Range<Point> {
+//             let ranges = editor.update(cx, |editor, cx| editor.excerpt_visible_offsets(None, cx));
+//             assert_eq!(
+//                 ranges.len(),
+//                 1,
+//                 "Single buffer should produce a single excerpt with visible range"
+//             );
+//             let (_, (excerpt_buffer, _, excerpt_visible_range)) =
+//                 ranges.into_iter().next().unwrap();
+//             excerpt_buffer.update(cx, |buffer, _| {
+//                 let snapshot = buffer.snapshot();
+//                 let start = buffer
+//                     .anchor_before(excerpt_visible_range.start)
+//                     .to_point(&snapshot);
+//                 let end = buffer
+//                     .anchor_after(excerpt_visible_range.end)
+//                     .to_point(&snapshot);
+//                 start..end
+//             })
+//         }
+
+//         // in large buffers, requests are made for more than visible range of a buffer.
+//         // invisible parts are queried later, to avoid excessive requests on quick typing.
+//         // wait the timeout needed to get all requests.
+//         cx.foreground().advance_clock(Duration::from_millis(
+//             INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100,
+//         ));
+//         cx.foreground().run_until_parked();
+//         let initial_visible_range = editor_visible_range(&editor, cx);
+//         let lsp_initial_visible_range = lsp::Range::new(
+//             lsp::Position::new(
+//                 initial_visible_range.start.row,
+//                 initial_visible_range.start.column,
+//             ),
+//             lsp::Position::new(
+//                 initial_visible_range.end.row,
+//                 initial_visible_range.end.column,
+//             ),
+//         );
+//         let expected_initial_query_range_end =
+//             lsp::Position::new(initial_visible_range.end.row * 2, 2);
+//         let mut expected_invisible_query_start = lsp_initial_visible_range.end;
+//         expected_invisible_query_start.character += 1;
+//         editor.update(cx, |editor, cx| {
+//             let ranges = lsp_request_ranges.lock().drain(..).collect::<Vec<_>>();
+//             assert_eq!(ranges.len(), 2,
+//                 "When scroll is at the edge of a big document, its visible part and the same range further should be queried in order, but got: {ranges:?}");
+//             let visible_query_range = &ranges[0];
+//             assert_eq!(visible_query_range.start, lsp_initial_visible_range.start);
+//             assert_eq!(visible_query_range.end, lsp_initial_visible_range.end);
+//             let invisible_query_range = &ranges[1];
+
+//             assert_eq!(invisible_query_range.start, expected_invisible_query_start, "Should initially query visible edge of the document");
+//             assert_eq!(invisible_query_range.end, expected_initial_query_range_end, "Should initially query visible edge of the document");
+
+//             let requests_count = lsp_request_count.load(Ordering::Acquire);
+//             assert_eq!(requests_count, 2, "Visible + invisible request");
+//             let expected_hints = vec!["1".to_string(), "2".to_string()];
+//             assert_eq!(
+//                 expected_hints,
+//                 cached_hint_labels(editor),
+//                 "Should have hints from both LSP requests made for a big file"
+//             );
+//             assert_eq!(expected_hints, visible_hint_labels(editor, cx), "Should display only hints from the visible range");
+//             assert_eq!(
+//                 editor.inlay_hint_cache().version, requests_count,
+//                 "LSP queries should've bumped the cache version"
+//             );
+//         });
+
+//         editor.update(cx, |editor, cx| {
+//             editor.scroll_screen(&ScrollAmount::Page(1.0), cx);
+//             editor.scroll_screen(&ScrollAmount::Page(1.0), cx);
+//         });
+//         cx.foreground().advance_clock(Duration::from_millis(
+//             INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100,
+//         ));
+//         cx.foreground().run_until_parked();
+//         let visible_range_after_scrolls = editor_visible_range(&editor, cx);
+//         let visible_line_count =
+//             editor.update(cx, |editor, _| editor.visible_line_count().unwrap());
+//         let selection_in_cached_range = editor.update(cx, |editor, cx| {
+//             let ranges = lsp_request_ranges
+//                 .lock()
+//                 .drain(..)
+//                 .sorted_by_key(|r| r.start)
+//                 .collect::<Vec<_>>();
+//             assert_eq!(
+//                 ranges.len(),
+//                 2,
+//                 "Should query 2 ranges after both scrolls, but got: {ranges:?}"
+//             );
+//             let first_scroll = &ranges[0];
+//             let second_scroll = &ranges[1];
+//             assert_eq!(
+//                 first_scroll.end, second_scroll.start,
+//                 "Should query 2 adjacent ranges after the scrolls, but got: {ranges:?}"
+//             );
+//             assert_eq!(
+//                 first_scroll.start, expected_initial_query_range_end,
+//                 "First scroll should start the query right after the end of the original scroll",
+//             );
+//             assert_eq!(
+//                 second_scroll.end,
+//                 lsp::Position::new(
+//                     visible_range_after_scrolls.end.row
+//                         + visible_line_count.ceil() as u32,
+//                     1,
+//                 ),
+//                 "Second scroll should query one more screen down after the end of the visible range"
+//             );
+
+//             let lsp_requests = lsp_request_count.load(Ordering::Acquire);
+//             assert_eq!(lsp_requests, 4, "Should query for hints after every scroll");
+//             let expected_hints = vec![
+//                 "1".to_string(),
+//                 "2".to_string(),
+//                 "3".to_string(),
+//                 "4".to_string(),
+//             ];
+//             assert_eq!(
+//                 expected_hints,
+//                 cached_hint_labels(editor),
+//                 "Should have hints from the new LSP response after the edit"
+//             );
+//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+//             assert_eq!(
+//                 editor.inlay_hint_cache().version,
+//                 lsp_requests,
+//                 "Should update the cache for every LSP response with hints added"
+//             );
+
+//             let mut selection_in_cached_range = visible_range_after_scrolls.end;
+//             selection_in_cached_range.row -= visible_line_count.ceil() as u32;
+//             selection_in_cached_range
+//         });
+
+//         editor.update(cx, |editor, cx| {
+//             editor.change_selections(Some(Autoscroll::center()), cx, |s| {
+//                 s.select_ranges([selection_in_cached_range..selection_in_cached_range])
+//             });
+//         });
+//         cx.foreground().advance_clock(Duration::from_millis(
+//             INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100,
+//         ));
+//         cx.foreground().run_until_parked();
+//         editor.update(cx, |_, _| {
+//             let ranges = lsp_request_ranges
+//                 .lock()
+//                 .drain(..)
+//                 .sorted_by_key(|r| r.start)
+//                 .collect::<Vec<_>>();
+//             assert!(ranges.is_empty(), "No new ranges or LSP queries should be made after returning to the selection with cached hints");
+//             assert_eq!(lsp_request_count.load(Ordering::Acquire), 4);
+//         });
+
+//         editor.update(cx, |editor, cx| {
+//             editor.handle_input("++++more text++++", cx);
+//         });
+//         cx.foreground().advance_clock(Duration::from_millis(
+//             INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100,
+//         ));
+//         cx.foreground().run_until_parked();
+//         editor.update(cx, |editor, cx| {
+//             let mut ranges = lsp_request_ranges.lock().drain(..).collect::<Vec<_>>();
+//             ranges.sort_by_key(|r| r.start);
+
+//             assert_eq!(ranges.len(), 3,
+//                 "On edit, should scroll to selection and query a range around it: visible + same range above and below. Instead, got query ranges {ranges:?}");
+//             let above_query_range = &ranges[0];
+//             let visible_query_range = &ranges[1];
+//             let below_query_range = &ranges[2];
+//             assert!(above_query_range.end.character < visible_query_range.start.character || above_query_range.end.line + 1 == visible_query_range.start.line,
+//                 "Above range {above_query_range:?} should be before visible range {visible_query_range:?}");
+//             assert!(visible_query_range.end.character < below_query_range.start.character || visible_query_range.end.line  + 1 == below_query_range.start.line,
+//                 "Visible range {visible_query_range:?} should be before below range {below_query_range:?}");
+//             assert!(above_query_range.start.line < selection_in_cached_range.row,
+//                 "Hints should be queried with the selected range after the query range start");
+//             assert!(below_query_range.end.line > selection_in_cached_range.row,
+//                 "Hints should be queried with the selected range before the query range end");
+//             assert!(above_query_range.start.line <= selection_in_cached_range.row - (visible_line_count * 3.0 / 2.0) as u32,
+//                 "Hints query range should contain one more screen before");
+//             assert!(below_query_range.end.line >= selection_in_cached_range.row + (visible_line_count * 3.0 / 2.0) as u32,
+//                 "Hints query range should contain one more screen after");
+
+//             let lsp_requests = lsp_request_count.load(Ordering::Acquire);
+//             assert_eq!(lsp_requests, 7, "There should be a visible range and two ranges above and below it queried");
+//             let expected_hints = vec!["5".to_string(), "6".to_string(), "7".to_string()];
+//             assert_eq!(expected_hints, cached_hint_labels(editor),
+//                 "Should have hints from the new LSP response after the edit");
+//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+//             assert_eq!(editor.inlay_hint_cache().version, lsp_requests, "Should update the cache for every LSP response with hints added");
+//         });
+//     }
+
+//     #[gpui::test(iterations = 10)]
+//     async fn test_multiple_excerpts_large_multibuffer(
+//         deterministic: Arc<Deterministic>,
+//         cx: &mut gpui::TestAppContext,
+//     ) {
+//         init_test(cx, |settings| {
+//             settings.defaults.inlay_hints = Some(InlayHintSettings {
+//                 enabled: true,
+//                 show_type_hints: true,
+//                 show_parameter_hints: true,
+//                 show_other_hints: true,
+//             })
+//         });
+
+//         let mut language = Language::new(
+//             LanguageConfig {
+//                 name: "Rust".into(),
+//                 path_suffixes: vec!["rs".to_string()],
+//                 ..Default::default()
+//             },
+//             Some(tree_sitter_rust::language()),
+//         );
+//         let mut fake_servers = language
+//             .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
+//                 capabilities: lsp::ServerCapabilities {
+//                     inlay_hint_provider: Some(lsp::OneOf::Left(true)),
+//                     ..Default::default()
+//                 },
+//                 ..Default::default()
+//             }))
+//             .await;
+//         let language = Arc::new(language);
+//         let fs = FakeFs::new(cx.background());
+//         fs.insert_tree(
+//             "/a",
+//             json!({
+//                 "main.rs": format!("fn main() {{\n{}\n}}", (0..501).map(|i| format!("let i = {i};\n")).collect::<Vec<_>>().join("")),
+//                 "other.rs": format!("fn main() {{\n{}\n}}", (0..501).map(|j| format!("let j = {j};\n")).collect::<Vec<_>>().join("")),
+//             }),
+//         )
+//         .await;
+//         let project = Project::test(fs, ["/a".as_ref()], cx).await;
+//         project.update(cx, |project, _| {
+//             project.languages().add(Arc::clone(&language))
+//         });
+//         let workspace = cx
+//             .add_window(|cx| Workspace::test_new(project.clone(), cx))
+//             .root(cx);
+//         let worktree_id = workspace.update(cx, |workspace, cx| {
+//             workspace.project().read_with(cx, |project, cx| {
+//                 project.worktrees(cx).next().unwrap().read(cx).id()
+//             })
+//         });
+
+//         let buffer_1 = project
+//             .update(cx, |project, cx| {
+//                 project.open_buffer((worktree_id, "main.rs"), cx)
+//             })
+//             .await
+//             .unwrap();
+//         let buffer_2 = project
+//             .update(cx, |project, cx| {
+//                 project.open_buffer((worktree_id, "other.rs"), cx)
+//             })
+//             .await
+//             .unwrap();
+//         let multibuffer = cx.add_model(|cx| {
+//             let mut multibuffer = MultiBuffer::new(0);
+//             multibuffer.push_excerpts(
+//                 buffer_1.clone(),
+//                 [
+//                     ExcerptRange {
+//                         context: Point::new(0, 0)..Point::new(2, 0),
+//                         primary: None,
+//                     },
+//                     ExcerptRange {
+//                         context: Point::new(4, 0)..Point::new(11, 0),
+//                         primary: None,
+//                     },
+//                     ExcerptRange {
+//                         context: Point::new(22, 0)..Point::new(33, 0),
+//                         primary: None,
+//                     },
+//                     ExcerptRange {
+//                         context: Point::new(44, 0)..Point::new(55, 0),
+//                         primary: None,
+//                     },
+//                     ExcerptRange {
+//                         context: Point::new(56, 0)..Point::new(66, 0),
+//                         primary: None,
+//                     },
+//                     ExcerptRange {
+//                         context: Point::new(67, 0)..Point::new(77, 0),
+//                         primary: None,
+//                     },
+//                 ],
+//                 cx,
+//             );
+//             multibuffer.push_excerpts(
+//                 buffer_2.clone(),
+//                 [
+//                     ExcerptRange {
+//                         context: Point::new(0, 1)..Point::new(2, 1),
+//                         primary: None,
+//                     },
+//                     ExcerptRange {
+//                         context: Point::new(4, 1)..Point::new(11, 1),
+//                         primary: None,
+//                     },
+//                     ExcerptRange {
+//                         context: Point::new(22, 1)..Point::new(33, 1),
+//                         primary: None,
+//                     },
+//                     ExcerptRange {
+//                         context: Point::new(44, 1)..Point::new(55, 1),
+//                         primary: None,
+//                     },
+//                     ExcerptRange {
+//                         context: Point::new(56, 1)..Point::new(66, 1),
+//                         primary: None,
+//                     },
+//                     ExcerptRange {
+//                         context: Point::new(67, 1)..Point::new(77, 1),
+//                         primary: None,
+//                     },
+//                 ],
+//                 cx,
+//             );
+//             multibuffer
+//         });
+
+//         deterministic.run_until_parked();
+//         cx.foreground().run_until_parked();
+//         let editor = cx
+//             .add_window(|cx| Editor::for_multibuffer(multibuffer, Some(project.clone()), cx))
+//             .root(cx);
+//         let editor_edited = Arc::new(AtomicBool::new(false));
+//         let fake_server = fake_servers.next().await.unwrap();
+//         let closure_editor_edited = Arc::clone(&editor_edited);
+//         fake_server
+//             .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
+//                 let task_editor_edited = Arc::clone(&closure_editor_edited);
+//                 async move {
+//                     let hint_text = if params.text_document.uri
+//                         == lsp::Url::from_file_path("/a/main.rs").unwrap()
+//                     {
+//                         "main hint"
+//                     } else if params.text_document.uri
+//                         == lsp::Url::from_file_path("/a/other.rs").unwrap()
+//                     {
+//                         "other hint"
+//                     } else {
+//                         panic!("unexpected uri: {:?}", params.text_document.uri);
+//                     };
+
+//                     // one hint per excerpt
+//                     let positions = [
+//                         lsp::Position::new(0, 2),
+//                         lsp::Position::new(4, 2),
+//                         lsp::Position::new(22, 2),
+//                         lsp::Position::new(44, 2),
+//                         lsp::Position::new(56, 2),
+//                         lsp::Position::new(67, 2),
+//                     ];
+//                     let out_of_range_hint = lsp::InlayHint {
+//                         position: lsp::Position::new(
+//                             params.range.start.line + 99,
+//                             params.range.start.character + 99,
+//                         ),
+//                         label: lsp::InlayHintLabel::String(
+//                             "out of excerpt range, should be ignored".to_string(),
+//                         ),
+//                         kind: None,
+//                         text_edits: None,
+//                         tooltip: None,
+//                         padding_left: None,
+//                         padding_right: None,
+//                         data: None,
+//                     };
+
+//                     let edited = task_editor_edited.load(Ordering::Acquire);
+//                     Ok(Some(
+//                         std::iter::once(out_of_range_hint)
+//                             .chain(positions.into_iter().enumerate().map(|(i, position)| {
+//                                 lsp::InlayHint {
+//                                     position,
+//                                     label: lsp::InlayHintLabel::String(format!(
+//                                         "{hint_text}{} #{i}",
+//                                         if edited { "(edited)" } else { "" },
+//                                     )),
+//                                     kind: None,
+//                                     text_edits: None,
+//                                     tooltip: None,
+//                                     padding_left: None,
+//                                     padding_right: None,
+//                                     data: None,
+//                                 }
+//                             }))
+//                             .collect(),
+//                     ))
+//                 }
+//             })
+//             .next()
+//             .await;
+//         cx.foreground().run_until_parked();
+
+//         editor.update(cx, |editor, cx| {
+//             let expected_hints = vec![
+//                 "main hint #0".to_string(),
+//                 "main hint #1".to_string(),
+//                 "main hint #2".to_string(),
+//                 "main hint #3".to_string(),
+//             ];
+//             assert_eq!(
+//                 expected_hints,
+//                 cached_hint_labels(editor),
+//                 "When scroll is at the edge of a multibuffer, its visible excerpts only should be queried for inlay hints"
+//             );
+//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+//             assert_eq!(editor.inlay_hint_cache().version, expected_hints.len(), "Every visible excerpt hints should bump the verison");
+//         });
+
+//         editor.update(cx, |editor, cx| {
+//             editor.change_selections(Some(Autoscroll::Next), cx, |s| {
+//                 s.select_ranges([Point::new(4, 0)..Point::new(4, 0)])
+//             });
+//             editor.change_selections(Some(Autoscroll::Next), cx, |s| {
+//                 s.select_ranges([Point::new(22, 0)..Point::new(22, 0)])
+//             });
+//             editor.change_selections(Some(Autoscroll::Next), cx, |s| {
+//                 s.select_ranges([Point::new(50, 0)..Point::new(50, 0)])
+//             });
+//         });
+//         cx.foreground().run_until_parked();
+//         editor.update(cx, |editor, cx| {
+//             let expected_hints = vec![
+//                 "main hint #0".to_string(),
+//                 "main hint #1".to_string(),
+//                 "main hint #2".to_string(),
+//                 "main hint #3".to_string(),
+//                 "main hint #4".to_string(),
+//                 "main hint #5".to_string(),
+//                 "other hint #0".to_string(),
+//                 "other hint #1".to_string(),
+//                 "other hint #2".to_string(),
+//             ];
+//             assert_eq!(expected_hints, cached_hint_labels(editor),
+//                 "With more scrolls of the multibuffer, more hints should be added into the cache and nothing invalidated without edits");
+//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+//             assert_eq!(editor.inlay_hint_cache().version, expected_hints.len(),
+//                 "Due to every excerpt having one hint, we update cache per new excerpt scrolled");
+//         });
+
+//         editor.update(cx, |editor, cx| {
+//             editor.change_selections(Some(Autoscroll::Next), cx, |s| {
+//                 s.select_ranges([Point::new(100, 0)..Point::new(100, 0)])
+//             });
+//         });
+//         cx.foreground().advance_clock(Duration::from_millis(
+//             INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100,
+//         ));
+//         cx.foreground().run_until_parked();
+//         let last_scroll_update_version = editor.update(cx, |editor, cx| {
+//             let expected_hints = vec![
+//                 "main hint #0".to_string(),
+//                 "main hint #1".to_string(),
+//                 "main hint #2".to_string(),
+//                 "main hint #3".to_string(),
+//                 "main hint #4".to_string(),
+//                 "main hint #5".to_string(),
+//                 "other hint #0".to_string(),
+//                 "other hint #1".to_string(),
+//                 "other hint #2".to_string(),
+//                 "other hint #3".to_string(),
+//                 "other hint #4".to_string(),
+//                 "other hint #5".to_string(),
+//             ];
+//             assert_eq!(expected_hints, cached_hint_labels(editor),
+//                 "After multibuffer was scrolled to the end, all hints for all excerpts should be fetched");
+//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+//             assert_eq!(editor.inlay_hint_cache().version, expected_hints.len());
+//             expected_hints.len()
+//         });
+
+//         editor.update(cx, |editor, cx| {
+//             editor.change_selections(Some(Autoscroll::Next), cx, |s| {
+//                 s.select_ranges([Point::new(4, 0)..Point::new(4, 0)])
+//             });
+//         });
+//         cx.foreground().run_until_parked();
+//         editor.update(cx, |editor, cx| {
+//             let expected_hints = vec![
+//                 "main hint #0".to_string(),
+//                 "main hint #1".to_string(),
+//                 "main hint #2".to_string(),
+//                 "main hint #3".to_string(),
+//                 "main hint #4".to_string(),
+//                 "main hint #5".to_string(),
+//                 "other hint #0".to_string(),
+//                 "other hint #1".to_string(),
+//                 "other hint #2".to_string(),
+//                 "other hint #3".to_string(),
+//                 "other hint #4".to_string(),
+//                 "other hint #5".to_string(),
+//             ];
+//             assert_eq!(expected_hints, cached_hint_labels(editor),
+//                 "After multibuffer was scrolled to the end, further scrolls up should not bring more hints");
+//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+//             assert_eq!(editor.inlay_hint_cache().version, last_scroll_update_version, "No updates should happen during scrolling already scolled buffer");
+//         });
+
+//         editor_edited.store(true, Ordering::Release);
+//         editor.update(cx, |editor, cx| {
+//             editor.change_selections(None, cx, |s| {
+//                 s.select_ranges([Point::new(56, 0)..Point::new(56, 0)])
+//             });
+//             editor.handle_input("++++more text++++", cx);
+//         });
+//         cx.foreground().run_until_parked();
+//         editor.update(cx, |editor, cx| {
+//             let expected_hints = vec![
+//                 "main hint(edited) #0".to_string(),
+//                 "main hint(edited) #1".to_string(),
+//                 "main hint(edited) #2".to_string(),
+//                 "main hint(edited) #3".to_string(),
+//                 "main hint(edited) #4".to_string(),
+//                 "main hint(edited) #5".to_string(),
+//                 "other hint(edited) #0".to_string(),
+//                 "other hint(edited) #1".to_string(),
+//             ];
+//             assert_eq!(
+//                 expected_hints,
+//                 cached_hint_labels(editor),
+//                 "After multibuffer edit, editor gets scolled back to the last selection; \
+// all hints should be invalidated and requeried for all of its visible excerpts"
+//             );
+//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+
+//             let current_cache_version = editor.inlay_hint_cache().version;
+//             let minimum_expected_version = last_scroll_update_version + expected_hints.len();
+//             assert!(
+//                 current_cache_version == minimum_expected_version || current_cache_version == minimum_expected_version + 1,
+//                 "Due to every excerpt having one hint, cache should update per new excerpt received + 1 potential sporadic update"
+//             );
+//         });
+//     }
+
+//     #[gpui::test]
+//     async fn test_excerpts_removed(
+//         deterministic: Arc<Deterministic>,
+//         cx: &mut gpui::TestAppContext,
+//     ) {
+//         init_test(cx, |settings| {
+//             settings.defaults.inlay_hints = Some(InlayHintSettings {
+//                 enabled: true,
+//                 show_type_hints: false,
+//                 show_parameter_hints: false,
+//                 show_other_hints: false,
+//             })
+//         });
+
+//         let mut language = Language::new(
+//             LanguageConfig {
+//                 name: "Rust".into(),
+//                 path_suffixes: vec!["rs".to_string()],
+//                 ..Default::default()
+//             },
+//             Some(tree_sitter_rust::language()),
+//         );
+//         let mut fake_servers = language
+//             .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
+//                 capabilities: lsp::ServerCapabilities {
+//                     inlay_hint_provider: Some(lsp::OneOf::Left(true)),
+//                     ..Default::default()
+//                 },
+//                 ..Default::default()
+//             }))
+//             .await;
+//         let language = Arc::new(language);
+//         let fs = FakeFs::new(cx.background());
+//         fs.insert_tree(
+//             "/a",
+//             json!({
+//                 "main.rs": format!("fn main() {{\n{}\n}}", (0..501).map(|i| format!("let i = {i};\n")).collect::<Vec<_>>().join("")),
+//                 "other.rs": format!("fn main() {{\n{}\n}}", (0..501).map(|j| format!("let j = {j};\n")).collect::<Vec<_>>().join("")),
+//             }),
+//         )
+//         .await;
+//         let project = Project::test(fs, ["/a".as_ref()], cx).await;
+//         project.update(cx, |project, _| {
+//             project.languages().add(Arc::clone(&language))
+//         });
+//         let workspace = cx
+//             .add_window(|cx| Workspace::test_new(project.clone(), cx))
+//             .root(cx);
+//         let worktree_id = workspace.update(cx, |workspace, cx| {
+//             workspace.project().read_with(cx, |project, cx| {
+//                 project.worktrees(cx).next().unwrap().read(cx).id()
+//             })
+//         });
+
+//         let buffer_1 = project
+//             .update(cx, |project, cx| {
+//                 project.open_buffer((worktree_id, "main.rs"), cx)
+//             })
+//             .await
+//             .unwrap();
+//         let buffer_2 = project
+//             .update(cx, |project, cx| {
+//                 project.open_buffer((worktree_id, "other.rs"), cx)
+//             })
+//             .await
+//             .unwrap();
+//         let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
+//         let (buffer_1_excerpts, buffer_2_excerpts) = multibuffer.update(cx, |multibuffer, cx| {
+//             let buffer_1_excerpts = multibuffer.push_excerpts(
+//                 buffer_1.clone(),
+//                 [ExcerptRange {
+//                     context: Point::new(0, 0)..Point::new(2, 0),
+//                     primary: None,
+//                 }],
+//                 cx,
+//             );
+//             let buffer_2_excerpts = multibuffer.push_excerpts(
+//                 buffer_2.clone(),
+//                 [ExcerptRange {
+//                     context: Point::new(0, 1)..Point::new(2, 1),
+//                     primary: None,
+//                 }],
+//                 cx,
+//             );
+//             (buffer_1_excerpts, buffer_2_excerpts)
+//         });
+
+//         assert!(!buffer_1_excerpts.is_empty());
+//         assert!(!buffer_2_excerpts.is_empty());
+
+//         deterministic.run_until_parked();
+//         cx.foreground().run_until_parked();
+//         let editor = cx
+//             .add_window(|cx| Editor::for_multibuffer(multibuffer, Some(project.clone()), cx))
+//             .root(cx);
+//         let editor_edited = Arc::new(AtomicBool::new(false));
+//         let fake_server = fake_servers.next().await.unwrap();
+//         let closure_editor_edited = Arc::clone(&editor_edited);
+//         fake_server
+//             .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
+//                 let task_editor_edited = Arc::clone(&closure_editor_edited);
+//                 async move {
+//                     let hint_text = if params.text_document.uri
+//                         == lsp::Url::from_file_path("/a/main.rs").unwrap()
+//                     {
+//                         "main hint"
+//                     } else if params.text_document.uri
+//                         == lsp::Url::from_file_path("/a/other.rs").unwrap()
+//                     {
+//                         "other hint"
+//                     } else {
+//                         panic!("unexpected uri: {:?}", params.text_document.uri);
+//                     };
+
+//                     let positions = [
+//                         lsp::Position::new(0, 2),
+//                         lsp::Position::new(4, 2),
+//                         lsp::Position::new(22, 2),
+//                         lsp::Position::new(44, 2),
+//                         lsp::Position::new(56, 2),
+//                         lsp::Position::new(67, 2),
+//                     ];
+//                     let out_of_range_hint = lsp::InlayHint {
+//                         position: lsp::Position::new(
+//                             params.range.start.line + 99,
+//                             params.range.start.character + 99,
+//                         ),
+//                         label: lsp::InlayHintLabel::String(
+//                             "out of excerpt range, should be ignored".to_string(),
+//                         ),
+//                         kind: None,
+//                         text_edits: None,
+//                         tooltip: None,
+//                         padding_left: None,
+//                         padding_right: None,
+//                         data: None,
+//                     };
+
+//                     let edited = task_editor_edited.load(Ordering::Acquire);
+//                     Ok(Some(
+//                         std::iter::once(out_of_range_hint)
+//                             .chain(positions.into_iter().enumerate().map(|(i, position)| {
+//                                 lsp::InlayHint {
+//                                     position,
+//                                     label: lsp::InlayHintLabel::String(format!(
+//                                         "{hint_text}{} #{i}",
+//                                         if edited { "(edited)" } else { "" },
+//                                     )),
+//                                     kind: None,
+//                                     text_edits: None,
+//                                     tooltip: None,
+//                                     padding_left: None,
+//                                     padding_right: None,
+//                                     data: None,
+//                                 }
+//                             }))
+//                             .collect(),
+//                     ))
+//                 }
+//             })
+//             .next()
+//             .await;
+//         cx.foreground().run_until_parked();
+
+//         editor.update(cx, |editor, cx| {
+//             assert_eq!(
+//                 vec!["main hint #0".to_string(), "other hint #0".to_string()],
+//                 cached_hint_labels(editor),
+//                 "Cache should update for both excerpts despite hints display was disabled"
+//             );
+//             assert!(
+//                 visible_hint_labels(editor, cx).is_empty(),
+//                 "All hints are disabled and should not be shown despite being present in the cache"
+//             );
+//             assert_eq!(
+//                 editor.inlay_hint_cache().version,
+//                 2,
+//                 "Cache should update once per excerpt query"
+//             );
+//         });
+
+//         editor.update(cx, |editor, cx| {
+//             editor.buffer().update(cx, |multibuffer, cx| {
+//                 multibuffer.remove_excerpts(buffer_2_excerpts, cx)
+//             })
+//         });
+//         cx.foreground().run_until_parked();
+//         editor.update(cx, |editor, cx| {
+//             assert_eq!(
+//                 vec!["main hint #0".to_string()],
+//                 cached_hint_labels(editor),
+//                 "For the removed excerpt, should clean corresponding cached hints"
+//             );
+//             assert!(
+//                 visible_hint_labels(editor, cx).is_empty(),
+//                 "All hints are disabled and should not be shown despite being present in the cache"
+//             );
+//             assert_eq!(
+//                 editor.inlay_hint_cache().version,
+//                 3,
+//                 "Excerpt removal should trigger a cache update"
+//             );
+//         });
+
+//         update_test_language_settings(cx, |settings| {
+//             settings.defaults.inlay_hints = Some(InlayHintSettings {
+//                 enabled: true,
+//                 show_type_hints: true,
+//                 show_parameter_hints: true,
+//                 show_other_hints: true,
+//             })
+//         });
+//         cx.foreground().run_until_parked();
+//         editor.update(cx, |editor, cx| {
+//             let expected_hints = vec!["main hint #0".to_string()];
+//             assert_eq!(
+//                 expected_hints,
+//                 cached_hint_labels(editor),
+//                 "Hint display settings change should not change the cache"
+//             );
+//             assert_eq!(
+//                 expected_hints,
+//                 visible_hint_labels(editor, cx),
+//                 "Settings change should make cached hints visible"
+//             );
+//             assert_eq!(
+//                 editor.inlay_hint_cache().version,
+//                 4,
+//                 "Settings change should trigger a cache update"
+//             );
+//         });
+//     }
+
+//     #[gpui::test]
+//     async fn test_inside_char_boundary_range_hints(cx: &mut gpui::TestAppContext) {
+//         init_test(cx, |settings| {
+//             settings.defaults.inlay_hints = Some(InlayHintSettings {
+//                 enabled: true,
+//                 show_type_hints: true,
+//                 show_parameter_hints: true,
+//                 show_other_hints: true,
+//             })
+//         });
+
+//         let mut language = Language::new(
+//             LanguageConfig {
+//                 name: "Rust".into(),
+//                 path_suffixes: vec!["rs".to_string()],
+//                 ..Default::default()
+//             },
+//             Some(tree_sitter_rust::language()),
+//         );
+//         let mut fake_servers = language
+//             .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
+//                 capabilities: lsp::ServerCapabilities {
+//                     inlay_hint_provider: Some(lsp::OneOf::Left(true)),
+//                     ..Default::default()
+//                 },
+//                 ..Default::default()
+//             }))
+//             .await;
+//         let fs = FakeFs::new(cx.background());
+//         fs.insert_tree(
+//             "/a",
+//             json!({
+//                 "main.rs": format!(r#"fn main() {{\n{}\n}}"#, format!("let i = {};\n", "√".repeat(10)).repeat(500)),
+//                 "other.rs": "// Test file",
+//             }),
+//         )
+//         .await;
+//         let project = Project::test(fs, ["/a".as_ref()], cx).await;
+//         project.update(cx, |project, _| project.languages().add(Arc::new(language)));
+//         let workspace = cx
+//             .add_window(|cx| Workspace::test_new(project.clone(), cx))
+//             .root(cx);
+//         let worktree_id = workspace.update(cx, |workspace, cx| {
+//             workspace.project().read_with(cx, |project, cx| {
+//                 project.worktrees(cx).next().unwrap().read(cx).id()
+//             })
+//         });
+
+//         let _buffer = project
+//             .update(cx, |project, cx| {
+//                 project.open_local_buffer("/a/main.rs", cx)
+//             })
+//             .await
+//             .unwrap();
+//         cx.foreground().run_until_parked();
+//         cx.foreground().start_waiting();
+//         let fake_server = fake_servers.next().await.unwrap();
+//         let editor = workspace
+//             .update(cx, |workspace, cx| {
+//                 workspace.open_path((worktree_id, "main.rs"), None, true, cx)
+//             })
+//             .await
+//             .unwrap()
+//             .downcast::<Editor>()
+//             .unwrap();
+//         let lsp_request_count = Arc::new(AtomicU32::new(0));
+//         let closure_lsp_request_count = Arc::clone(&lsp_request_count);
+//         fake_server
+//             .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
+//                 let task_lsp_request_count = Arc::clone(&closure_lsp_request_count);
+//                 async move {
+//                     assert_eq!(
+//                         params.text_document.uri,
+//                         lsp::Url::from_file_path("/a/main.rs").unwrap(),
+//                     );
+//                     let query_start = params.range.start;
+//                     let i = Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::Release) + 1;
+//                     Ok(Some(vec![lsp::InlayHint {
+//                         position: query_start,
+//                         label: lsp::InlayHintLabel::String(i.to_string()),
+//                         kind: None,
+//                         text_edits: None,
+//                         tooltip: None,
+//                         padding_left: None,
+//                         padding_right: None,
+//                         data: None,
+//                     }]))
+//                 }
+//             })
+//             .next()
+//             .await;
+
+//         cx.foreground().run_until_parked();
+//         editor.update(cx, |editor, cx| {
+//             editor.change_selections(None, cx, |s| {
+//                 s.select_ranges([Point::new(10, 0)..Point::new(10, 0)])
+//             })
+//         });
+//         cx.foreground().run_until_parked();
+//         editor.update(cx, |editor, cx| {
+//             let expected_hints = vec!["1".to_string()];
+//             assert_eq!(expected_hints, cached_hint_labels(editor));
+//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+//             assert_eq!(editor.inlay_hint_cache().version, 1);
+//         });
+//     }
+
+//     #[gpui::test]
+//     async fn test_toggle_inlay_hints(cx: &mut gpui::TestAppContext) {
+//         init_test(cx, |settings| {
+//             settings.defaults.inlay_hints = Some(InlayHintSettings {
+//                 enabled: false,
+//                 show_type_hints: true,
+//                 show_parameter_hints: true,
+//                 show_other_hints: true,
+//             })
+//         });
+
+//         let (file_with_hints, editor, fake_server) = prepare_test_objects(cx).await;
+
+//         editor.update(cx, |editor, cx| {
+//             editor.toggle_inlay_hints(&crate::ToggleInlayHints, cx)
+//         });
+//         cx.foreground().start_waiting();
+//         let lsp_request_count = Arc::new(AtomicU32::new(0));
+//         let closure_lsp_request_count = Arc::clone(&lsp_request_count);
+//         fake_server
+//             .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
+//                 let task_lsp_request_count = Arc::clone(&closure_lsp_request_count);
+//                 async move {
+//                     assert_eq!(
+//                         params.text_document.uri,
+//                         lsp::Url::from_file_path(file_with_hints).unwrap(),
+//                     );
+
+//                     let i = Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst) + 1;
+//                     Ok(Some(vec![lsp::InlayHint {
+//                         position: lsp::Position::new(0, i),
+//                         label: lsp::InlayHintLabel::String(i.to_string()),
+//                         kind: None,
+//                         text_edits: None,
+//                         tooltip: None,
+//                         padding_left: None,
+//                         padding_right: None,
+//                         data: None,
+//                     }]))
+//                 }
+//             })
+//             .next()
+//             .await;
+//         cx.foreground().run_until_parked();
+//         editor.update(cx, |editor, cx| {
+//             let expected_hints = vec!["1".to_string()];
+//             assert_eq!(
+//                 expected_hints,
+//                 cached_hint_labels(editor),
+//                 "Should display inlays after toggle despite them disabled in settings"
+//             );
+//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+//             assert_eq!(
+//                 editor.inlay_hint_cache().version,
+//                 1,
+//                 "First toggle should be cache's first update"
+//             );
+//         });
+
+//         editor.update(cx, |editor, cx| {
+//             editor.toggle_inlay_hints(&crate::ToggleInlayHints, cx)
+//         });
+//         cx.foreground().run_until_parked();
+//         editor.update(cx, |editor, cx| {
+//             assert!(
+//                 cached_hint_labels(editor).is_empty(),
+//                 "Should clear hints after 2nd toggle"
+//             );
+//             assert!(visible_hint_labels(editor, cx).is_empty());
+//             assert_eq!(editor.inlay_hint_cache().version, 2);
+//         });
+
+//         update_test_language_settings(cx, |settings| {
+//             settings.defaults.inlay_hints = Some(InlayHintSettings {
+//                 enabled: true,
+//                 show_type_hints: true,
+//                 show_parameter_hints: true,
+//                 show_other_hints: true,
+//             })
+//         });
+//         cx.foreground().run_until_parked();
+//         editor.update(cx, |editor, cx| {
+//             let expected_hints = vec!["2".to_string()];
+//             assert_eq!(
+//                 expected_hints,
+//                 cached_hint_labels(editor),
+//                 "Should query LSP hints for the 2nd time after enabling hints in settings"
+//             );
+//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+//             assert_eq!(editor.inlay_hint_cache().version, 3);
+//         });
+
+//         editor.update(cx, |editor, cx| {
+//             editor.toggle_inlay_hints(&crate::ToggleInlayHints, cx)
+//         });
+//         cx.foreground().run_until_parked();
+//         editor.update(cx, |editor, cx| {
+//             assert!(
+//                 cached_hint_labels(editor).is_empty(),
+//                 "Should clear hints after enabling in settings and a 3rd toggle"
+//             );
+//             assert!(visible_hint_labels(editor, cx).is_empty());
+//             assert_eq!(editor.inlay_hint_cache().version, 4);
+//         });
+
+//         editor.update(cx, |editor, cx| {
+//             editor.toggle_inlay_hints(&crate::ToggleInlayHints, cx)
+//         });
+//         cx.foreground().run_until_parked();
+//         editor.update(cx, |editor, cx| {
+//             let expected_hints = vec!["3".to_string()];
+//             assert_eq!(
+//                 expected_hints,
+//                 cached_hint_labels(editor),
+//                 "Should query LSP hints for the 3rd time after enabling hints in settings and toggling them back on"
+//             );
+//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+//             assert_eq!(editor.inlay_hint_cache().version, 5);
+//         });
+//     }
+
+//     pub(crate) fn init_test(cx: &mut TestAppContext, f: impl Fn(&mut AllLanguageSettingsContent)) {
+//         cx.foreground().forbid_parking();
+
+//         cx.update(|cx| {
+//             cx.set_global(SettingsStore::test(cx));
+//             theme::init(cx);
+//             client::init_settings(cx);
+//             language::init(cx);
+//             Project::init_settings(cx);
+//             workspace::init_settings(cx);
+//             crate::init(cx);
+//         });
+
+//         update_test_language_settings(cx, f);
+//     }
+
+//     async fn prepare_test_objects(
+//         cx: &mut TestAppContext,
+//     ) -> (&'static str, ViewHandle<Editor>, FakeLanguageServer) {
+//         let mut language = Language::new(
+//             LanguageConfig {
+//                 name: "Rust".into(),
+//                 path_suffixes: vec!["rs".to_string()],
+//                 ..Default::default()
+//             },
+//             Some(tree_sitter_rust::language()),
+//         );
+//         let mut fake_servers = language
+//             .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
+//                 capabilities: lsp::ServerCapabilities {
+//                     inlay_hint_provider: Some(lsp::OneOf::Left(true)),
+//                     ..Default::default()
+//                 },
+//                 ..Default::default()
+//             }))
+//             .await;
+
+//         let fs = FakeFs::new(cx.background());
+//         fs.insert_tree(
+//             "/a",
+//             json!({
+//                 "main.rs": "fn main() { a } // and some long comment to ensure inlays are not trimmed out",
+//                 "other.rs": "// Test file",
+//             }),
+//         )
+//         .await;
+
+//         let project = Project::test(fs, ["/a".as_ref()], cx).await;
+//         project.update(cx, |project, _| project.languages().add(Arc::new(language)));
+//         let workspace = cx
+//             .add_window(|cx| Workspace::test_new(project.clone(), cx))
+//             .root(cx);
+//         let worktree_id = workspace.update(cx, |workspace, cx| {
+//             workspace.project().read_with(cx, |project, cx| {
+//                 project.worktrees(cx).next().unwrap().read(cx).id()
+//             })
+//         });
+
+//         let _buffer = project
+//             .update(cx, |project, cx| {
+//                 project.open_local_buffer("/a/main.rs", cx)
+//             })
+//             .await
+//             .unwrap();
+//         cx.foreground().run_until_parked();
+//         cx.foreground().start_waiting();
+//         let fake_server = fake_servers.next().await.unwrap();
+//         let editor = workspace
+//             .update(cx, |workspace, cx| {
+//                 workspace.open_path((worktree_id, "main.rs"), None, true, cx)
+//             })
+//             .await
+//             .unwrap()
+//             .downcast::<Editor>()
+//             .unwrap();
+
+//         editor.update(cx, |editor, cx| {
+//             assert!(cached_hint_labels(editor).is_empty());
+//             assert!(visible_hint_labels(editor, cx).is_empty());
+//             assert_eq!(editor.inlay_hint_cache().version, 0);
+//         });
+
+//         ("/a/main.rs", editor, fake_server)
+//     }
+
+//     pub fn cached_hint_labels(editor: &Editor) -> Vec<String> {
+//         let mut labels = Vec::new();
+//         for (_, excerpt_hints) in &editor.inlay_hint_cache().hints {
+//             let excerpt_hints = excerpt_hints.read();
+//             for id in &excerpt_hints.ordered_hints {
+//                 labels.push(excerpt_hints.hints_by_id[id].text());
+//             }
+//         }
+
+//         labels.sort();
+//         labels
+//     }
+
+//     pub fn visible_hint_labels(editor: &Editor, cx: &ViewContext<'_, '_, Editor>) -> Vec<String> {
+//         let mut hints = editor
+//             .visible_inlay_hints(cx)
+//             .into_iter()
+//             .map(|hint| hint.text.to_string())
+//             .collect::<Vec<_>>();
+//         hints.sort();
+//         hints
+//     }
+// }

crates/editor2/src/items.rs 🔗

@@ -20,23 +20,18 @@ use smallvec::SmallVec;
 use std::{
     borrow::Cow,
     cmp::{self, Ordering},
-    fmt::Write,
     iter,
     ops::Range,
     path::{Path, PathBuf},
     sync::Arc,
 };
 use text::Selection;
-use util::{
-    paths::{PathExt, FILE_ROW_COLUMN_DELIMITER},
-    ResultExt, TryFutureExt,
-};
+use util::{paths::PathExt, ResultExt, TryFutureExt};
 use workspace::item::{BreadcrumbText, FollowableItemHandle};
 use workspace::{
     item::{FollowableItem, Item, ItemEvent, ItemHandle, ProjectItem},
     searchable::{Direction, SearchEvent, SearchableItem, SearchableItemHandle},
-    ItemId, ItemNavHistory, Pane, StatusItemView, ToolbarItemLocation, ViewId, Workspace,
-    WorkspaceId,
+    ItemId, ItemNavHistory, Pane, ToolbarItemLocation, ViewId, Workspace, WorkspaceId,
 };
 
 pub const MAX_TAB_TITLE_LEN: usize = 24;
@@ -607,7 +602,7 @@ impl Item for Editor {
     where
         Self: Sized,
     {
-        Some(self.clone(cx))
+        Some(self.clone())
     }
 
     fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext<Self>) {
@@ -483,39 +483,40 @@ pub fn show_link_definition(
                         });
 
                     if any_definition_does_not_contain_current_location {
-                        // Highlight symbol using theme link definition highlight style
-                        let style = theme::current(cx).editor.link_definition;
-                        let highlight_range =
-                            symbol_range.unwrap_or_else(|| match &trigger_point {
-                                TriggerPoint::Text(trigger_anchor) => {
-                                    let snapshot = &snapshot.buffer_snapshot;
-                                    // If no symbol range returned from language server, use the surrounding word.
-                                    let (offset_range, _) =
-                                        snapshot.surrounding_word(*trigger_anchor);
-                                    RangeInEditor::Text(
-                                        snapshot.anchor_before(offset_range.start)
-                                            ..snapshot.anchor_after(offset_range.end),
-                                    )
-                                }
-                                TriggerPoint::InlayHint(highlight, _, _) => {
-                                    RangeInEditor::Inlay(highlight.clone())
-                                }
-                            });
-
-                        match highlight_range {
-                            RangeInEditor::Text(text_range) => this
-                                .highlight_text::<LinkGoToDefinitionState>(
-                                    vec![text_range],
-                                    style,
-                                    cx,
-                                ),
-                            RangeInEditor::Inlay(highlight) => this
-                                .highlight_inlays::<LinkGoToDefinitionState>(
-                                    vec![highlight],
-                                    style,
-                                    cx,
-                                ),
-                        }
+                        // todo!()
+                        // // Highlight symbol using theme link definition highlight style
+                        // let style = theme::current(cx).editor.link_definition;
+                        // let highlight_range =
+                        //     symbol_range.unwrap_or_else(|| match &trigger_point {
+                        //         TriggerPoint::Text(trigger_anchor) => {
+                        //             let snapshot = &snapshot.buffer_snapshot;
+                        //             // If no symbol range returned from language server, use the surrounding word.
+                        //             let (offset_range, _) =
+                        //                 snapshot.surrounding_word(*trigger_anchor);
+                        //             RangeInEditor::Text(
+                        //                 snapshot.anchor_before(offset_range.start)
+                        //                     ..snapshot.anchor_after(offset_range.end),
+                        //             )
+                        //         }
+                        //         TriggerPoint::InlayHint(highlight, _, _) => {
+                        //             RangeInEditor::Inlay(highlight.clone())
+                        //         }
+                        //     });
+
+                        // match highlight_range {
+                        //     RangeInEditor::Text(text_range) => this
+                        //         .highlight_text::<LinkGoToDefinitionState>(
+                        //             vec![text_range],
+                        //             style,
+                        //             cx,
+                        //         ),
+                        //     RangeInEditor::Inlay(highlight) => this
+                        //         .highlight_inlays::<LinkGoToDefinitionState>(
+                        //             vec![highlight],
+                        //             style,
+                        //             cx,
+                        //         ),
+                        // }
                     } else {
                         hide_link_definition(this, cx);
                     }
@@ -599,671 +600,671 @@ fn go_to_fetched_definition_of_kind(
     }
 }
 
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::{
-        display_map::ToDisplayPoint,
-        editor_tests::init_test,
-        inlay_hint_cache::tests::{cached_hint_labels, visible_hint_labels},
-        test::editor_lsp_test_context::EditorLspTestContext,
-    };
-    use futures::StreamExt;
-    use gpui::{
-        platform::{self, Modifiers, ModifiersChangedEvent},
-        View,
-    };
-    use indoc::indoc;
-    use language::language_settings::InlayHintSettings;
-    use lsp::request::{GotoDefinition, GotoTypeDefinition};
-    use util::assert_set_eq;
-
-    #[gpui::test]
-    async fn test_link_go_to_type_definition(cx: &mut gpui::TestAppContext) {
-        init_test(cx, |_| {});
-
-        let mut cx = EditorLspTestContext::new_rust(
-            lsp::ServerCapabilities {
-                hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
-                type_definition_provider: Some(lsp::TypeDefinitionProviderCapability::Simple(true)),
-                ..Default::default()
-            },
-            cx,
-        )
-        .await;
-
-        cx.set_state(indoc! {"
-            struct A;
-            let vˇariable = A;
-        "});
-
-        // Basic hold cmd+shift, expect highlight in region if response contains type definition
-        let hover_point = cx.display_point(indoc! {"
-            struct A;
-            let vˇariable = A;
-        "});
-        let symbol_range = cx.lsp_range(indoc! {"
-            struct A;
-            let «variable» = A;
-        "});
-        let target_range = cx.lsp_range(indoc! {"
-            struct «A»;
-            let variable = A;
-        "});
-
-        let mut requests =
-            cx.handle_request::<GotoTypeDefinition, _, _>(move |url, _, _| async move {
-                Ok(Some(lsp::GotoTypeDefinitionResponse::Link(vec![
-                    lsp::LocationLink {
-                        origin_selection_range: Some(symbol_range),
-                        target_uri: url.clone(),
-                        target_range,
-                        target_selection_range: target_range,
-                    },
-                ])))
-            });
-
-        // Press cmd+shift to trigger highlight
-        cx.update_editor(|editor, cx| {
-            update_go_to_definition_link(
-                editor,
-                Some(GoToDefinitionTrigger::Text(hover_point)),
-                true,
-                true,
-                cx,
-            );
-        });
-        requests.next().await;
-        cx.foreground().run_until_parked();
-        cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
-            struct A;
-            let «variable» = A;
-        "});
-
-        // Unpress shift causes highlight to go away (normal goto-definition is not valid here)
-        cx.update_editor(|editor, cx| {
-            editor.modifiers_changed(
-                &platform::ModifiersChangedEvent {
-                    modifiers: Modifiers {
-                        cmd: true,
-                        ..Default::default()
-                    },
-                    ..Default::default()
-                },
-                cx,
-            );
-        });
-        // Assert no link highlights
-        cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
-            struct A;
-            let variable = A;
-        "});
-
-        // Cmd+shift click without existing definition requests and jumps
-        let hover_point = cx.display_point(indoc! {"
-            struct A;
-            let vˇariable = A;
-        "});
-        let target_range = cx.lsp_range(indoc! {"
-            struct «A»;
-            let variable = A;
-        "});
-
-        let mut requests =
-            cx.handle_request::<GotoTypeDefinition, _, _>(move |url, _, _| async move {
-                Ok(Some(lsp::GotoTypeDefinitionResponse::Link(vec![
-                    lsp::LocationLink {
-                        origin_selection_range: None,
-                        target_uri: url,
-                        target_range,
-                        target_selection_range: target_range,
-                    },
-                ])))
-            });
-
-        cx.update_editor(|editor, cx| {
-            go_to_fetched_type_definition(editor, PointForPosition::valid(hover_point), false, cx);
-        });
-        requests.next().await;
-        cx.foreground().run_until_parked();
-
-        cx.assert_editor_state(indoc! {"
-            struct «Aˇ»;
-            let variable = A;
-        "});
-    }
-
-    #[gpui::test]
-    async fn test_link_go_to_definition(cx: &mut gpui::TestAppContext) {
-        init_test(cx, |_| {});
-
-        let mut cx = EditorLspTestContext::new_rust(
-            lsp::ServerCapabilities {
-                hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
-                ..Default::default()
-            },
-            cx,
-        )
-        .await;
-
-        cx.set_state(indoc! {"
-            fn ˇtest() { do_work(); }
-            fn do_work() { test(); }
-        "});
-
-        // Basic hold cmd, expect highlight in region if response contains definition
-        let hover_point = cx.display_point(indoc! {"
-            fn test() { do_wˇork(); }
-            fn do_work() { test(); }
-        "});
-        let symbol_range = cx.lsp_range(indoc! {"
-            fn test() { «do_work»(); }
-            fn do_work() { test(); }
-        "});
-        let target_range = cx.lsp_range(indoc! {"
-            fn test() { do_work(); }
-            fn «do_work»() { test(); }
-        "});
-
-        let mut requests = cx.handle_request::<GotoDefinition, _, _>(move |url, _, _| async move {
-            Ok(Some(lsp::GotoDefinitionResponse::Link(vec![
-                lsp::LocationLink {
-                    origin_selection_range: Some(symbol_range),
-                    target_uri: url.clone(),
-                    target_range,
-                    target_selection_range: target_range,
-                },
-            ])))
-        });
-
-        cx.update_editor(|editor, cx| {
-            update_go_to_definition_link(
-                editor,
-                Some(GoToDefinitionTrigger::Text(hover_point)),
-                true,
-                false,
-                cx,
-            );
-        });
-        requests.next().await;
-        cx.foreground().run_until_parked();
-        cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
-            fn test() { «do_work»(); }
-            fn do_work() { test(); }
-        "});
-
-        // Unpress cmd causes highlight to go away
-        cx.update_editor(|editor, cx| {
-            editor.modifiers_changed(&Default::default(), cx);
-        });
-
-        // Assert no link highlights
-        cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
-            fn test() { do_work(); }
-            fn do_work() { test(); }
-        "});
-
-        // Response without source range still highlights word
-        cx.update_editor(|editor, _| editor.link_go_to_definition_state.last_trigger_point = None);
-        let mut requests = cx.handle_request::<GotoDefinition, _, _>(move |url, _, _| async move {
-            Ok(Some(lsp::GotoDefinitionResponse::Link(vec![
-                lsp::LocationLink {
-                    // No origin range
-                    origin_selection_range: None,
-                    target_uri: url.clone(),
-                    target_range,
-                    target_selection_range: target_range,
-                },
-            ])))
-        });
-        cx.update_editor(|editor, cx| {
-            update_go_to_definition_link(
-                editor,
-                Some(GoToDefinitionTrigger::Text(hover_point)),
-                true,
-                false,
-                cx,
-            );
-        });
-        requests.next().await;
-        cx.foreground().run_until_parked();
-
-        cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
-            fn test() { «do_work»(); }
-            fn do_work() { test(); }
-        "});
-
-        // Moving mouse to location with no response dismisses highlight
-        let hover_point = cx.display_point(indoc! {"
-            fˇn test() { do_work(); }
-            fn do_work() { test(); }
-        "});
-        let mut requests = cx
-            .lsp
-            .handle_request::<GotoDefinition, _, _>(move |_, _| async move {
-                // No definitions returned
-                Ok(Some(lsp::GotoDefinitionResponse::Link(vec![])))
-            });
-        cx.update_editor(|editor, cx| {
-            update_go_to_definition_link(
-                editor,
-                Some(GoToDefinitionTrigger::Text(hover_point)),
-                true,
-                false,
-                cx,
-            );
-        });
-        requests.next().await;
-        cx.foreground().run_until_parked();
-
-        // Assert no link highlights
-        cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
-            fn test() { do_work(); }
-            fn do_work() { test(); }
-        "});
-
-        // Move mouse without cmd and then pressing cmd triggers highlight
-        let hover_point = cx.display_point(indoc! {"
-            fn test() { do_work(); }
-            fn do_work() { teˇst(); }
-        "});
-        cx.update_editor(|editor, cx| {
-            update_go_to_definition_link(
-                editor,
-                Some(GoToDefinitionTrigger::Text(hover_point)),
-                false,
-                false,
-                cx,
-            );
-        });
-        cx.foreground().run_until_parked();
-
-        // Assert no link highlights
-        cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
-            fn test() { do_work(); }
-            fn do_work() { test(); }
-        "});
-
-        let symbol_range = cx.lsp_range(indoc! {"
-            fn test() { do_work(); }
-            fn do_work() { «test»(); }
-        "});
-        let target_range = cx.lsp_range(indoc! {"
-            fn «test»() { do_work(); }
-            fn do_work() { test(); }
-        "});
-
-        let mut requests = cx.handle_request::<GotoDefinition, _, _>(move |url, _, _| async move {
-            Ok(Some(lsp::GotoDefinitionResponse::Link(vec![
-                lsp::LocationLink {
-                    origin_selection_range: Some(symbol_range),
-                    target_uri: url,
-                    target_range,
-                    target_selection_range: target_range,
-                },
-            ])))
-        });
-        cx.update_editor(|editor, cx| {
-            editor.modifiers_changed(
-                &ModifiersChangedEvent {
-                    modifiers: Modifiers {
-                        cmd: true,
-                        ..Default::default()
-                    },
-                },
-                cx,
-            );
-        });
-        requests.next().await;
-        cx.foreground().run_until_parked();
-
-        cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
-            fn test() { do_work(); }
-            fn do_work() { «test»(); }
-        "});
-
-        // Deactivating the window dismisses the highlight
-        cx.update_workspace(|workspace, cx| {
-            workspace.on_window_activation_changed(false, cx);
-        });
-        cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
-            fn test() { do_work(); }
-            fn do_work() { test(); }
-        "});
-
-        // Moving the mouse restores the highlights.
-        cx.update_editor(|editor, cx| {
-            update_go_to_definition_link(
-                editor,
-                Some(GoToDefinitionTrigger::Text(hover_point)),
-                true,
-                false,
-                cx,
-            );
-        });
-        cx.foreground().run_until_parked();
-        cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
-            fn test() { do_work(); }
-            fn do_work() { «test»(); }
-        "});
-
-        // Moving again within the same symbol range doesn't re-request
-        let hover_point = cx.display_point(indoc! {"
-            fn test() { do_work(); }
-            fn do_work() { tesˇt(); }
-        "});
-        cx.update_editor(|editor, cx| {
-            update_go_to_definition_link(
-                editor,
-                Some(GoToDefinitionTrigger::Text(hover_point)),
-                true,
-                false,
-                cx,
-            );
-        });
-        cx.foreground().run_until_parked();
-        cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
-            fn test() { do_work(); }
-            fn do_work() { «test»(); }
-        "});
-
-        // Cmd click with existing definition doesn't re-request and dismisses highlight
-        cx.update_editor(|editor, cx| {
-            go_to_fetched_definition(editor, PointForPosition::valid(hover_point), false, cx);
-        });
-        // Assert selection moved to to definition
-        cx.lsp
-            .handle_request::<GotoDefinition, _, _>(move |_, _| async move {
-                // Empty definition response to make sure we aren't hitting the lsp and using
-                // the cached location instead
-                Ok(Some(lsp::GotoDefinitionResponse::Link(vec![])))
-            });
-        cx.foreground().run_until_parked();
-        cx.assert_editor_state(indoc! {"
-            fn «testˇ»() { do_work(); }
-            fn do_work() { test(); }
-        "});
-
-        // Assert no link highlights after jump
-        cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
-            fn test() { do_work(); }
-            fn do_work() { test(); }
-        "});
-
-        // Cmd click without existing definition requests and jumps
-        let hover_point = cx.display_point(indoc! {"
-            fn test() { do_wˇork(); }
-            fn do_work() { test(); }
-        "});
-        let target_range = cx.lsp_range(indoc! {"
-            fn test() { do_work(); }
-            fn «do_work»() { test(); }
-        "});
-
-        let mut requests = cx.handle_request::<GotoDefinition, _, _>(move |url, _, _| async move {
-            Ok(Some(lsp::GotoDefinitionResponse::Link(vec![
-                lsp::LocationLink {
-                    origin_selection_range: None,
-                    target_uri: url,
-                    target_range,
-                    target_selection_range: target_range,
-                },
-            ])))
-        });
-        cx.update_editor(|editor, cx| {
-            go_to_fetched_definition(editor, PointForPosition::valid(hover_point), false, cx);
-        });
-        requests.next().await;
-        cx.foreground().run_until_parked();
-        cx.assert_editor_state(indoc! {"
-            fn test() { do_work(); }
-            fn «do_workˇ»() { test(); }
-        "});
-
-        // 1. We have a pending selection, mouse point is over a symbol that we have a response for, hitting cmd and nothing happens
-        // 2. Selection is completed, hovering
-        let hover_point = cx.display_point(indoc! {"
-            fn test() { do_wˇork(); }
-            fn do_work() { test(); }
-        "});
-        let target_range = cx.lsp_range(indoc! {"
-            fn test() { do_work(); }
-            fn «do_work»() { test(); }
-        "});
-        let mut requests = cx.handle_request::<GotoDefinition, _, _>(move |url, _, _| async move {
-            Ok(Some(lsp::GotoDefinitionResponse::Link(vec![
-                lsp::LocationLink {
-                    origin_selection_range: None,
-                    target_uri: url,
-                    target_range,
-                    target_selection_range: target_range,
-                },
-            ])))
-        });
-
-        // create a pending selection
-        let selection_range = cx.ranges(indoc! {"
-            fn «test() { do_w»ork(); }
-            fn do_work() { test(); }
-        "})[0]
-            .clone();
-        cx.update_editor(|editor, cx| {
-            let snapshot = editor.buffer().read(cx).snapshot(cx);
-            let anchor_range = snapshot.anchor_before(selection_range.start)
-                ..snapshot.anchor_after(selection_range.end);
-            editor.change_selections(Some(crate::Autoscroll::fit()), cx, |s| {
-                s.set_pending_anchor_range(anchor_range, crate::SelectMode::Character)
-            });
-        });
-        cx.update_editor(|editor, cx| {
-            update_go_to_definition_link(
-                editor,
-                Some(GoToDefinitionTrigger::Text(hover_point)),
-                true,
-                false,
-                cx,
-            );
-        });
-        cx.foreground().run_until_parked();
-        assert!(requests.try_next().is_err());
-        cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
-            fn test() { do_work(); }
-            fn do_work() { test(); }
-        "});
-        cx.foreground().run_until_parked();
-    }
-
-    #[gpui::test]
-    async fn test_link_go_to_inlay(cx: &mut gpui::TestAppContext) {
-        init_test(cx, |settings| {
-            settings.defaults.inlay_hints = Some(InlayHintSettings {
-                enabled: true,
-                show_type_hints: true,
-                show_parameter_hints: true,
-                show_other_hints: true,
-            })
-        });
-
-        let mut cx = EditorLspTestContext::new_rust(
-            lsp::ServerCapabilities {
-                inlay_hint_provider: Some(lsp::OneOf::Left(true)),
-                ..Default::default()
-            },
-            cx,
-        )
-        .await;
-        cx.set_state(indoc! {"
-            struct TestStruct;
-
-            fn main() {
-                let variableˇ = TestStruct;
-            }
-        "});
-        let hint_start_offset = cx.ranges(indoc! {"
-            struct TestStruct;
-
-            fn main() {
-                let variableˇ = TestStruct;
-            }
-        "})[0]
-            .start;
-        let hint_position = cx.to_lsp(hint_start_offset);
-        let target_range = cx.lsp_range(indoc! {"
-            struct «TestStruct»;
-
-            fn main() {
-                let variable = TestStruct;
-            }
-        "});
-
-        let expected_uri = cx.buffer_lsp_url.clone();
-        let hint_label = ": TestStruct";
-        cx.lsp
-            .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
-                let expected_uri = expected_uri.clone();
-                async move {
-                    assert_eq!(params.text_document.uri, expected_uri);
-                    Ok(Some(vec![lsp::InlayHint {
-                        position: hint_position,
-                        label: lsp::InlayHintLabel::LabelParts(vec![lsp::InlayHintLabelPart {
-                            value: hint_label.to_string(),
-                            location: Some(lsp::Location {
-                                uri: params.text_document.uri,
-                                range: target_range,
-                            }),
-                            ..Default::default()
-                        }]),
-                        kind: Some(lsp::InlayHintKind::TYPE),
-                        text_edits: None,
-                        tooltip: None,
-                        padding_left: Some(false),
-                        padding_right: Some(false),
-                        data: None,
-                    }]))
-                }
-            })
-            .next()
-            .await;
-        cx.foreground().run_until_parked();
-        cx.update_editor(|editor, cx| {
-            let expected_layers = vec![hint_label.to_string()];
-            assert_eq!(expected_layers, cached_hint_labels(editor));
-            assert_eq!(expected_layers, visible_hint_labels(editor, cx));
-        });
-
-        let inlay_range = cx
-            .ranges(indoc! {"
-            struct TestStruct;
-
-            fn main() {
-                let variable« »= TestStruct;
-            }
-        "})
-            .get(0)
-            .cloned()
-            .unwrap();
-        let hint_hover_position = cx.update_editor(|editor, cx| {
-            let snapshot = editor.snapshot(cx);
-            let previous_valid = inlay_range.start.to_display_point(&snapshot);
-            let next_valid = inlay_range.end.to_display_point(&snapshot);
-            assert_eq!(previous_valid.row(), next_valid.row());
-            assert!(previous_valid.column() < next_valid.column());
-            let exact_unclipped = DisplayPoint::new(
-                previous_valid.row(),
-                previous_valid.column() + (hint_label.len() / 2) as u32,
-            );
-            PointForPosition {
-                previous_valid,
-                next_valid,
-                exact_unclipped,
-                column_overshoot_after_line_end: 0,
-            }
-        });
-        // Press cmd to trigger highlight
-        cx.update_editor(|editor, cx| {
-            update_inlay_link_and_hover_points(
-                &editor.snapshot(cx),
-                hint_hover_position,
-                editor,
-                true,
-                false,
-                cx,
-            );
-        });
-        cx.foreground().run_until_parked();
-        cx.update_editor(|editor, cx| {
-            let snapshot = editor.snapshot(cx);
-            let actual_highlights = snapshot
-                .inlay_highlights::<LinkGoToDefinitionState>()
-                .into_iter()
-                .flat_map(|highlights| highlights.values().map(|(_, highlight)| highlight))
-                .collect::<Vec<_>>();
-
-            let buffer_snapshot = editor.buffer().update(cx, |buffer, cx| buffer.snapshot(cx));
-            let expected_highlight = InlayHighlight {
-                inlay: InlayId::Hint(0),
-                inlay_position: buffer_snapshot.anchor_at(inlay_range.start, Bias::Right),
-                range: 0..hint_label.len(),
-            };
-            assert_set_eq!(actual_highlights, vec![&expected_highlight]);
-        });
-
-        // Unpress cmd causes highlight to go away
-        cx.update_editor(|editor, cx| {
-            editor.modifiers_changed(
-                &platform::ModifiersChangedEvent {
-                    modifiers: Modifiers {
-                        cmd: false,
-                        ..Default::default()
-                    },
-                    ..Default::default()
-                },
-                cx,
-            );
-        });
-        // Assert no link highlights
-        cx.update_editor(|editor, cx| {
-            let snapshot = editor.snapshot(cx);
-            let actual_ranges = snapshot
-                .text_highlight_ranges::<LinkGoToDefinitionState>()
-                .map(|ranges| ranges.as_ref().clone().1)
-                .unwrap_or_default();
-
-            assert!(actual_ranges.is_empty(), "When no cmd is pressed, should have no hint label selected, but got: {actual_ranges:?}");
-        });
-
-        // Cmd+click without existing definition requests and jumps
-        cx.update_editor(|editor, cx| {
-            editor.modifiers_changed(
-                &platform::ModifiersChangedEvent {
-                    modifiers: Modifiers {
-                        cmd: true,
-                        ..Default::default()
-                    },
-                    ..Default::default()
-                },
-                cx,
-            );
-            update_inlay_link_and_hover_points(
-                &editor.snapshot(cx),
-                hint_hover_position,
-                editor,
-                true,
-                false,
-                cx,
-            );
-        });
-        cx.foreground().run_until_parked();
-        cx.update_editor(|editor, cx| {
-            go_to_fetched_type_definition(editor, hint_hover_position, false, cx);
-        });
-        cx.foreground().run_until_parked();
-        cx.assert_editor_state(indoc! {"
-            struct «TestStructˇ»;
-
-            fn main() {
-                let variable = TestStruct;
-            }
-        "});
-    }
-}
+// #[cfg(test)]
+// mod tests {
+//     use super::*;
+//     use crate::{
+//         display_map::ToDisplayPoint,
+//         editor_tests::init_test,
+//         inlay_hint_cache::tests::{cached_hint_labels, visible_hint_labels},
+//         test::editor_lsp_test_context::EditorLspTestContext,
+//     };
+//     use futures::StreamExt;
+//     use gpui::{
+//         platform::{self, Modifiers, ModifiersChangedEvent},
+//         View,
+//     };
+//     use indoc::indoc;
+//     use language::language_settings::InlayHintSettings;
+//     use lsp::request::{GotoDefinition, GotoTypeDefinition};
+//     use util::assert_set_eq;
+
+//     #[gpui::test]
+//     async fn test_link_go_to_type_definition(cx: &mut gpui::TestAppContext) {
+//         init_test(cx, |_| {});
+
+//         let mut cx = EditorLspTestContext::new_rust(
+//             lsp::ServerCapabilities {
+//                 hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
+//                 type_definition_provider: Some(lsp::TypeDefinitionProviderCapability::Simple(true)),
+//                 ..Default::default()
+//             },
+//             cx,
+//         )
+//         .await;
+
+//         cx.set_state(indoc! {"
+//             struct A;
+//             let vˇariable = A;
+//         "});
+
+//         // Basic hold cmd+shift, expect highlight in region if response contains type definition
+//         let hover_point = cx.display_point(indoc! {"
+//             struct A;
+//             let vˇariable = A;
+//         "});
+//         let symbol_range = cx.lsp_range(indoc! {"
+//             struct A;
+//             let «variable» = A;
+//         "});
+//         let target_range = cx.lsp_range(indoc! {"
+//             struct «A»;
+//             let variable = A;
+//         "});
+
+//         let mut requests =
+//             cx.handle_request::<GotoTypeDefinition, _, _>(move |url, _, _| async move {
+//                 Ok(Some(lsp::GotoTypeDefinitionResponse::Link(vec![
+//                     lsp::LocationLink {
+//                         origin_selection_range: Some(symbol_range),
+//                         target_uri: url.clone(),
+//                         target_range,
+//                         target_selection_range: target_range,
+//                     },
+//                 ])))
+//             });
+
+//         // Press cmd+shift to trigger highlight
+//         cx.update_editor(|editor, cx| {
+//             update_go_to_definition_link(
+//                 editor,
+//                 Some(GoToDefinitionTrigger::Text(hover_point)),
+//                 true,
+//                 true,
+//                 cx,
+//             );
+//         });
+//         requests.next().await;
+//         cx.foreground().run_until_parked();
+//         cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
+//             struct A;
+//             let «variable» = A;
+//         "});
+
+//         // Unpress shift causes highlight to go away (normal goto-definition is not valid here)
+//         cx.update_editor(|editor, cx| {
+//             editor.modifiers_changed(
+//                 &platform::ModifiersChangedEvent {
+//                     modifiers: Modifiers {
+//                         cmd: true,
+//                         ..Default::default()
+//                     },
+//                     ..Default::default()
+//                 },
+//                 cx,
+//             );
+//         });
+//         // Assert no link highlights
+//         cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
+//             struct A;
+//             let variable = A;
+//         "});
+
+//         // Cmd+shift click without existing definition requests and jumps
+//         let hover_point = cx.display_point(indoc! {"
+//             struct A;
+//             let vˇariable = A;
+//         "});
+//         let target_range = cx.lsp_range(indoc! {"
+//             struct «A»;
+//             let variable = A;
+//         "});
+
+//         let mut requests =
+//             cx.handle_request::<GotoTypeDefinition, _, _>(move |url, _, _| async move {
+//                 Ok(Some(lsp::GotoTypeDefinitionResponse::Link(vec![
+//                     lsp::LocationLink {
+//                         origin_selection_range: None,
+//                         target_uri: url,
+//                         target_range,
+//                         target_selection_range: target_range,
+//                     },
+//                 ])))
+//             });
+
+//         cx.update_editor(|editor, cx| {
+//             go_to_fetched_type_definition(editor, PointForPosition::valid(hover_point), false, cx);
+//         });
+//         requests.next().await;
+//         cx.foreground().run_until_parked();
+
+//         cx.assert_editor_state(indoc! {"
+//             struct «Aˇ»;
+//             let variable = A;
+//         "});
+//     }
+
+//     #[gpui::test]
+//     async fn test_link_go_to_definition(cx: &mut gpui::TestAppContext) {
+//         init_test(cx, |_| {});
+
+//         let mut cx = EditorLspTestContext::new_rust(
+//             lsp::ServerCapabilities {
+//                 hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
+//                 ..Default::default()
+//             },
+//             cx,
+//         )
+//         .await;
+
+//         cx.set_state(indoc! {"
+//             fn ˇtest() { do_work(); }
+//             fn do_work() { test(); }
+//         "});
+
+//         // Basic hold cmd, expect highlight in region if response contains definition
+//         let hover_point = cx.display_point(indoc! {"
+//             fn test() { do_wˇork(); }
+//             fn do_work() { test(); }
+//         "});
+//         let symbol_range = cx.lsp_range(indoc! {"
+//             fn test() { «do_work»(); }
+//             fn do_work() { test(); }
+//         "});
+//         let target_range = cx.lsp_range(indoc! {"
+//             fn test() { do_work(); }
+//             fn «do_work»() { test(); }
+//         "});
+
+//         let mut requests = cx.handle_request::<GotoDefinition, _, _>(move |url, _, _| async move {
+//             Ok(Some(lsp::GotoDefinitionResponse::Link(vec![
+//                 lsp::LocationLink {
+//                     origin_selection_range: Some(symbol_range),
+//                     target_uri: url.clone(),
+//                     target_range,
+//                     target_selection_range: target_range,
+//                 },
+//             ])))
+//         });
+
+//         cx.update_editor(|editor, cx| {
+//             update_go_to_definition_link(
+//                 editor,
+//                 Some(GoToDefinitionTrigger::Text(hover_point)),
+//                 true,
+//                 false,
+//                 cx,
+//             );
+//         });
+//         requests.next().await;
+//         cx.foreground().run_until_parked();
+//         cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
+//             fn test() { «do_work»(); }
+//             fn do_work() { test(); }
+//         "});
+
+//         // Unpress cmd causes highlight to go away
+//         cx.update_editor(|editor, cx| {
+//             editor.modifiers_changed(&Default::default(), cx);
+//         });
+
+//         // Assert no link highlights
+//         cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
+//             fn test() { do_work(); }
+//             fn do_work() { test(); }
+//         "});
+
+//         // Response without source range still highlights word
+//         cx.update_editor(|editor, _| editor.link_go_to_definition_state.last_trigger_point = None);
+//         let mut requests = cx.handle_request::<GotoDefinition, _, _>(move |url, _, _| async move {
+//             Ok(Some(lsp::GotoDefinitionResponse::Link(vec![
+//                 lsp::LocationLink {
+//                     // No origin range
+//                     origin_selection_range: None,
+//                     target_uri: url.clone(),
+//                     target_range,
+//                     target_selection_range: target_range,
+//                 },
+//             ])))
+//         });
+//         cx.update_editor(|editor, cx| {
+//             update_go_to_definition_link(
+//                 editor,
+//                 Some(GoToDefinitionTrigger::Text(hover_point)),
+//                 true,
+//                 false,
+//                 cx,
+//             );
+//         });
+//         requests.next().await;
+//         cx.foreground().run_until_parked();
+
+//         cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
+//             fn test() { «do_work»(); }
+//             fn do_work() { test(); }
+//         "});
+
+//         // Moving mouse to location with no response dismisses highlight
+//         let hover_point = cx.display_point(indoc! {"
+//             fˇn test() { do_work(); }
+//             fn do_work() { test(); }
+//         "});
+//         let mut requests = cx
+//             .lsp
+//             .handle_request::<GotoDefinition, _, _>(move |_, _| async move {
+//                 // No definitions returned
+//                 Ok(Some(lsp::GotoDefinitionResponse::Link(vec![])))
+//             });
+//         cx.update_editor(|editor, cx| {
+//             update_go_to_definition_link(
+//                 editor,
+//                 Some(GoToDefinitionTrigger::Text(hover_point)),
+//                 true,
+//                 false,
+//                 cx,
+//             );
+//         });
+//         requests.next().await;
+//         cx.foreground().run_until_parked();
+
+//         // Assert no link highlights
+//         cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
+//             fn test() { do_work(); }
+//             fn do_work() { test(); }
+//         "});
+
+//         // Move mouse without cmd and then pressing cmd triggers highlight
+//         let hover_point = cx.display_point(indoc! {"
+//             fn test() { do_work(); }
+//             fn do_work() { teˇst(); }
+//         "});
+//         cx.update_editor(|editor, cx| {
+//             update_go_to_definition_link(
+//                 editor,
+//                 Some(GoToDefinitionTrigger::Text(hover_point)),
+//                 false,
+//                 false,
+//                 cx,
+//             );
+//         });
+//         cx.foreground().run_until_parked();
+
+//         // Assert no link highlights
+//         cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
+//             fn test() { do_work(); }
+//             fn do_work() { test(); }
+//         "});
+
+//         let symbol_range = cx.lsp_range(indoc! {"
+//             fn test() { do_work(); }
+//             fn do_work() { «test»(); }
+//         "});
+//         let target_range = cx.lsp_range(indoc! {"
+//             fn «test»() { do_work(); }
+//             fn do_work() { test(); }
+//         "});
+
+//         let mut requests = cx.handle_request::<GotoDefinition, _, _>(move |url, _, _| async move {
+//             Ok(Some(lsp::GotoDefinitionResponse::Link(vec![
+//                 lsp::LocationLink {
+//                     origin_selection_range: Some(symbol_range),
+//                     target_uri: url,
+//                     target_range,
+//                     target_selection_range: target_range,
+//                 },
+//             ])))
+//         });
+//         cx.update_editor(|editor, cx| {
+//             editor.modifiers_changed(
+//                 &ModifiersChangedEvent {
+//                     modifiers: Modifiers {
+//                         cmd: true,
+//                         ..Default::default()
+//                     },
+//                 },
+//                 cx,
+//             );
+//         });
+//         requests.next().await;
+//         cx.foreground().run_until_parked();
+
+//         cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
+//             fn test() { do_work(); }
+//             fn do_work() { «test»(); }
+//         "});
+
+//         // Deactivating the window dismisses the highlight
+//         cx.update_workspace(|workspace, cx| {
+//             workspace.on_window_activation_changed(false, cx);
+//         });
+//         cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
+//             fn test() { do_work(); }
+//             fn do_work() { test(); }
+//         "});
+
+//         // Moving the mouse restores the highlights.
+//         cx.update_editor(|editor, cx| {
+//             update_go_to_definition_link(
+//                 editor,
+//                 Some(GoToDefinitionTrigger::Text(hover_point)),
+//                 true,
+//                 false,
+//                 cx,
+//             );
+//         });
+//         cx.foreground().run_until_parked();
+//         cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
+//             fn test() { do_work(); }
+//             fn do_work() { «test»(); }
+//         "});
+
+//         // Moving again within the same symbol range doesn't re-request
+//         let hover_point = cx.display_point(indoc! {"
+//             fn test() { do_work(); }
+//             fn do_work() { tesˇt(); }
+//         "});
+//         cx.update_editor(|editor, cx| {
+//             update_go_to_definition_link(
+//                 editor,
+//                 Some(GoToDefinitionTrigger::Text(hover_point)),
+//                 true,
+//                 false,
+//                 cx,
+//             );
+//         });
+//         cx.foreground().run_until_parked();
+//         cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
+//             fn test() { do_work(); }
+//             fn do_work() { «test»(); }
+//         "});
+
+//         // Cmd click with existing definition doesn't re-request and dismisses highlight
+//         cx.update_editor(|editor, cx| {
+//             go_to_fetched_definition(editor, PointForPosition::valid(hover_point), false, cx);
+//         });
+//         // Assert selection moved to to definition
+//         cx.lsp
+//             .handle_request::<GotoDefinition, _, _>(move |_, _| async move {
+//                 // Empty definition response to make sure we aren't hitting the lsp and using
+//                 // the cached location instead
+//                 Ok(Some(lsp::GotoDefinitionResponse::Link(vec![])))
+//             });
+//         cx.foreground().run_until_parked();
+//         cx.assert_editor_state(indoc! {"
+//             fn «testˇ»() { do_work(); }
+//             fn do_work() { test(); }
+//         "});
+
+//         // Assert no link highlights after jump
+//         cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
+//             fn test() { do_work(); }
+//             fn do_work() { test(); }
+//         "});
+
+//         // Cmd click without existing definition requests and jumps
+//         let hover_point = cx.display_point(indoc! {"
+//             fn test() { do_wˇork(); }
+//             fn do_work() { test(); }
+//         "});
+//         let target_range = cx.lsp_range(indoc! {"
+//             fn test() { do_work(); }
+//             fn «do_work»() { test(); }
+//         "});
+
+//         let mut requests = cx.handle_request::<GotoDefinition, _, _>(move |url, _, _| async move {
+//             Ok(Some(lsp::GotoDefinitionResponse::Link(vec![
+//                 lsp::LocationLink {
+//                     origin_selection_range: None,
+//                     target_uri: url,
+//                     target_range,
+//                     target_selection_range: target_range,
+//                 },
+//             ])))
+//         });
+//         cx.update_editor(|editor, cx| {
+//             go_to_fetched_definition(editor, PointForPosition::valid(hover_point), false, cx);
+//         });
+//         requests.next().await;
+//         cx.foreground().run_until_parked();
+//         cx.assert_editor_state(indoc! {"
+//             fn test() { do_work(); }
+//             fn «do_workˇ»() { test(); }
+//         "});
+
+//         // 1. We have a pending selection, mouse point is over a symbol that we have a response for, hitting cmd and nothing happens
+//         // 2. Selection is completed, hovering
+//         let hover_point = cx.display_point(indoc! {"
+//             fn test() { do_wˇork(); }
+//             fn do_work() { test(); }
+//         "});
+//         let target_range = cx.lsp_range(indoc! {"
+//             fn test() { do_work(); }
+//             fn «do_work»() { test(); }
+//         "});
+//         let mut requests = cx.handle_request::<GotoDefinition, _, _>(move |url, _, _| async move {
+//             Ok(Some(lsp::GotoDefinitionResponse::Link(vec![
+//                 lsp::LocationLink {
+//                     origin_selection_range: None,
+//                     target_uri: url,
+//                     target_range,
+//                     target_selection_range: target_range,
+//                 },
+//             ])))
+//         });
+
+//         // create a pending selection
+//         let selection_range = cx.ranges(indoc! {"
+//             fn «test() { do_w»ork(); }
+//             fn do_work() { test(); }
+//         "})[0]
+//             .clone();
+//         cx.update_editor(|editor, cx| {
+//             let snapshot = editor.buffer().read(cx).snapshot(cx);
+//             let anchor_range = snapshot.anchor_before(selection_range.start)
+//                 ..snapshot.anchor_after(selection_range.end);
+//             editor.change_selections(Some(crate::Autoscroll::fit()), cx, |s| {
+//                 s.set_pending_anchor_range(anchor_range, crate::SelectMode::Character)
+//             });
+//         });
+//         cx.update_editor(|editor, cx| {
+//             update_go_to_definition_link(
+//                 editor,
+//                 Some(GoToDefinitionTrigger::Text(hover_point)),
+//                 true,
+//                 false,
+//                 cx,
+//             );
+//         });
+//         cx.foreground().run_until_parked();
+//         assert!(requests.try_next().is_err());
+//         cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
+//             fn test() { do_work(); }
+//             fn do_work() { test(); }
+//         "});
+//         cx.foreground().run_until_parked();
+//     }
+
+//     #[gpui::test]
+//     async fn test_link_go_to_inlay(cx: &mut gpui::TestAppContext) {
+//         init_test(cx, |settings| {
+//             settings.defaults.inlay_hints = Some(InlayHintSettings {
+//                 enabled: true,
+//                 show_type_hints: true,
+//                 show_parameter_hints: true,
+//                 show_other_hints: true,
+//             })
+//         });
+
+//         let mut cx = EditorLspTestContext::new_rust(
+//             lsp::ServerCapabilities {
+//                 inlay_hint_provider: Some(lsp::OneOf::Left(true)),
+//                 ..Default::default()
+//             },
+//             cx,
+//         )
+//         .await;
+//         cx.set_state(indoc! {"
+//             struct TestStruct;
+
+//             fn main() {
+//                 let variableˇ = TestStruct;
+//             }
+//         "});
+//         let hint_start_offset = cx.ranges(indoc! {"
+//             struct TestStruct;
+
+//             fn main() {
+//                 let variableˇ = TestStruct;
+//             }
+//         "})[0]
+//             .start;
+//         let hint_position = cx.to_lsp(hint_start_offset);
+//         let target_range = cx.lsp_range(indoc! {"
+//             struct «TestStruct»;
+
+//             fn main() {
+//                 let variable = TestStruct;
+//             }
+//         "});
+
+//         let expected_uri = cx.buffer_lsp_url.clone();
+//         let hint_label = ": TestStruct";
+//         cx.lsp
+//             .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
+//                 let expected_uri = expected_uri.clone();
+//                 async move {
+//                     assert_eq!(params.text_document.uri, expected_uri);
+//                     Ok(Some(vec![lsp::InlayHint {
+//                         position: hint_position,
+//                         label: lsp::InlayHintLabel::LabelParts(vec![lsp::InlayHintLabelPart {
+//                             value: hint_label.to_string(),
+//                             location: Some(lsp::Location {
+//                                 uri: params.text_document.uri,
+//                                 range: target_range,
+//                             }),
+//                             ..Default::default()
+//                         }]),
+//                         kind: Some(lsp::InlayHintKind::TYPE),
+//                         text_edits: None,
+//                         tooltip: None,
+//                         padding_left: Some(false),
+//                         padding_right: Some(false),
+//                         data: None,
+//                     }]))
+//                 }
+//             })
+//             .next()
+//             .await;
+//         cx.foreground().run_until_parked();
+//         cx.update_editor(|editor, cx| {
+//             let expected_layers = vec![hint_label.to_string()];
+//             assert_eq!(expected_layers, cached_hint_labels(editor));
+//             assert_eq!(expected_layers, visible_hint_labels(editor, cx));
+//         });
+
+//         let inlay_range = cx
+//             .ranges(indoc! {"
+//             struct TestStruct;
+
+//             fn main() {
+//                 let variable« »= TestStruct;
+//             }
+//         "})
+//             .get(0)
+//             .cloned()
+//             .unwrap();
+//         let hint_hover_position = cx.update_editor(|editor, cx| {
+//             let snapshot = editor.snapshot(cx);
+//             let previous_valid = inlay_range.start.to_display_point(&snapshot);
+//             let next_valid = inlay_range.end.to_display_point(&snapshot);
+//             assert_eq!(previous_valid.row(), next_valid.row());
+//             assert!(previous_valid.column() < next_valid.column());
+//             let exact_unclipped = DisplayPoint::new(
+//                 previous_valid.row(),
+//                 previous_valid.column() + (hint_label.len() / 2) as u32,
+//             );
+//             PointForPosition {
+//                 previous_valid,
+//                 next_valid,
+//                 exact_unclipped,
+//                 column_overshoot_after_line_end: 0,
+//             }
+//         });
+//         // Press cmd to trigger highlight
+//         cx.update_editor(|editor, cx| {
+//             update_inlay_link_and_hover_points(
+//                 &editor.snapshot(cx),
+//                 hint_hover_position,
+//                 editor,
+//                 true,
+//                 false,
+//                 cx,
+//             );
+//         });
+//         cx.foreground().run_until_parked();
+//         cx.update_editor(|editor, cx| {
+//             let snapshot = editor.snapshot(cx);
+//             let actual_highlights = snapshot
+//                 .inlay_highlights::<LinkGoToDefinitionState>()
+//                 .into_iter()
+//                 .flat_map(|highlights| highlights.values().map(|(_, highlight)| highlight))
+//                 .collect::<Vec<_>>();
+
+//             let buffer_snapshot = editor.buffer().update(cx, |buffer, cx| buffer.snapshot(cx));
+//             let expected_highlight = InlayHighlight {
+//                 inlay: InlayId::Hint(0),
+//                 inlay_position: buffer_snapshot.anchor_at(inlay_range.start, Bias::Right),
+//                 range: 0..hint_label.len(),
+//             };
+//             assert_set_eq!(actual_highlights, vec![&expected_highlight]);
+//         });
+
+//         // Unpress cmd causes highlight to go away
+//         cx.update_editor(|editor, cx| {
+//             editor.modifiers_changed(
+//                 &platform::ModifiersChangedEvent {
+//                     modifiers: Modifiers {
+//                         cmd: false,
+//                         ..Default::default()
+//                     },
+//                     ..Default::default()
+//                 },
+//                 cx,
+//             );
+//         });
+//         // Assert no link highlights
+//         cx.update_editor(|editor, cx| {
+//             let snapshot = editor.snapshot(cx);
+//             let actual_ranges = snapshot
+//                 .text_highlight_ranges::<LinkGoToDefinitionState>()
+//                 .map(|ranges| ranges.as_ref().clone().1)
+//                 .unwrap_or_default();
+
+//             assert!(actual_ranges.is_empty(), "When no cmd is pressed, should have no hint label selected, but got: {actual_ranges:?}");
+//         });
+
+//         // Cmd+click without existing definition requests and jumps
+//         cx.update_editor(|editor, cx| {
+//             editor.modifiers_changed(
+//                 &platform::ModifiersChangedEvent {
+//                     modifiers: Modifiers {
+//                         cmd: true,
+//                         ..Default::default()
+//                     },
+//                     ..Default::default()
+//                 },
+//                 cx,
+//             );
+//             update_inlay_link_and_hover_points(
+//                 &editor.snapshot(cx),
+//                 hint_hover_position,
+//                 editor,
+//                 true,
+//                 false,
+//                 cx,
+//             );
+//         });
+//         cx.foreground().run_until_parked();
+//         cx.update_editor(|editor, cx| {
+//             go_to_fetched_type_definition(editor, hint_hover_position, false, cx);
+//         });
+//         cx.foreground().run_until_parked();
+//         cx.assert_editor_state(indoc! {"
+//             struct «TestStructˇ»;
+
+//             fn main() {
+//                 let variable = TestStruct;
+//             }
+//         "});
+//     }
+// }

crates/editor2/src/mouse_context_menu.rs 🔗

@@ -1,13 +1,10 @@
-use crate::{
-    DisplayPoint, Editor, EditorMode, FindAllReferences, GoToDefinition, GoToTypeDefinition,
-    Rename, RevealInFinder, SelectMode, ToggleCodeActions,
-};
+use crate::{DisplayPoint, Editor, EditorMode, SelectMode};
 use context_menu::ContextMenuItem;
-use gpui::{elements::AnchorCorner, geometry::vector::Vector2F, ViewContext};
+use gpui::{Pixels, Point, ViewContext};
 
 pub fn deploy_context_menu(
     editor: &mut Editor,
-    position: Vector2F,
+    position: Point<Pixels>,
     point: DisplayPoint,
     cx: &mut ViewContext<Editor>,
 ) {
@@ -31,66 +28,67 @@ pub fn deploy_context_menu(
         s.set_pending_display_range(point..point, SelectMode::Character);
     });
 
-    editor.mouse_context_menu.update(cx, |menu, cx| {
-        menu.show(
-            position,
-            AnchorCorner::TopLeft,
-            vec![
-                ContextMenuItem::action("Rename Symbol", Rename),
-                ContextMenuItem::action("Go to Definition", GoToDefinition),
-                ContextMenuItem::action("Go to Type Definition", GoToTypeDefinition),
-                ContextMenuItem::action("Find All References", FindAllReferences),
-                ContextMenuItem::action(
-                    "Code Actions",
-                    ToggleCodeActions {
-                        deployed_from_indicator: false,
-                    },
-                ),
-                ContextMenuItem::Separator,
-                ContextMenuItem::action("Reveal in Finder", RevealInFinder),
-            ],
-            cx,
-        );
-    });
+    // todo!()
+    // editor.mouse_context_menu.update(cx, |menu, cx| {
+    //     menu.show(
+    //         position,
+    //         AnchorCorner::TopLeft,
+    //         vec![
+    //             ContextMenuItem::action("Rename Symbol", Rename),
+    //             ContextMenuItem::action("Go to Definition", GoToDefinition),
+    //             ContextMenuItem::action("Go to Type Definition", GoToTypeDefinition),
+    //             ContextMenuItem::action("Find All References", FindAllReferences),
+    //             ContextMenuItem::action(
+    //                 "Code Actions",
+    //                 ToggleCodeActions {
+    //                     deployed_from_indicator: false,
+    //                 },
+    //             ),
+    //             ContextMenuItem::Separator,
+    //             ContextMenuItem::action("Reveal in Finder", RevealInFinder),
+    //         ],
+    //         cx,
+    //     );
+    // });
     cx.notify();
 }
 
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::{editor_tests::init_test, test::editor_lsp_test_context::EditorLspTestContext};
-    use indoc::indoc;
+// #[cfg(test)]
+// mod tests {
+//     use super::*;
+//     use crate::{editor_tests::init_test, test::editor_lsp_test_context::EditorLspTestContext};
+//     use indoc::indoc;
 
-    #[gpui::test]
-    async fn test_mouse_context_menu(cx: &mut gpui::TestAppContext) {
-        init_test(cx, |_| {});
+//     #[gpui::test]
+//     async fn test_mouse_context_menu(cx: &mut gpui::TestAppContext) {
+//         init_test(cx, |_| {});
 
-        let mut cx = EditorLspTestContext::new_rust(
-            lsp::ServerCapabilities {
-                hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
-                ..Default::default()
-            },
-            cx,
-        )
-        .await;
+//         let mut cx = EditorLspTestContext::new_rust(
+//             lsp::ServerCapabilities {
+//                 hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
+//                 ..Default::default()
+//             },
+//             cx,
+//         )
+//         .await;
 
-        cx.set_state(indoc! {"
-            fn teˇst() {
-                do_work();
-            }
-        "});
-        let point = cx.display_point(indoc! {"
-            fn test() {
-                do_wˇork();
-            }
-        "});
-        cx.update_editor(|editor, cx| deploy_context_menu(editor, Default::default(), point, cx));
+//         cx.set_state(indoc! {"
+//             fn teˇst() {
+//                 do_work();
+//             }
+//         "});
+//         let point = cx.display_point(indoc! {"
+//             fn test() {
+//                 do_wˇork();
+//             }
+//         "});
+//         cx.update_editor(|editor, cx| deploy_context_menu(editor, Default::default(), point, cx));
 
-        cx.assert_editor_state(indoc! {"
-            fn test() {
-                do_wˇork();
-            }
-        "});
-        cx.editor(|editor, app| assert!(editor.mouse_context_menu.read(app).visible()));
-    }
-}
+//         cx.assert_editor_state(indoc! {"
+//             fn test() {
+//                 do_wˇork();
+//             }
+//         "});
+//         cx.editor(|editor, app| assert!(editor.mouse_context_menu.read(app).visible()));
+//     }
+// }

crates/editor2/src/movement.rs 🔗

@@ -1,6 +1,6 @@
 use super::{Bias, DisplayPoint, DisplaySnapshot, SelectionGoal, ToDisplayPoint};
 use crate::{char_kind, CharKind, EditorStyle, ToOffset, ToPoint};
-use gpui::{FontCache, TextLayoutCache};
+use gpui::TextSystem;
 use language::Point;
 use std::{ops::Range, sync::Arc};
 
@@ -13,8 +13,7 @@ pub enum FindRange {
 /// TextLayoutDetails encompasses everything we need to move vertically
 /// taking into account variable width characters.
 pub struct TextLayoutDetails {
-    pub font_cache: Arc<FontCache>,
-    pub text_layout_cache: Arc<TextLayoutCache>,
+    pub text_system: TextSystem,
     pub editor_style: EditorStyle,
 }
 

crates/editor2/src/scroll.rs 🔗

@@ -2,19 +2,6 @@ pub mod actions;
 pub mod autoscroll;
 pub mod scroll_amount;
 
-use std::{
-    cmp::Ordering,
-    time::{Duration, Instant},
-};
-
-use gpui::{
-    geometry::vector::{vec2f, Vector2F},
-    AppContext, Axis, Task, ViewContext,
-};
-use language::{Bias, Point};
-use util::ResultExt;
-use workspace::WorkspaceId;
-
 use crate::{
     display_map::{DisplaySnapshot, ToDisplayPoint},
     hover_popover::hide_hover,
@@ -22,6 +9,14 @@ use crate::{
     Anchor, DisplayPoint, Editor, EditorMode, Event, InlayHintRefreshReason, MultiBufferSnapshot,
     ToPoint,
 };
+use gpui::{point, AppContext, Pixels, Task, ViewContext};
+use language::{Bias, Point};
+use std::{
+    cmp::Ordering,
+    time::{Duration, Instant},
+};
+use util::ResultExt;
+use workspace::WorkspaceId;
 
 use self::{
     autoscroll::{Autoscroll, AutoscrollStrategy},
@@ -37,19 +32,19 @@ pub struct ScrollbarAutoHide(pub bool);
 
 #[derive(Clone, Copy, Debug, PartialEq)]
 pub struct ScrollAnchor {
-    pub offset: Vector2F,
+    pub offset: gpui::Point<f32>,
     pub anchor: Anchor,
 }
 
 impl ScrollAnchor {
     fn new() -> Self {
         Self {
-            offset: Vector2F::zero(),
+            offset: Point::zero(),
             anchor: Anchor::min(),
         }
     }
 
-    pub fn scroll_position(&self, snapshot: &DisplaySnapshot) -> Vector2F {
+    pub fn scroll_position(&self, snapshot: &DisplaySnapshot) -> Point<Pixels> {
         let mut scroll_position = self.offset;
         if self.anchor != Anchor::min() {
             let scroll_top = self.anchor.to_display_point(snapshot).row() as f32;
@@ -65,6 +60,12 @@ impl ScrollAnchor {
     }
 }
 
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub enum Axis {
+    Vertical,
+    Horizontal,
+}
+
 #[derive(Clone, Copy, Debug)]
 pub struct OngoingScroll {
     last_event: Instant,
@@ -79,7 +80,7 @@ impl OngoingScroll {
         }
     }
 
-    pub fn filter(&self, delta: &mut Vector2F) -> Option<Axis> {
+    pub fn filter(&self, delta: &mut Point<Pixels>) -> Option<Axis> {
         const UNLOCK_PERCENT: f32 = 1.9;
         const UNLOCK_LOWER_BOUND: f32 = 6.;
         let mut axis = self.axis;
@@ -114,8 +115,8 @@ impl OngoingScroll {
         }
 
         match axis {
-            Some(Axis::Vertical) => *delta = vec2f(0., delta.y()),
-            Some(Axis::Horizontal) => *delta = vec2f(delta.x(), 0.),
+            Some(Axis::Vertical) => *delta = point(0., delta.y()),
+            Some(Axis::Horizontal) => *delta = point(delta.x(), 0.),
             None => {}
         }
 
@@ -128,7 +129,7 @@ pub struct ScrollManager {
     anchor: ScrollAnchor,
     ongoing: OngoingScroll,
     autoscroll_request: Option<(Autoscroll, bool)>,
-    last_autoscroll: Option<(Vector2F, f32, f32, AutoscrollStrategy)>,
+    last_autoscroll: Option<(gpui::Point<Pixels>, f32, f32, AutoscrollStrategy)>,
     show_scrollbars: bool,
     hide_scrollbar_task: Option<Task<()>>,
     visible_line_count: Option<f32>,
@@ -166,13 +167,13 @@ impl ScrollManager {
         self.ongoing.axis = axis;
     }
 
-    pub fn scroll_position(&self, snapshot: &DisplaySnapshot) -> Vector2F {
+    pub fn scroll_position(&self, snapshot: &DisplaySnapshot) -> Point<Pixels> {
         self.anchor.scroll_position(snapshot)
     }
 
     fn set_scroll_position(
         &mut self,
-        scroll_position: Vector2F,
+        scroll_position: Point<Pixels>,
         map: &DisplaySnapshot,
         local: bool,
         autoscroll: bool,
@@ -183,7 +184,7 @@ impl ScrollManager {
             (
                 ScrollAnchor {
                     anchor: Anchor::min(),
-                    offset: scroll_position.max(vec2f(0., 0.)),
+                    offset: scroll_position.max(Point::zero()),
                 },
                 0,
             )
@@ -197,7 +198,7 @@ impl ScrollManager {
             (
                 ScrollAnchor {
                     anchor: top_anchor,
-                    offset: vec2f(
+                    offset: point(
                         scroll_position.x(),
                         scroll_position.y() - top_anchor.to_display_point(&map).row() as f32,
                     ),
@@ -310,13 +311,17 @@ impl Editor {
         }
     }
 
-    pub fn set_scroll_position(&mut self, scroll_position: Vector2F, cx: &mut ViewContext<Self>) {
+    pub fn set_scroll_position(
+        &mut self,
+        scroll_position: Point<Pixels>,
+        cx: &mut ViewContext<Self>,
+    ) {
         self.set_scroll_position_internal(scroll_position, true, false, cx);
     }
 
     pub(crate) fn set_scroll_position_internal(
         &mut self,
-        scroll_position: Vector2F,
+        scroll_position: Point<Pixels>,
         local: bool,
         autoscroll: bool,
         cx: &mut ViewContext<Self>,
@@ -337,7 +342,7 @@ impl Editor {
         self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
     }
 
-    pub fn scroll_position(&self, cx: &mut ViewContext<Self>) -> Vector2F {
+    pub fn scroll_position(&self, cx: &mut ViewContext<Self>) -> Point<Pixels> {
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
         self.scroll_manager.anchor.scroll_position(&display_map)
     }
@@ -379,7 +384,7 @@ impl Editor {
         }
 
         let cur_position = self.scroll_position(cx);
-        let new_pos = cur_position + vec2f(0., amount.lines(self));
+        let new_pos = cur_position + point(0., amount.lines(self));
         self.set_scroll_position(new_pos, cx);
     }
 
@@ -427,7 +432,7 @@ impl Editor {
                 .snapshot(cx)
                 .anchor_at(Point::new(top_row as u32, 0), Bias::Left);
             let scroll_anchor = ScrollAnchor {
-                offset: Vector2F::new(x, y),
+                offset: Point::new(x, y),
                 anchor: top_anchor,
             };
             self.set_scroll_anchor(scroll_anchor, cx);

crates/editor2/src/scroll/actions.rs 🔗

@@ -17,7 +17,7 @@ use gpui::AppContext;
 // );
 
 pub fn init(cx: &mut AppContext) {
-    /// todo!()
+    // todo!()
     // cx.add_action(Editor::next_screen);
     // cx.add_action(Editor::scroll_cursor_top);
     // cx.add_action(Editor::scroll_cursor_center);

crates/editor2/src/selections_collection.rs 🔗

@@ -25,8 +25,8 @@ pub struct PendingSelection {
 
 #[derive(Debug, Clone)]
 pub struct SelectionsCollection {
-    display_map: ModelHandle<DisplayMap>,
-    buffer: ModelHandle<MultiBuffer>,
+    display_map: Model<DisplayMap>,
+    buffer: Model<MultiBuffer>,
     pub next_selection_id: usize,
     pub line_mode: bool,
     disjoint: Arc<[Selection<Anchor>]>,
@@ -34,7 +34,7 @@ pub struct SelectionsCollection {
 }
 
 impl SelectionsCollection {
-    pub fn new(display_map: ModelHandle<DisplayMap>, buffer: ModelHandle<MultiBuffer>) -> Self {
+    pub fn new(display_map: Model<DisplayMap>, buffer: Model<MultiBuffer>) -> Self {
         Self {
             display_map,
             buffer,

crates/editor2/src/test.rs 🔗

@@ -67,16 +67,13 @@ pub fn assert_text_with_selections(
 // RA thinks this is dead code even though it is used in a whole lot of tests
 #[allow(dead_code)]
 #[cfg(any(test, feature = "test-support"))]
-pub(crate) fn build_editor(
-    buffer: ModelHandle<MultiBuffer>,
-    cx: &mut ViewContext<Editor>,
-) -> Editor {
+pub(crate) fn build_editor(buffer: Model<MultiBuffer>, cx: &mut ViewContext<Editor>) -> Editor {
     Editor::new(EditorMode::Full, buffer, None, None, cx)
 }
 
 pub(crate) fn build_editor_with_project(
-    project: ModelHandle<Project>,
-    buffer: ModelHandle<MultiBuffer>,
+    project: Model<Project>,
+    buffer: Model<MultiBuffer>,
     cx: &mut ViewContext<Editor>,
 ) -> Editor {
     Editor::new(EditorMode::Full, buffer, Some(project), None, cx)

crates/editor2/src/test/editor_lsp_test_context.rs 🔗

@@ -9,7 +9,7 @@ use anyhow::Result;
 use crate::{Editor, ToPoint};
 use collections::HashSet;
 use futures::Future;
-use gpui::{json, ViewContext, ViewHandle};
+use gpui::{json, View, ViewContext};
 use indoc::indoc;
 use language::{point_to_lsp, FakeLspAdapter, Language, LanguageConfig, LanguageQueries};
 use lsp::{notification, request};

crates/editor2/src/test/editor_test_context.rs 🔗

@@ -3,8 +3,7 @@ use crate::{
 };
 use futures::Future;
 use gpui::{
-    executor::Foreground, keymap_matcher::Keystroke, AnyWindowHandle, AppContext, ContextHandle,
-    ModelContext, ViewContext, ViewHandle,
+    AnyWindowHandle, AppContext, ForegroundExecutor, Keystroke, ModelContext, View, ViewContext,
 };
 use indoc::indoc;
 use language::{Buffer, BufferSnapshot};
@@ -23,7 +22,7 @@ use super::build_editor_with_project;
 pub struct EditorTestContext<'a> {
     pub cx: &'a mut gpui::TestAppContext,
     pub window: AnyWindowHandle,
-    pub editor: ViewHandle<Editor>,
+    pub editor: View<Editor>,
 }
 
 impl<'a> EditorTestContext<'a> {
@@ -119,37 +118,37 @@ impl<'a> EditorTestContext<'a> {
         self.buffer(|buffer, _| buffer.snapshot())
     }
 
-    pub fn simulate_keystroke(&mut self, keystroke_text: &str) -> ContextHandle {
-        let keystroke_under_test_handle =
-            self.add_assertion_context(format!("Simulated Keystroke: {:?}", keystroke_text));
-        let keystroke = Keystroke::parse(keystroke_text).unwrap();
-
-        self.cx.dispatch_keystroke(self.window, keystroke, false);
-
-        keystroke_under_test_handle
-    }
-
-    pub fn simulate_keystrokes<const COUNT: usize>(
-        &mut self,
-        keystroke_texts: [&str; COUNT],
-    ) -> ContextHandle {
-        let keystrokes_under_test_handle =
-            self.add_assertion_context(format!("Simulated Keystrokes: {:?}", keystroke_texts));
-        for keystroke_text in keystroke_texts.into_iter() {
-            self.simulate_keystroke(keystroke_text);
-        }
-        // it is common for keyboard shortcuts to kick off async actions, so this ensures that they are complete
-        // before returning.
-        // NOTE: we don't do this in simulate_keystroke() because a possible cause of bugs is that typing too
-        // quickly races with async actions.
-        if let Foreground::Deterministic { cx_id: _, executor } = self.cx.foreground().as_ref() {
-            executor.run_until_parked();
-        } else {
-            unreachable!();
-        }
-
-        keystrokes_under_test_handle
-    }
+    // pub fn simulate_keystroke(&mut self, keystroke_text: &str) -> ContextHandle {
+    //     let keystroke_under_test_handle =
+    //         self.add_assertion_context(format!("Simulated Keystroke: {:?}", keystroke_text));
+    //     let keystroke = Keystroke::parse(keystroke_text).unwrap();
+
+    //     self.cx.dispatch_keystroke(self.window, keystroke, false);
+
+    //     keystroke_under_test_handle
+    // }
+
+    // pub fn simulate_keystrokes<const COUNT: usize>(
+    //     &mut self,
+    //     keystroke_texts: [&str; COUNT],
+    // ) -> ContextHandle {
+    //     let keystrokes_under_test_handle =
+    //         self.add_assertion_context(format!("Simulated Keystrokes: {:?}", keystroke_texts));
+    //     for keystroke_text in keystroke_texts.into_iter() {
+    //         self.simulate_keystroke(keystroke_text);
+    //     }
+    //     // it is common for keyboard shortcuts to kick off async actions, so this ensures that they are complete
+    //     // before returning.
+    //     // NOTE: we don't do this in simulate_keystroke() because a possible cause of bugs is that typing too
+    //     // quickly races with async actions.
+    //     if let Foreground::Deterministic { cx_id: _, executor } = self.cx.foreground().as_ref() {
+    //         executor.run_until_parked();
+    //     } else {
+    //         unreachable!();
+    //     }
+
+    //     keystrokes_under_test_handle
+    // }
 
     pub fn ranges(&self, marked_text: &str) -> Vec<Range<usize>> {
         let (unmarked_text, ranges) = marked_text_ranges(marked_text, false);
@@ -177,144 +176,144 @@ impl<'a> EditorTestContext<'a> {
         self.update_buffer(|buffer, cx| buffer.set_diff_base(diff_base, cx));
     }
 
-    /// Change the editor's text and selections using a string containing
-    /// embedded range markers that represent the ranges and directions of
-    /// each selection.
-    ///
-    /// Returns a context handle so that assertion failures can print what
-    /// editor state was needed to cause the failure.
-    ///
-    /// See the `util::test::marked_text_ranges` function for more information.
-    pub fn set_state(&mut self, marked_text: &str) -> ContextHandle {
-        let state_context = self.add_assertion_context(format!(
-            "Initial Editor State: \"{}\"",
-            marked_text.escape_debug().to_string()
-        ));
-        let (unmarked_text, selection_ranges) = marked_text_ranges(marked_text, true);
-        self.editor.update(self.cx, |editor, cx| {
-            editor.set_text(unmarked_text, cx);
-            editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                s.select_ranges(selection_ranges)
-            })
-        });
-        state_context
-    }
-
-    /// Only change the editor's selections
-    pub fn set_selections_state(&mut self, marked_text: &str) -> ContextHandle {
-        let state_context = self.add_assertion_context(format!(
-            "Initial Editor State: \"{}\"",
-            marked_text.escape_debug().to_string()
-        ));
-        let (unmarked_text, selection_ranges) = marked_text_ranges(marked_text, true);
-        self.editor.update(self.cx, |editor, cx| {
-            assert_eq!(editor.text(cx), unmarked_text);
-            editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
-                s.select_ranges(selection_ranges)
-            })
-        });
-        state_context
-    }
-
-    /// Make an assertion about the editor's text and the ranges and directions
-    /// of its selections using a string containing embedded range markers.
-    ///
-    /// See the `util::test::marked_text_ranges` function for more information.
-    #[track_caller]
-    pub fn assert_editor_state(&mut self, marked_text: &str) {
-        let (unmarked_text, expected_selections) = marked_text_ranges(marked_text, true);
-        let buffer_text = self.buffer_text();
-
-        if buffer_text != unmarked_text {
-            panic!("Unmarked text doesn't match buffer text\nBuffer text: {buffer_text:?}\nUnmarked text: {unmarked_text:?}\nRaw buffer text\n{buffer_text}Raw unmarked text\n{unmarked_text}");
-        }
-
-        self.assert_selections(expected_selections, marked_text.to_string())
-    }
-
-    pub fn editor_state(&mut self) -> String {
-        generate_marked_text(self.buffer_text().as_str(), &self.editor_selections(), true)
-    }
-
-    #[track_caller]
-    pub fn assert_editor_background_highlights<Tag: 'static>(&mut self, marked_text: &str) {
-        let expected_ranges = self.ranges(marked_text);
-        let actual_ranges: Vec<Range<usize>> = self.update_editor(|editor, cx| {
-            let snapshot = editor.snapshot(cx);
-            editor
-                .background_highlights
-                .get(&TypeId::of::<Tag>())
-                .map(|h| h.1.clone())
-                .unwrap_or_default()
-                .into_iter()
-                .map(|range| range.to_offset(&snapshot.buffer_snapshot))
-                .collect()
-        });
-        assert_set_eq!(actual_ranges, expected_ranges);
-    }
-
-    #[track_caller]
-    pub fn assert_editor_text_highlights<Tag: ?Sized + 'static>(&mut self, marked_text: &str) {
-        let expected_ranges = self.ranges(marked_text);
-        let snapshot = self.update_editor(|editor, cx| editor.snapshot(cx));
-        let actual_ranges: Vec<Range<usize>> = snapshot
-            .text_highlight_ranges::<Tag>()
-            .map(|ranges| ranges.as_ref().clone().1)
-            .unwrap_or_default()
-            .into_iter()
-            .map(|range| range.to_offset(&snapshot.buffer_snapshot))
-            .collect();
-        assert_set_eq!(actual_ranges, expected_ranges);
-    }
-
-    #[track_caller]
-    pub fn assert_editor_selections(&mut self, expected_selections: Vec<Range<usize>>) {
-        let expected_marked_text =
-            generate_marked_text(&self.buffer_text(), &expected_selections, true);
-        self.assert_selections(expected_selections, expected_marked_text)
-    }
-
-    fn editor_selections(&self) -> Vec<Range<usize>> {
-        self.editor
-            .read_with(self.cx, |editor, cx| editor.selections.all::<usize>(cx))
-            .into_iter()
-            .map(|s| {
-                if s.reversed {
-                    s.end..s.start
-                } else {
-                    s.start..s.end
-                }
-            })
-            .collect::<Vec<_>>()
-    }
-
-    #[track_caller]
-    fn assert_selections(
-        &mut self,
-        expected_selections: Vec<Range<usize>>,
-        expected_marked_text: String,
-    ) {
-        let actual_selections = self.editor_selections();
-        let actual_marked_text =
-            generate_marked_text(&self.buffer_text(), &actual_selections, true);
-        if expected_selections != actual_selections {
-            panic!(
-                indoc! {"
-
-                    {}Editor has unexpected selections.
-
-                    Expected selections:
-                    {}
-
-                    Actual selections:
-                    {}
-                "},
-                self.assertion_context(),
-                expected_marked_text,
-                actual_marked_text,
-            );
-        }
-    }
+    // /// Change the editor's text and selections using a string containing
+    // /// embedded range markers that represent the ranges and directions of
+    // /// each selection.
+    // ///
+    // /// Returns a context handle so that assertion failures can print what
+    // /// editor state was needed to cause the failure.
+    // ///
+    // /// See the `util::test::marked_text_ranges` function for more information.
+    // pub fn set_state(&mut self, marked_text: &str) -> ContextHandle {
+    //     let state_context = self.add_assertion_context(format!(
+    //         "Initial Editor State: \"{}\"",
+    //         marked_text.escape_debug().to_string()
+    //     ));
+    //     let (unmarked_text, selection_ranges) = marked_text_ranges(marked_text, true);
+    //     self.editor.update(self.cx, |editor, cx| {
+    //         editor.set_text(unmarked_text, cx);
+    //         editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+    //             s.select_ranges(selection_ranges)
+    //         })
+    //     });
+    //     state_context
+    // }
+
+    // /// Only change the editor's selections
+    // pub fn set_selections_state(&mut self, marked_text: &str) -> ContextHandle {
+    //     let state_context = self.add_assertion_context(format!(
+    //         "Initial Editor State: \"{}\"",
+    //         marked_text.escape_debug().to_string()
+    //     ));
+    //     let (unmarked_text, selection_ranges) = marked_text_ranges(marked_text, true);
+    //     self.editor.update(self.cx, |editor, cx| {
+    //         assert_eq!(editor.text(cx), unmarked_text);
+    //         editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
+    //             s.select_ranges(selection_ranges)
+    //         })
+    //     });
+    //     state_context
+    // }
+
+    // /// Make an assertion about the editor's text and the ranges and directions
+    // /// of its selections using a string containing embedded range markers.
+    // ///
+    // /// See the `util::test::marked_text_ranges` function for more information.
+    // #[track_caller]
+    // pub fn assert_editor_state(&mut self, marked_text: &str) {
+    //     let (unmarked_text, expected_selections) = marked_text_ranges(marked_text, true);
+    //     let buffer_text = self.buffer_text();
+
+    //     if buffer_text != unmarked_text {
+    //         panic!("Unmarked text doesn't match buffer text\nBuffer text: {buffer_text:?}\nUnmarked text: {unmarked_text:?}\nRaw buffer text\n{buffer_text}Raw unmarked text\n{unmarked_text}");
+    //     }
+
+    //     self.assert_selections(expected_selections, marked_text.to_string())
+    // }
+
+    // pub fn editor_state(&mut self) -> String {
+    //     generate_marked_text(self.buffer_text().as_str(), &self.editor_selections(), true)
+    // }
+
+    // #[track_caller]
+    // pub fn assert_editor_background_highlights<Tag: 'static>(&mut self, marked_text: &str) {
+    //     let expected_ranges = self.ranges(marked_text);
+    //     let actual_ranges: Vec<Range<usize>> = self.update_editor(|editor, cx| {
+    //         let snapshot = editor.snapshot(cx);
+    //         editor
+    //             .background_highlights
+    //             .get(&TypeId::of::<Tag>())
+    //             .map(|h| h.1.clone())
+    //             .unwrap_or_default()
+    //             .into_iter()
+    //             .map(|range| range.to_offset(&snapshot.buffer_snapshot))
+    //             .collect()
+    //     });
+    //     assert_set_eq!(actual_ranges, expected_ranges);
+    // }
+
+    // #[track_caller]
+    // pub fn assert_editor_text_highlights<Tag: ?Sized + 'static>(&mut self, marked_text: &str) {
+    //     let expected_ranges = self.ranges(marked_text);
+    //     let snapshot = self.update_editor(|editor, cx| editor.snapshot(cx));
+    //     let actual_ranges: Vec<Range<usize>> = snapshot
+    //         .text_highlight_ranges::<Tag>()
+    //         .map(|ranges| ranges.as_ref().clone().1)
+    //         .unwrap_or_default()
+    //         .into_iter()
+    //         .map(|range| range.to_offset(&snapshot.buffer_snapshot))
+    //         .collect();
+    //     assert_set_eq!(actual_ranges, expected_ranges);
+    // }
+
+    // #[track_caller]
+    // pub fn assert_editor_selections(&mut self, expected_selections: Vec<Range<usize>>) {
+    //     let expected_marked_text =
+    //         generate_marked_text(&self.buffer_text(), &expected_selections, true);
+    //     self.assert_selections(expected_selections, expected_marked_text)
+    // }
+
+    // fn editor_selections(&self) -> Vec<Range<usize>> {
+    //     self.editor
+    //         .read_with(self.cx, |editor, cx| editor.selections.all::<usize>(cx))
+    //         .into_iter()
+    //         .map(|s| {
+    //             if s.reversed {
+    //                 s.end..s.start
+    //             } else {
+    //                 s.start..s.end
+    //             }
+    //         })
+    //         .collect::<Vec<_>>()
+    // }
+
+    // #[track_caller]
+    // fn assert_selections(
+    //     &mut self,
+    //     expected_selections: Vec<Range<usize>>,
+    //     expected_marked_text: String,
+    // ) {
+    //     let actual_selections = self.editor_selections();
+    //     let actual_marked_text =
+    //         generate_marked_text(&self.buffer_text(), &actual_selections, true);
+    //     if expected_selections != actual_selections {
+    //         panic!(
+    //             indoc! {"
+
+    //                 {}Editor has unexpected selections.
+
+    //                 Expected selections:
+    //                 {}
+
+    //                 Actual selections:
+    //                 {}
+    //             "},
+    //             self.assertion_context(),
+    //             expected_marked_text,
+    //             actual_marked_text,
+    //         );
+    //     }
+    // }
 }
 
 impl<'a> Deref for EditorTestContext<'a> {

crates/gpui2/src/text_system.rs 🔗

@@ -7,7 +7,7 @@ use anyhow::anyhow;
 pub use font_features::*;
 pub use line::*;
 pub use line_layout::*;
-use line_wrapper::*;
+pub use line_wrapper::*;
 use smallvec::SmallVec;
 
 use crate::{

crates/language2/Cargo.toml 🔗

@@ -58,6 +58,7 @@ unicase = "2.6"
 rand = { workspace = true, optional = true }
 tree-sitter-rust = { workspace = true, optional = true }
 tree-sitter-typescript = { workspace = true, optional = true }
+pulldown-cmark = { version = "0.9.2", default-features = false }
 
 [dev-dependencies]
 client = { package = "client2", path = "../client2", features = ["test-support"] }

crates/language2/src/language2.rs 🔗

@@ -8,6 +8,7 @@ mod syntax_map;
 
 #[cfg(test)]
 mod buffer_tests;
+pub mod markdown;
 
 use anyhow::{anyhow, Context, Result};
 use async_trait::async_trait;

crates/language2/src/markdown.rs 🔗

@@ -0,0 +1,301 @@
+use std::sync::Arc;
+use std::{ops::Range, path::PathBuf};
+
+use crate::{HighlightId, Language, LanguageRegistry};
+use gpui::{px, FontStyle, FontWeight, HighlightStyle, UnderlineStyle};
+use pulldown_cmark::{CodeBlockKind, Event, Options, Parser, Tag};
+
+#[derive(Debug, Clone)]
+pub struct ParsedMarkdown {
+    pub text: String,
+    pub highlights: Vec<(Range<usize>, MarkdownHighlight)>,
+    pub region_ranges: Vec<Range<usize>>,
+    pub regions: Vec<ParsedRegion>,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum MarkdownHighlight {
+    Style(MarkdownHighlightStyle),
+    Code(HighlightId),
+}
+
+impl MarkdownHighlight {
+    pub fn to_highlight_style(&self, theme: &theme::SyntaxTheme) -> Option<HighlightStyle> {
+        match self {
+            MarkdownHighlight::Style(style) => {
+                let mut highlight = HighlightStyle::default();
+
+                if style.italic {
+                    highlight.font_style = Some(FontStyle::Italic);
+                }
+
+                if style.underline {
+                    highlight.underline = Some(UnderlineStyle {
+                        thickness: px(1.),
+                        ..Default::default()
+                    });
+                }
+
+                if style.weight != FontWeight::default() {
+                    highlight.font_weight = Some(style.weight);
+                }
+
+                Some(highlight)
+            }
+
+            MarkdownHighlight::Code(id) => id.style(theme),
+        }
+    }
+}
+
+#[derive(Debug, Clone, Default, PartialEq, Eq)]
+pub struct MarkdownHighlightStyle {
+    pub italic: bool,
+    pub underline: bool,
+    pub weight: FontWeight,
+}
+
+#[derive(Debug, Clone)]
+pub struct ParsedRegion {
+    pub code: bool,
+    pub link: Option<Link>,
+}
+
+#[derive(Debug, Clone)]
+pub enum Link {
+    Web { url: String },
+    Path { path: PathBuf },
+}
+
+impl Link {
+    fn identify(text: String) -> Option<Link> {
+        if text.starts_with("http") {
+            return Some(Link::Web { url: text });
+        }
+
+        let path = PathBuf::from(text);
+        if path.is_absolute() {
+            return Some(Link::Path { path });
+        }
+
+        None
+    }
+}
+
+pub async fn parse_markdown(
+    markdown: &str,
+    language_registry: &Arc<LanguageRegistry>,
+    language: Option<Arc<Language>>,
+) -> ParsedMarkdown {
+    let mut text = String::new();
+    let mut highlights = Vec::new();
+    let mut region_ranges = Vec::new();
+    let mut regions = Vec::new();
+
+    parse_markdown_block(
+        markdown,
+        language_registry,
+        language,
+        &mut text,
+        &mut highlights,
+        &mut region_ranges,
+        &mut regions,
+    )
+    .await;
+
+    ParsedMarkdown {
+        text,
+        highlights,
+        region_ranges,
+        regions,
+    }
+}
+
+pub async fn parse_markdown_block(
+    markdown: &str,
+    language_registry: &Arc<LanguageRegistry>,
+    language: Option<Arc<Language>>,
+    text: &mut String,
+    highlights: &mut Vec<(Range<usize>, MarkdownHighlight)>,
+    region_ranges: &mut Vec<Range<usize>>,
+    regions: &mut Vec<ParsedRegion>,
+) {
+    let mut bold_depth = 0;
+    let mut italic_depth = 0;
+    let mut link_url = None;
+    let mut current_language = None;
+    let mut list_stack = Vec::new();
+
+    for event in Parser::new_ext(&markdown, Options::all()) {
+        let prev_len = text.len();
+        match event {
+            Event::Text(t) => {
+                if let Some(language) = &current_language {
+                    highlight_code(text, highlights, t.as_ref(), language);
+                } else {
+                    text.push_str(t.as_ref());
+
+                    let mut style = MarkdownHighlightStyle::default();
+
+                    if bold_depth > 0 {
+                        style.weight = FontWeight::BOLD;
+                    }
+
+                    if italic_depth > 0 {
+                        style.italic = true;
+                    }
+
+                    if let Some(link) = link_url.clone().and_then(|u| Link::identify(u)) {
+                        region_ranges.push(prev_len..text.len());
+                        regions.push(ParsedRegion {
+                            code: false,
+                            link: Some(link),
+                        });
+                        style.underline = true;
+                    }
+
+                    if style != MarkdownHighlightStyle::default() {
+                        let mut new_highlight = true;
+                        if let Some((last_range, MarkdownHighlight::Style(last_style))) =
+                            highlights.last_mut()
+                        {
+                            if last_range.end == prev_len && last_style == &style {
+                                last_range.end = text.len();
+                                new_highlight = false;
+                            }
+                        }
+                        if new_highlight {
+                            let range = prev_len..text.len();
+                            highlights.push((range, MarkdownHighlight::Style(style)));
+                        }
+                    }
+                }
+            }
+
+            Event::Code(t) => {
+                text.push_str(t.as_ref());
+                region_ranges.push(prev_len..text.len());
+
+                let link = link_url.clone().and_then(|u| Link::identify(u));
+                if link.is_some() {
+                    highlights.push((
+                        prev_len..text.len(),
+                        MarkdownHighlight::Style(MarkdownHighlightStyle {
+                            underline: true,
+                            ..Default::default()
+                        }),
+                    ));
+                }
+                regions.push(ParsedRegion { code: true, link });
+            }
+
+            Event::Start(tag) => match tag {
+                Tag::Paragraph => new_paragraph(text, &mut list_stack),
+
+                Tag::Heading(_, _, _) => {
+                    new_paragraph(text, &mut list_stack);
+                    bold_depth += 1;
+                }
+
+                Tag::CodeBlock(kind) => {
+                    new_paragraph(text, &mut list_stack);
+                    current_language = if let CodeBlockKind::Fenced(language) = kind {
+                        language_registry
+                            .language_for_name(language.as_ref())
+                            .await
+                            .ok()
+                    } else {
+                        language.clone()
+                    }
+                }
+
+                Tag::Emphasis => italic_depth += 1,
+
+                Tag::Strong => bold_depth += 1,
+
+                Tag::Link(_, url, _) => link_url = Some(url.to_string()),
+
+                Tag::List(number) => {
+                    list_stack.push((number, false));
+                }
+
+                Tag::Item => {
+                    let len = list_stack.len();
+                    if let Some((list_number, has_content)) = list_stack.last_mut() {
+                        *has_content = false;
+                        if !text.is_empty() && !text.ends_with('\n') {
+                            text.push('\n');
+                        }
+                        for _ in 0..len - 1 {
+                            text.push_str("  ");
+                        }
+                        if let Some(number) = list_number {
+                            text.push_str(&format!("{}. ", number));
+                            *number += 1;
+                            *has_content = false;
+                        } else {
+                            text.push_str("- ");
+                        }
+                    }
+                }
+
+                _ => {}
+            },
+
+            Event::End(tag) => match tag {
+                Tag::Heading(_, _, _) => bold_depth -= 1,
+                Tag::CodeBlock(_) => current_language = None,
+                Tag::Emphasis => italic_depth -= 1,
+                Tag::Strong => bold_depth -= 1,
+                Tag::Link(_, _, _) => link_url = None,
+                Tag::List(_) => drop(list_stack.pop()),
+                _ => {}
+            },
+
+            Event::HardBreak => text.push('\n'),
+
+            Event::SoftBreak => text.push(' '),
+
+            _ => {}
+        }
+    }
+}
+
+pub fn highlight_code(
+    text: &mut String,
+    highlights: &mut Vec<(Range<usize>, MarkdownHighlight)>,
+    content: &str,
+    language: &Arc<Language>,
+) {
+    let prev_len = text.len();
+    text.push_str(content);
+    for (range, highlight_id) in language.highlight_text(&content.into(), 0..content.len()) {
+        let highlight = MarkdownHighlight::Code(highlight_id);
+        highlights.push((prev_len + range.start..prev_len + range.end, highlight));
+    }
+}
+
+pub fn new_paragraph(text: &mut String, list_stack: &mut Vec<(Option<u64>, bool)>) {
+    let mut is_subsequent_paragraph_of_list = false;
+    if let Some((_, has_content)) = list_stack.last_mut() {
+        if *has_content {
+            is_subsequent_paragraph_of_list = true;
+        } else {
+            *has_content = true;
+            return;
+        }
+    }
+
+    if !text.is_empty() {
+        if !text.ends_with('\n') {
+            text.push('\n');
+        }
+        text.push('\n');
+    }
+    for _ in 0..list_stack.len().saturating_sub(1) {
+        text.push_str("  ");
+    }
+    if is_subsequent_paragraph_of_list {
+        text.push_str("  ");
+    }
+}